使 Java 的模数表现得像负数一样的最佳方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4412179/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to make Java's modulus behave like it should with negative numbers?
提问by fent
In java when you do
在java中,当你这样做时
a % b
If a is negative, it will return a negative result, instead of wrapping around to b like it should. What's the best way to fix this? Only way I can think is
如果 a 是负数,它将返回一个负结果,而不是像它应该的那样环绕到 b 。解决这个问题的最佳方法是什么?我能想到的唯一方法是
a < 0 ? b + a : a % b
回答by Peter Lawrey
It behaves as it should a % b = a - a / b * b; i.e. it's the remainder.
它的行为应该是 a % b = a - a / b * b; 即它的余数。
You can do (a % b + b) % b
你可以做 (a % b + b) % b
This expression works as the result of (a % b)
is necessarily lower than b
, no matter if a
is positive or negative. Adding b
takes care of the negative values of a
, since (a % b)
is a negative value between -b
and 0
, (a % b + b)
is necessarily lower than b
and positive. The last modulo is there in case a
was positive to begin with, since if a
is positive (a % b + b)
would become larger than b
. Therefore, (a % b + b) % b
turns it into smaller than b
again (and doesn't affect negative a
values).
这个表达式的工作原理是 的结果(a % b)
必然低于b
,无论a
是正数还是负数。添加b
处理 的负值a
,因为和(a % b)
之间的负值,必然低于正值。最后一个模在那里,以防万一开始是正的,因为如果是正将变得大于。因此,将其变成更小(并且不影响负值)。-b
0
(a % b + b)
b
a
a
(a % b + b)
b
(a % b + b) % b
b
a
回答by John Krueger
As of Java 8, you can use Math.floorMod(int x, int y)and Math.floorMod(long x, long y). Both of these methods return the same results as Peter's answer.
从 Java 8 开始,您可以使用Math.floorMod(int x, int y)和Math.floorMod(long x, long y)。这两种方法都返回与彼得回答相同的结果。
Math.floorMod( 2, 3) = 2
Math.floorMod(-2, 3) = 1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
回答by Ibrahim Arief
For those not using (or not able to use) Java 8 yet, Guava came to the rescue with IntMath.mod(), available since Guava 11.0.
对于那些还没有使用(或无法使用)Java 8 的人,Guava 使用IntMath.mod()来救援,自 Guava 11.0 开始可用。
IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1
One caveat: unlike Java 8's Math.floorMod(), the divisor (the second parameter) cannot be negative.
一个警告:与 Java 8 的 Math.floorMod() 不同,除数(第二个参数)不能为负数。
回答by Chris Golledge
In number theory, the result is always positive. I would guess that this is not always the case in computer languages because not all programmers are mathematicians. My two cents, I would consider it a design defect of the language, but you can't change it now.
在数论中,结果总是正的。我猜想在计算机语言中情况并非总是如此,因为并非所有程序员都是数学家。我的两分钱,我认为这是语言的设计缺陷,但你现在不能改变它。
=MOD(-4,180) = 176 =MOD(176, 180) = 176
=MOD(-4,180) = 176 =MOD(176, 180) = 176
because 180 * (-1) + 176 = -4 the same as 180 * 0 + 176 = 176
因为 180 * (-1) + 176 = -4 与 180 * 0 + 176 = 176 相同
Using the clock example here, http://mathworld.wolfram.com/Congruence.htmlyou would not say duration_of_time mod cycle_length is -45 minutes, you would say 15 minutes, even though both answers satisfy the base equation.
使用这里的时钟示例,http://mathworld.wolfram.com/Congruence.html 你不会说duration_of_time mod cycle_length 是-45 分钟,你会说15 分钟,即使两个答案都满足基本方程。
回答by Stefan Reich
Here is an alternative:
这是一个替代方案:
a < 0 ? b-1 - (-a-1) % b : a % b
This might or might not be faster than that other formula [(a % b + b) % b], come to think of it. It contains a branch which is usually bad with modern processors, but uses one less modulo operation.
这可能会也可能不会比其他公式 [(a % b + b) % b] 快,想想看。它包含一个对于现代处理器来说通常很糟糕的分支,但使用较少的模运算。
Actually it might definitely be slower.
实际上它肯定会更慢。
(Edit: Fixed the formula.)
(编辑:修正了公式。)
回答by Scott Carey
Java 8 has Math.floorMod
, but it is very slow (its implementation has multiple divisions, multiplications, and a conditional). Its possible that the JVM has an intrinsic optimized stub for it, however, which would speed it up significantly.
Java 8 有Math.floorMod
,但速度很慢(它的实现有多个除法、乘法和条件)。然而,JVM 可能有一个内在的优化存根,这会显着加快它的速度。
The fastest way to do this without floorMod
is like some other answers here, but with no conditional branches and only one slow %
op.
这样做的最快方法floorMod
就像这里的其他一些答案,但没有条件分支,只有一个慢速%
操作。
Assuming n is positive, and x may be anything:
假设 n 是正数,而 x 可以是任何东西:
int remainder = (x % n); // may be negative if x is negative
//if remainder is negative, adds n, otherwise adds 0
return ((remainder >> 31) & n) + remainder;
The results when n = 3
:
结果时n = 3
:
x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
0| 0
1| 1
2| 2
3| 0
4| 1
If you only need a uniform distribution between 0
and n-1
and not the exact mod operator, and your x
's do not cluster near 0
, the following will be even faster, as there is more instruction level parallelism and the slow %
computation will occur in parallel with the other parts as they do not depend on its result.
如果你只需要之间的均匀分布0
和n-1
不准确的Mod运算符,和你x
的不群附近0
,以下将更快,因为有更多的指令级并行性和缓慢的%
计算会发生在平行于其他部分,因为它们不依赖于它的结果。
return ((x >> 31) & (n - 1)) + (x % n)
return ((x >> 31) & (n - 1)) + (x % n)
The results for the above with n = 3
:
上面的结果与n = 3
:
x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
0| 0
1| 1
2| 2
3| 0
4| 1
5| 2
If the input is random in the full range of an int, the distribution of both two solutions will be the same. If the input clusters near zero, there will be too few results at n - 1
in the latter solution.
如果输入在 int 的整个范围内是随机的,则两个解的分布将相同。如果输入聚类接近于零,则n - 1
后一种解决方案中的结果将太少。