我最近了解到整数溢出是C中未定义的行为(侧面问题 - 它是否也是C++中的UB?)
经常在C语言编程,你需要找到两个值的平均值a
和b
.但是,这样做(a+b)/2
会导致溢出和未定义的行为.
所以我的问题是-什么是找到两个值的平均值的正确方法a
,并b
用C?
(a >> 1) + (b >> 1) + (((a & 1) + (b & 1)) >> 1)
c int数学中的shift语句(x >> i)等于2除以i的幂.所以声明(a >> 1)+(b >> 1)与a/2 + b/2相同.但是,也需要添加数字截断部分的平均值.该值可以通过掩蔽(a&1),添加((a&1)+(b&1))和除(((a&1)+(b&1))>> 1)来获得.平均值变为(a >> 1)+(b >> 1)+(((a&1)+(b&1))>> 1)
注意:使用>>和&而不是/和%作为除法和余数运算符的原因之一是效率.
在Secure Coding的帮助下
if (((si_b > 0) && (si_a > (INT_MAX - si_b))) || ((si_b < 0) && (si_a < (INT_MIN - si_b)))) { /* will overflow, so use difference method */ return si_b + (si_a - si_b) / 2; } else { /* the addition will not overflow */ return (si_a + si_b) / 2; }
附录
感谢@chux指出舍入问题.这是一个经过正确舍入测试的版本......
int avgnoov (int si_a, int si_b) { if ((si_b > 0) && (si_a > (INT_MAX - si_b))) { /* will overflow, so use difference method */ /* both si_a and si_b > 0; we want difference also > 0 so rounding works correctly */ if (si_a >= si_b) return si_b + (si_a - si_b) / 2; else return si_a + (si_b - si_a) / 2; } else if ((si_b < 0) && (si_a < (INT_MIN - si_b))) { /* will overflow, so use difference method */ /* both si_a and si_b < 0; we want difference also < 0 so rounding works correctly */ if (si_a <= si_b) return si_b + (si_a - si_b) / 2; else return si_a + (si_b - si_a) / 2; } else { /* the addition will not overflow */ return (si_a + si_b) / 2; } }