提问人:Kevin Meier 提问时间:7/19/2023 最后编辑:phuclvKevin Meier 更新时间:7/20/2023 访问量:53
uint64_t:四舍五入的除法
uint64_t: Division with rounding
问:
我正在尝试创建一个代码,该代码将 a 除以另一个,并且它对结果应用舍入。代码应该尽可能快,并且适用于所有输入(例如,我希望它现在有条件)。uint64_t
uint64_t
我目前的解决方案如下所示:
static inline uint64_t divide_with_rounding(uint64_t n, uint64_t d)
{
uint64_t a = n / d;
uint64_t r = n % d;
return a + (r >= d - (d / 2));
}
GCC 很好地优化了除法+模,并且 .但我想知道是否有更短更好的解决方案。/ 2
例如,像这样的东西:
static inline uint64_t divide_with_rounding(uint64_t n, uint64_t d)
{
return (n + d / 2) / d;
}
但是,该缺点产生 0.divide_with_rounding(UINT64_MAX, 1000)
答:
2赞
phuclv
7/20/2023
#1
表达式在数学上是 round(x/d) = ⌊(x + d/2)/d⌋。根据楼层函数的性质 ⌊x + n⌋ = ⌊x⌋ + n,我们可以证明,在 d 的情况下,结果是
如果 d 是奇数,我们可以替换 d = 2k + 1 并证明结果是相同的。因此,您可以只使用
if (n >= d/2)
return (n - d/2)/d + 1;
else
return (n + d/2)/d;
这样可以避免溢出的情况n + d/2
但是,如果不是编译时常量,那么如果分支错误预测成本很高,则执行 128 x 64 位除法可能会更快。在 MSVC 中,您可以这样做d
uint64_t nH = 0, nL = n, rem = 0;
nL += d/2;
nH += nL < n; // { nH, nL } += d/2
return _udiv128(nH, nL, d, &rem); // { nH, nL } / d
在具有 GCC、ICC、Clang 等类型的编译器中......直接使用即可__int128
__int128 N = n;
N += d/2;
return N/d;
评论
(n + d/2)/d
(n + d - 1)/d