无符号整数的四舍五入,无溢出

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 / d01这一事实:

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 &lt;= n%d);

【讨论】:

我可以建议return n/d + n%d / (d-d/2),没有分支(布尔决策)吗? @goodvibration 但是您的建议有一个额外的除法运算来代替比较;我认为后者更快。 @AdrianMole:这取决于平台。我会把它留在这里,让任何未来的读者来决定(假设它是正确的)。 n%d 可能是除法的“免费”副作用。d/2 将通过班次完成,这很便宜。 也许一个体面的编译器不需要分支。在&lt;=比较之后,只需添加符号标志的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 &lt; 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 &lt;= remainder时需要递增。

总结:

您需要不同的条件,具体取决于 oddeven 除数。

【讨论】:

以上是关于无符号整数的四舍五入,无溢出的主要内容,如果未能解决你的问题,请参考以下文章

c语言如何让输出结果精确到两位小数

如何在 Rust 中将有符号整数添加到无符号整数,检查无符号溢出?

浮点数怎么表示精确度?

带符号的 16 位 SSE 平均值

C++无符号整数溢出探究

C++无符号整数溢出探究