我正在try 创建一个代码,它将一个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;
}

然而,这一点有一个缺点,即divide_with_rounding(UINT64_MAX, 1000)等于0.

推荐答案

从数学上讲,这个表达式是round(x/d) = ⌊(x + d/2)/d⌋.从property of floor function⌊x+n⌋=⌊x⌋+n我们可以证明,在d为偶数的情况下,结果为

\left\lfloor \frac{n + \left\lfloor \frac{d}{2}\right\rfloor }{d} \right\rfloor = \left\lfloor \frac{n - \frac{d}{2} + d}{d} \right\rfloor = \left\lfloor \frac{n - \frac{d}{2}}{d} + 1 \right\rfloor = \left\lfloor \frac{n - \frac{d}{2}}{d} \right\rfloor + 1

如果d是奇数,我们可以替换d=2k+1,并证明结果是相同的.因此,您只需使用

if (n >= d/2)
    return (n - d/2)/d + 1;
else
    return (n + d/2)/d;

这将避免n + d/2溢出的情况

但是,在d不是编译时间常量的情况下,如果分支错误预测成本很高,则执行128×64位除法可能会更快.在MSVC中,您可以这样做

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;

C++相关问答推荐

为指针 struct 创建宏

特定闪存扇区的内存别名

在C中将通用字符名称转换为UTF-8

如何知道我是否从非阻塞套接字读取所有内容

使用AVX2的英特尔2022编译器的NaN问题&;/fp:FAST

轮询libusb_pollfd struct 列表的正确方式是什么?

错误Cygwin_Except::Open_stackdupfile:正在转储堆栈跟踪是什么?

为什么我可以在GCC的标签后声明变量,但不能声明Clang?

有什么方法可以将字符串与我们 Select 的子字符串分开吗?喜欢:SIN(LOG(10))

Boyer Moore算法的简单版本中的未定义行为

如何读取文件并将内容保存在字符串中?(在C语言中,没有崩溃或核心转储错误)

在vfork()之后,链接器如何在不 destruct 父内存的情况下解析execve()?

C语言中的外部关键字

在函数外部使用内联ASM时无法指定操作数

C中的回文数字

try 判断长整数是否为素数

C 错误:对 int 数组使用 typedef 时出现不兼容的指针类型问题

中位数和众数不正确

C 中 struct 体自赋值是否安全?特别是如果一侧是指向 struct 的指针?

Codewars Kata 掷骰子的不稳定行为