解决求平均值出现加和导致的溢出问题

解决求平均值出现加和导致的溢出问题


试问求平均值能玩出什么样的花样来?

微软大神@Raymond发表了一篇长文,讲述的是关于求平均值的算法。

附上长文链接:https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223#comments


求平均值这很简单,小学时候的知识,无非就是N个数的加和在除以N,再简单不过了。

对于无符号的两个数求平均,我们通常这么写:

(注:这里uint32_t以32位表示)

uint32_t average(uint32_t a, uint32_t b)
{
	return (a + b) / 2;
}

但是这样就存在一个问题,当a和b两个值都比较大的时候,a加b的结果会超出uint32_t的最大表示范围,例如:

!!!!!!!!!average(0X80000000, 0X80000000) = 0!!!!!!!!!!!!!

所以为了避免这种情况,进行优化成这样:

方法一

uint32_t average(uint32_t low, uint32_t high)
{
    return low + (high - low) / 2;
}

此方法需要知道大的那个值,当知道相加的两个无符号整数中的较大值时,减去较小值再除二,就可以以提前减少长度,从而避免了数值溢出。

方法二

还有另一种算法不依赖于知道哪个值更大,已经被申请为美国专利于2016年到期:

uint32_t average(uint32_t a, uint32_t b)
{
    return (a / 2) + (b / 2) + (a & b & 1);
}

这个方法的诀窍在于将两个数先进行除法缩小,然后同时对最低位进行位与,从而保证当两个数都为奇数时,(a & b & 1)的结果能得到前面除法的一个补正。

方法三

该方法被称为SWAR技术,它代表"寄存器中的SIMD"。

uint32_t average(uint32_t a, uint32_t b)
{
    return (a & b) + (a ^ b) / 2;
}

方法四

作者还提出了第二种思路,如果无符号32位整形的本身寄存器大小为64位的时候,或者编译器支持的多字节运算,就可以使用强制转换的方式:

uint32_t average(uint32_t a, uint32_t b)
{
    // Suppose "unsigned" is a 32-bit type and
    // "unsigned long long" is a 64-bit type.
    return ((unsigned long long)a + b) / 2;
}

不过要注意一点,使用这种方式要保证64位寄存器的前32位为0才可以,要不然计算出来的还是不对的。

方法五

该方法是该长文评论区的一位网友提出的:

uint32_t avg(uint32_t a, uint32_t b)
{
	return (a & b) + ((a ^ b) / 2);
}

这篇博客长文真就是微软及各路大神的讨论区,学无止境!


ends…

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

觉皇嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值