在 C/C++ 中, 直接利用 (x + y) >> 1
来计算
(两个整数的平均值并向下取整)以及直接利用
(x + y + 1) >> 1
来计算
(两个整数的平均值并向上取整)的结果可能有误, 因为
(x + y) >> 1
和
(x + y + 1) >> 1
中的
x + y
可能会发生数值溢出. 而
和
的结果是不可能数值溢出的, 这就引发我们思考可不可能通过某种方式来规避平均值计算中的数值溢出.
注: 本文假设符号数的右移运算符进行的是算术右移, 符号数的编码方式采用的是 two's complement 编码.
方式一
利用如下公式
下面是对上述两式的证明:
其中
均为整数.
借用上面的公式可以将
转化为如下的 C/C++ 代码 (据说这段代码还被申请了专利):
(x >> 1) + (y >> 1) + (x & y & 1);
可以将
转化为如下的 C/C++ 代码:
(x >> 1) + (y >> 1) + ((x | y) & 1);
这两段代码都不会发生数值溢出.
方式二
设 x 和 y 只能取 0 和 1 值, 则:
注意上表中的 10 是二进制下的 10, 即十进制下的 2, & 是逻辑与操作, | 是逻辑或运算, ^ 是逻辑异或操作.
由上表可见 x + y = 2*(x & y) + (x ^ y) = 2*(x | y) - (x ^ y)
.
无符号整型
对于无符号整型, 设
和
, 其中
.
上式用 C/C++语言可以表示为:
(x & y) + ((x ^ y) >> 1);
上式用 C/C++ 语言可以表示为:
(x | y) - ((x ^ y) >> 1);
有符号整型
对于有符号整型, 设
和
(这里采用的是符号数的 two's-complement 编码表示), 其中
.
上式用 C/C++ 语言可以表示为:
(x & y) + ((x ^ y) >> 1);
上式用 C/C++ 语言可以表示为:
(x | y) - ((x ^ y) >> 1);
综合
综合上面的分析, 可见对于有符号整型和无符号整型,
都可以用 C/C++ 语言表示为:
(x & y) + ((x ^ y) >> 1);
都可以用 C/C++ 语言表示为:
(x | y) - ((x ^ y) >> 1);
参考
https://stackoverflow.com/questions/24920503/what-is-the-right-way-to-find-the-average-of-two-valuesstackoverflow.com https://stackoverflow.com/questions/3816446/how-can-i-safely-average-two-unsigned-ints-in-cstackoverflow.com版权声明
版权声明:自由分享,保持署名-非商业用途-非衍生,知识共享3.0协议。
如果你对本文有疑问或建议,欢迎留言!转载请保留版权声明!