在你做的时候在java中
a % b
如果 a 是负数,它将返回一个负数结果,而不是像它应该的那样环绕到 b。解决此问题的最佳方法是什么?我能想到的唯一方法是
a < 0 ? b + a : a % b
在你做的时候在java中
a % b
如果 a 是负数,它将返回一个负数结果,而不是像它应该的那样环绕到 b。解决此问题的最佳方法是什么?我能想到的唯一方法是
a < 0 ? b + a : a % b
它的行为应该是 a % b = a - a / b * b; 即这是剩余的。
你可以做 (a % b + b) % b
这个表达式的工作原理(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
从 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
对于那些还没有使用(或不能使用)Java 8 的人,Guava 使用IntMath.mod()来拯救,自 Guava 11.0 起可用。
IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1
一个警告:与 Java 8 的 Math.floorMod() 不同,除数(第二个参数)不能为负数。
在数论中,结果总是正的。我猜这在计算机语言中并非总是如此,因为并非所有程序员都是数学家。我的两分钱,我认为这是语言的设计缺陷,但你现在不能改变它。
=MOD(-4,180) = 176 =MOD(176, 180) = 176
因为 180 * (-1) + 176 = -4 与 180 * 0 + 176 = 176 相同
使用此处的时钟示例http://mathworld.wolfram.com/Congruence.html 你不会说 duration_of_time mod cycle_length 是 -45 分钟,你会说 15 分钟,即使两个答案都满足基本方程。
Java 8 有Math.floorMod
,但速度很慢(它的实现有多个除法、乘法和条件)。然而,JVM 可能有一个针对它的内在优化存根,这将显着加快它的速度。
最快的方法floorMod
就像这里的一些其他答案一样,但没有条件分支,只有一个慢%
操作。
假设 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;
结果n = 3
:
x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
0| 0
1| 1
2| 2
3| 0
4| 1
如果您只需要0
和n-1
而不是确切的 mod 运算符之间的均匀分布,并且您x
的 ' 不聚集在 附近0
,那么下面会更快,因为有更多的指令级并行性,并且慢速%
计算将与另一个并行发生部分,因为它们不依赖于其结果。
return ((x >> 31) & (n - 1)) + (x % n)
上述结果与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
如果输入在 int 的整个范围内是随机的,则两个解的分布将相同。如果输入聚类接近零,则后一种解决方案中的结果将太少n - 1
。
这是一个替代方案:
a < 0 ? b-1 - (-a-1) % b : a % b
这可能会或可能不会比其他公式 [(a % b + b) % b] 更快。与其他公式不同,它包含一个分支,但使用了一个较少的模运算。如果计算机可以正确预测 < 0,则可能是胜利。
(编辑:修正了公式。)