• C/C++之计算两个整型的平均值


    在 C/C++ 中, 直接利用 (x + y) >> 1 来计算 (leftlfloor {left( {x + y} ight)/2} ight floor) (两个整数的平均值并向下取整)以及直接利用 (x + y + 1) >> 1 来计算 (leftlceil {left( {x + y} ight)/2} ight ceil) (两个整数的平均值并向上取整)的结果可能有误, 因为 (x + y) >> 1(x + y + 1) >> 1 中的 x + y 可能会发生数值溢出. 而 (leftlfloor {left( {x + y} ight)/2} ight floor)(leftlceil {left( {x + y} ight)/2} ight ceil) 的结果是不可能数值溢出的, 这就引发我们思考可不可能通过某种方式来规避平均值计算中的数值溢出.

    注: 本文假设符号数的右移运算符进行的是算术右移, 符号数的编码方式采用的是 two's complement 编码.

    方式一

    利用如下公式

    (egin{align} leftlfloor {left( {x + y} ight)/2} ight floor = leftlfloor {x/2} ight floor + leftlfloor {y/2} ight floor + leftlfloor {left( {xmod 2 + ymod 2} ight)/2} ight floor hfill \ leftlceil {left( {x + y} ight)/2} ight ceil = leftlfloor {x/2} ight floor + leftlfloor {y/2} ight floor + leftlceil {left( {xmod 2 + ymod 2} ight)/2} ight ceil hfill \ end{align})

    下面是对上述两式的证明:
    (egin{align} leftlfloor {left( {x + y} ight)/2} ight floor &= left{ {egin{array}{*{20}{c}} {m + n}&{x = 2m,y = 2n} \ {m + n}&{x = 2m + 1,y = 2n} \ {m + n}&{x = 2m,y = 2n + 1} \ {m + n + 1}&{x = 2m + 1,y = 2n + 1} end{array}} ight. \ &= leftlfloor {x/2} ight floor + leftlfloor {y/2} ight floor + leftlfloor {left( {xmod 2 + ymod 2} ight)/2} ight floor \ end{align})

    (egin{align} leftlceil {left( {x + y} ight)/2} ight ceil &= left{ {egin{array}{*{20}{c}} {m + n}&{x = 2m,y = 2n} \ {m + n + 1}&{x = 2m + 1,y = 2n} \ {m + n + 1}&{x = 2m,y = 2n + 1} \ {m + n + 1}&{x = 2m + 1,y = 2n + 1} end{array}} ight. \ &= leftlfloor {x/2} ight floor + leftlfloor {y/2} ight floor + leftlceil {left( {xmod 2 + ymod 2} ight)/2} ight ceil \ end{align})

    其中 (m,n) 均为整数.

    借用上面的公式可以将 (leftlfloor {left( {x + y} ight)/2} ight floor) 转化为如下的 C/C++ 代码 (据说这段代码还被申请了专利):

    (x >> 1) + (y >> 1) + (x & y & 1);
    

    可以将 (leftlceil {left( {x + y} ight)/2} ight ceil) 转化为如下的 C/C++ 代码:

    (x >> 1) + (y >> 1) + ((x | y) & 1);
    

    这两段代码都不会发生数值溢出.

    方式二

    设 x 和 y 只能取 0 和 1 值, 则:

    x y x + y x ^ y x & y x | y 2*(x & y) + (x ^ y) 2*(x | y) - (x ^ y)
    0 0 0 0 0 0 0 + 0 = 0 0 - 0 = 0
    0 1 1 1 0 1 0 + 1 = 1 10 - 1 = 1
    1 0 1 1 0 1 0 + 1 = 1 10 - 1 = 1
    1 1 10 0 1 1 10 + 0 = 10 10 - 0 = 10

    注意上表中的 10 是二进制下的 10, 即十进制下的 2, & 是逻辑与操作, | 是逻辑或运算, ^ 是逻辑异或操作.

    由上表可见 x + y = 2*(x & y) + (x ^ y) = 2*(x | y) - (x ^ y).

    无符号整型

    对于无符号整型, 设 (x = sum olimits_{i = 0}^{n - 1} {{u_i}{2^i}})(y = sum olimits_{i = 0}^{n - 1} {{v_i}{2^i}}), 其中 (u_i,v_iinleft{ 0, 1 ight}).

    (egin{align} x + y &= sum olimits_{i = 0}^{n - 1} {{u_i}{2^i}} { ext{ + }}sum olimits_{i = 0}^{n - 1} {{v_i}{2^i}} \ &= sum olimits_{i = 0}^{n - 1} {left( {{u_i} + {v_i}} ight){2^i}} \ &= sum olimits_{i = 0}^{n - 1} {left( {2 imes left( {{u_i}& {v_i}} ight) + left( {{u_i} wedge {v_i}} ight)} ight){2^i}} \ &= 2sum olimits_{i = 0}^{n - 1} {left( {{u_i}& {v_i}} ight){2^i}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^i}} \ end{align})

    (egin{align} leftlfloor {left( {x + y} ight)/2} ight floor &= leftlfloor {sum olimits_{i = 0}^{n - 1} {left( {{u_i}& {v_i}} ight){2^i}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight floor \ &= sum olimits_{i = 0}^{n - 1} {left( {{u_i}& {v_i}} ight){2^i}} + sum olimits_{i = 1}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} \ end{align})

    上式用 C/C++ 语言可以表示为:

    (x & y) + ((x ^ y) >> 1);
    

    (egin{align} x + y &= sum olimits_{i = 0}^{n - 1} {{u_i}{2^i}} { ext{ + }}sum olimits_{i = 0}^{n - 1} {{v_i}{2^i}} \ &= sum olimits_{i = 0}^{n - 1} {left( {{u_i} + {v_i}} ight){2^i}} \ &= sum olimits_{i = 0}^{n - 1} {left( {2 imes left( {{u_i}|{v_i}} ight) - left( {{u_i} wedge {v_i}} ight)} ight){2^i}} \ &= 2sum olimits_{i = 0}^{n - 1} {left( {{u_i}|{v_i}} ight){2^i}} - sum olimits_{i = 0}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^i}} \ end{align})

    (egin{align} leftlceil {left( {x + y} ight)/2} ight ceil &= leftlceil {sum olimits_{i = 0}^{n - 1} {left( {{u_i}|{v_i}} ight){2^i}} - sum olimits_{i = 0}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight ceil \ &= sum olimits_{i = 0}^{n - 1} {left( {{u_i}|{v_i}} ight){2^i}} - sum olimits_{i = 1}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} \ end{align})

    上式用 C/C++ 语言可以表示为:

    (x | y) - ((x ^ y) >> 1);
    

    有符号整型

    对于有符号整型, 设 (x = - {u_{n - 1}}{2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {{u_i}{2^i}})(y = - {v_{n - 1}}{2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {{v_i}{2^i}}), 其中 (u_i,v_iinleft{ 0, 1 ight}).

    (egin{align} x + y &= - {u_{n - 1}}{2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {{u_i}{2^i}} - {v_{n - 1}}{2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {{v_i}{2^i}} \ &= - left( {{u_{n - 1}} + {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {left( {{u_i} + {v_i}} ight){2^i}} \ &= - left( {2 imes left( {{u_{n - 1}}& {v_{n - 1}}} ight) + left( {{u_{n - 1}} wedge {v_{n - 1}}} ight)} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {left( {2 imes left( {{u_i}& {v_i}} ight) + left( {{u_i} wedge {v_i}} ight)} ight){2^i}} \ &= 2left( { - left( {{u_{n - 1}}& {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}& {v_i}} ight){2^i}} } ight) + left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {left( {{u_i} wedge {v_i}} ight){2^i}} } ight) \ end{align})

    (egin{align} leftlfloor {left( {x + y} ight)/2} ight floor &= leftlfloor {left( { - left( {{u_{n - 1}}& {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}& {v_i}} ight){2^i}} } ight) + left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 2}} + sum olimits_{i = 0}^{n - 2} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight)} ight floor \ &= left( { - left( {{u_{n - 1}}& {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}& {v_i}} ight){2^i}} } ight) + left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 2}} + sum olimits_{i = 1}^{n - 2} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight) \ &= left( { - left( {{u_{n - 1}}& {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}& {v_i}} ight){2^i}} } ight) + left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 1}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight) \ end{align})

    上式用 C/C++ 语言可以表示为:

    (x & y) + ((x ^ y) >> 1);
    

    (egin{align} x + y &= - {u_{n - 1}}{2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {{u_i}{2^i}} - {v_{n - 1}}{2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {{v_i}{2^i}} \ &= - left( {{u_{n - 1}} + {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {left( {{u_i} + {v_i}} ight){2^i}} \ &= - left( {2 imes left( {{u_{n - 1}}|{v_{n - 1}}} ight) - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight)} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {left( {2 imes left( {{u_i}|{v_i}} ight) - left( {{u_i} wedge {v_i}} ight)} ight){2^i}} \ &= 2left( { - left( {{u_{n - 1}}|{v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}|{v_i}} ight){2^i}} } ight) - left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 2} {left( {{u_i} wedge {v_i}} ight){2^i}} } ight) \ end{align})

    (egin{align} leftlceil {left( {x + y} ight)/2} ight ceil &= leftlceil {left( { - left( {{u_{n - 1}}|{v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}|{v_i}} ight){2^i}} } ight) - left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 2}} + sum olimits_{i = 0}^{n - 2} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight)} ight ceil \ &= left( { - left( {{u_{n - 1}}|{v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}|{v_i}} ight){2^i}} } ight) - left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 2}} + sum olimits_{i = 1}^{n - 2} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight) \ &= left( { - left( {{u_{n - 1}}|{v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 0}^{n - 1} {left( {{u_i}|{v_i}} ight){2^i}} } ight) - left( { - left( {{u_{n - 1}} wedge {v_{n - 1}}} ight){2^{n - 1}} + sum olimits_{i = 1}^{n - 1} {left( {{u_i} wedge {v_i}} ight){2^{i - 1}}} } ight) \ end{align})

    上式用 C/C++ 语言可以表示为:

    (x | y) - ((x ^ y) >> 1);
    

    综合

    综合上面的分析, 可见对于有符号整型和无符号整型,

    (leftlfloor {left( {x + y} ight)/2} ight floor) 都可以用 C/C++ 语言表示为:

    (x & y) + ((x ^ y) >> 1);
    

    (leftlceil {left( {x + y} ight)/2} ight ceil) 都可以用 C/C++ 语言表示为:

    (x | y) - ((x ^ y) >> 1);
    

    参考:

    版权声明

    版权声明:自由分享,保持署名-非商业用途-非衍生,知识共享3.0协议。
    如果你对本文有疑问或建议,欢迎留言!转载请保留版权声明!
    如果你觉得本文不错, 也可以用微信赞赏一下哈.

  • 相关阅读:
    Fedora 18 Beta 天高地厚
    Action script 天高地厚
    DLL动态连接库 天高地厚
    VC下Debug和Release区别
    如何解决"应用程序无法启动,因为应用程序的并行配置不正确"问题
    GetWindowLong (hwnd, 0)
    windows文件关联、打开方式列表之修改注册表攻略
    小游戏开发流程
    常用CHM帮助文档集锦下载
    关于jsp页面传递中文参数乱码的解决
  • 原文地址:https://www.cnblogs.com/quarryman/p/two_integer_average.html
Copyright © 2020-2023  润新知