有效计算整数除法的下限
如何在 Go 中有效地计算两个整数的商,并向下舍入(不是朝零舍入)?下面的代码似乎给出了正确的结果,但看起来笨拙且低效:(
func floorDiv(a, b int) int {
if b < 0 {
a, b = -a, -b
}
r := a % b
if r < 0 {
r += b
}
return (a - r) / b
}
How can I efficiently compute the quotient of two integers, rounded down (not towards zero) in Go? The following code seems to give the correct result, but looks awkward and inefficient:
func floorDiv(a, b int) int {
if b < 0 {
a, b = -a, -b
}
r := a % b
if r < 0 {
r += b
}
return (a - r) / b
}
(also on the playground). Is there a better way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
将整数转换为 float64,除以
math.Floor
。基准测试显示它们的运行时间大约相同,并且与简单的添加函数相同。
这表明它太快了,我们只是对循环进行基准测试。它不太可能成为瓶颈,我建议最简单的选择。
Convert the integers to float64, divide, and use
math.Floor
.Benchmarking shows they run in about the same time, and the same as a simple add function.
This suggests it's so fast we're just benchmarking the loop. It is unlikely to be a bottleneck, I would suggest the simplest option.
位旋转是你的朋友 - 我将把它反对将两个整数转换为双精度并使用
math.Floor()
。位旋转使我们能够轻松识别二进制补码整数的符号:
二进制补码整数可能包含的最小[负]值已设置符号位,其余位全部清除(
0x80000000
)。将整数值与该最小值进行按位与,得到 0 或该整数类型可能包含的最小值:
math.MinInt
这让我们可以这样做:
计算
x / y
的商和余数。如果余数为零,则商为 ⌊ x / y ⌋。
否则(余数非零),
我们必须从商中减去 1 才能得到 ⌊ x / y ⌋。
商为 ⌊ x / y ⌋。
单击此处查看 Go Playground
x
的结果,使得 -10 ≤ x ≤ +10,且 y = 3:基准
基准5 次不同运行的计时表明,转换为浮点数并使用 math.Floor() 比整数除法和位旋转慢近 21 倍。
[这是否真正重要完全取决于用例。]
基准测试代码每次循环迭代调用被基准测试的函数 21 次(-10 到 +10 包括在内),因此循环代码的成本不会掩盖该函数正在被基准化。
运行这个基准测试:
Bit twiddling is your friend here — I'll put this up against converting two integers to doubles and using
math.Floor()
.Bit-twiddling allows us to easily identify the sign of a two's-complement integer:
The smallest [negative] value that a two's-complement integer may contain has the sign bit set and the remaining bits all clear (
0x80000000
).A bitwise AND of an integer value against that smallest value gives us either 0 or the smallest value that that integer type may contain:
5 & math.MinInt
yields 00 & math.MinInt
yields 0-5 & Math.MinInt
yieldsmath.MinInt
That lets us do this:
Compute the quotient and remainder for
x / y
.If the remainder is zero, the quotient is ⌊ x / y ⌋.
Otherwise (the remainder is non-zero),
we must subtract 1 from the quotient to yield ⌊ x / y ⌋.
the quotient is ⌊ x / y ⌋.
Click here for the Go Playground
Results for
x
such that -10 ≤x
≤ +10, andy
= 3:Benchmarks
Benchmark timings across 5 different runs show that converting to float and using
math.Floor()
to be nearly 21x slower than integer division and bit twiddling.[Whether or not that actually matters is entirely dependent on the use case.]
The benchmark code calls the function being benchmarked 21x per loop iteration (for -10 to +10 inclusive) so the cost of the loop code doesn't mask the function being benchmarked.
running this benchmark:
乘数(a * b)的情况下的基准会更快:-
我尝试了异或方法,但它更慢:
这是我的单元测试方法
基准方法:
以及所有输出:
编辑
The benchmark in case multiply numbers (a*b) will be faster:-
I tried the Xor method but it is slower:
this is my unit test method
the bench-mark method:
and the output for all:
Edit after I saw the result of Bit twiddling slower in my benchmark I try the bench method of the op and this is my result: