找到不大于 A 的最大数的最有效方法,该数可被 B 整除 [关闭]

Posted

技术标签:

【中文标题】找到不大于 A 的最大数的最有效方法,该数可被 B 整除 [关闭]【英文标题】:Most efficient way to find the greatest number not greater than A, which is divisible by B [closed] 【发布时间】:2019-08-14 08:01:07 【问题描述】:

我有2个号码A和B。我想找到C = A - (A % B),但是有一些问题。首先,如果CD = A / B 应该具有相同的奇偶校验((偶数和偶数)或(奇数和奇数)),否则C 应该递增(++C)。第二个问题是我经常做这个计算,所以我希望它的成本尽可能小。现在我的解决方案是这样的:

uint32_t D = A / B;
C = D * B;
if ((C ^ D) & 0x1) ++C;

有没有更好的方法来做到这一点?由于编译器优化,(C % 2) != (D % 2) 可能更快,但我无法证明这一点。我也想知道是否可以使用某些特定的 intel 函数(寄存器)来完成。

【问题讨论】:

(C ^ D) & 0x1 将是01,您可以无条件地将其添加到C 但是在开始进行此类微优化之前,请先启用优化并进行 measureprofile 构建,如果需要,请检查生成的程序集。也许这不是您真正认为的那样的瓶颈? 地板(A / B)* B 【参考方案1】:

我假设输入 AB 也是 uint32_t?

除法的成本使其他一切都相形见绌,除非 B 在内联后的编译时已知。 (即使它不是 2 的幂)。与其他任何指令相比,实际的 div 指令非常昂贵,并且无法使用 SIMD 进行矢量化。 (x86 上唯一可用的 SIMD 除法是 FP,或者当然是整数移位除以 2)。

到目前为止,您可以做的最有用的事情是安排 B 的值在编译时对编译器可见,或者至少在链接时优化跨文件内联。 (Why does GCC use multiplication by a strange number in implementing integer division?)


如果B 不是编译时常数,x86 除法将免费产生余数以及商。 subimul 便宜,所以使用并让编译器优化:

uint32_t D = A / B;
uint32_t C = A - A % B;

如果B 是一个编译时常量,编译器会将其优化为除法,然后再乘法,并且(希望)将其优化到与原来的一样好。


不,(C^D) ^ 1 应该是一种更有效的方法来检查低位是否与(C % 2) != (D % 2) 不同。在合并之前对每个输入做一些单独的事情会花费更多的指令,因此最好引导编译器朝着更有效的 asm 实现方向发展。 (显然,看看这两种情况的 asm 输出是个好主意)。

可能有用的是使用+ 而不是^。 XOR = 没有进位的加法,但你只关心低位。 ^+ 的低位始终相同。这使编译器可以选择使用lea 指令进行复制和添加。 (在这种情况下可能没有帮助;如果编译器破坏了 持有D 的寄存器中的值,假设在此之后它已经死了。但是如果你也直接用D)


当然,您实际上并不想使用if(...) 进行分支,因此您应该将其写为:

C += (C+D) & 1;       // +1 if low bits differ

【讨论】:

以上是关于找到不大于 A 的最大数的最有效方法,该数可被 B 整除 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

HDU 6108(整除判断 数学)

获得数年数月数的最有效方法

在任意数量的数组之间找到共同项的最有效方法

小明A+B(杭电2096)

有效地找到最小值和最大值

52 回文平方数