125

在你做的时候在java中

a % b

如果 a 是负数,它将返回一个负数结果,而不是像它应该的那样环绕到 b。解决此问题的最佳方法是什么?我能想到的唯一方法是

a < 0 ? b + a : a % b
4

6 回答 6

162

它的行为应该是 a % b = a - a / b * b; 即这是剩余的。

你可以做 (a % b + b) % b


这个表达式的工作原理(a % b)是必然低于b,无论a是正数还是负数。加法b处理 的负值a,因为和(a % b)之间的负值,必然低于和 正值。最后一个模数以防万一开始是正数,因为如果是正数将变得大于。因此,将其变为比再次更小(并且不影响负值)。-b0(a % b + b)baa(a % b + b)b(a % b + b) % bba

于 2010-12-10T18:46:29.060 回答
117

从 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
于 2014-09-14T04:45:40.640 回答
14

对于那些还没有使用(或不能使用)Java 8 的人,Guava 使用IntMath.mod()来拯救,自 Guava 11.0 起可用。

IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1

一个警告:与 Java 8 的 Math.floorMod() 不同,除数(第二个参数)不能为负数。

于 2016-03-25T17:10:09.467 回答
10

在数论中,结果总是正的。我猜这在计算机语言中并非总是如此,因为并非所有程序员都是数学家。我的两分钱,我认为这是语言的设计缺陷,但你现在不能改变它。

=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 分钟,即使两个答案都满足基本方程。

于 2016-06-09T22:14:54.540 回答
5

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

如果您只需要0n-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

于 2018-10-01T19:24:07.350 回答
2

这是一个替代方案:

a < 0 ? b-1 - (-a-1) % b : a % b

这可能会或可能不会比其他公式 [(a % b + b) % b] 更快。与其他公式不同,它包含一个分支,但使用了一个较少的模运算。如果计算机可以正确预测 < 0,则可能是胜利。

(编辑:修正了公式。)

于 2017-01-31T05:58:28.023 回答