无符号整数的四舍五入,无溢出
Posted
技术标签:
【中文标题】无符号整数的四舍五入,无溢出【英文标题】:Round division of unsigned integers with no overflow 【发布时间】:2020-06-21 16:30:00 【问题描述】:我正在寻找一种溢出安全的方法来执行无符号整数的四舍五入。
我有这个:
uint roundDiv(uint n, uint d)
return (n + d / 2) / d;
但不幸的是,n + d / 2
表达式可能会溢出。
我想我必须检查n % d
是否小于d / 2
。
但d / 2
本身可能会被截断(当d
为奇数时)。
所以我想我应该检查n % d * 2
是否小于d
。
或者即使没有逻辑条件,也可以依赖n % d * 2 / d
是0
或1
这一事实:
uint roundDiv(uint n, uint d)
return n / d + n % d * 2 / d;
这很好用,但是n % d * 2
可能再次溢出。
是否有任何自定义方法来实现溢出安全的整数除法?
更新
我想出了这个:
uint roundDiv(uint n, uint d)
if (n % d < (d + d % 2) / 2)
return n / d;
return n / d + 1;
不过,d + d % 2
表达式可能会溢出。
【问题讨论】:
评论不用于扩展讨论;这个对话是moved to chat。 【参考方案1】:return n/d + (d-d/2 <= n%d);
【讨论】:
我可以建议return n/d + n%d / (d-d/2)
,没有分支(布尔决策)吗?
@goodvibration 但是您的建议有一个额外的除法运算来代替比较;我认为后者更快。
@AdrianMole:这取决于平台。我会把它留在这里,让任何未来的读者来决定(假设它是正确的)。
n%d
可能是除法的“免费”副作用。d/2
将通过班次完成,这很便宜。
也许一个体面的编译器不需要分支。在<=
比较之后,只需添加符号标志的NOT?【参考方案2】:
在任何阶段避免溢出的方法是,正如 OP 所述,将余数与除数的一半进行比较,但结果并不像最初看起来那样明显。以下是一些示例,假设0.5
会四舍五入。首先是一个奇数除数:
Numerator Divisor Required Quotient Remainder Half divisor Quot < Req?
3 3 1 1 0 1 no
4 3 1 1 1 1 no
5 3 2 1 2 1 yes
6 3 2 2 0 1 no
上面,唯一需要的增量是d / 2 < remainder
。现在有一个偶数除数:
Numerator Divisor Required Quotient Remainder Half divisor Quot < Req?
4 4 1 1 0 2 no
5 4 1 1 1 2 no
6 4 2 1 2 2 yes
7 4 2 1 3 2 yes
8 4 2 2 0 2 no
但是这里,d / 2 <= remainder
时需要递增。
总结:
您需要不同的条件,具体取决于 odd 或 even 除数。【讨论】:
以上是关于无符号整数的四舍五入,无溢出的主要内容,如果未能解决你的问题,请参考以下文章