使 Java 的模数表现得像负数那样的最佳方法?

发布于 2024-10-06 22:20:28 字数 168 浏览 11 评论 0原文

在java中,当你这样做时

a % b

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

a < 0 ? b + a : a % b

In java when you do

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 < 0 ? b + a : a % b

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

行至春深 2024-10-13 22:20:28

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

您可以执行(a % b + b) % b


该表达式的工作原理是,无论 a 是正数还是负数,(a % b) 的结果都必然低于 b。添加 b 可以处理 a 的负值,因为 (a % b)-b 之间的负值code> 和 0(a % b + b) 必然低于 b 并且为正值。最后一个模是在 a 开始时为正数的情况下出现的,因为如果 a 为正数 (a % b + b) 会变得更大比b。因此,(a % b + b) % b 再次将其变为小于 b(并且不会影响负的 a 值)。

It behaves as it should: a % b = a - a / b * b; i.e. it's the remainder.

You can do (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).

坏尐絯℡ 2024-10-13 22:20:28

从 Java 8 开始,您可以使用 Math.floorMod (int x, int y)Math.floorMod(长 x, 长 y)。这两种方法都返回与 Peter 的答案相同的结果。

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2

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.

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
当爱已成负担 2024-10-13 22:20:28

对于那些尚未使用(或无法使用)Java 8 的人,Guava 可以通过 IntMath.mod(),自 Guava 11.0 起可用。

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

需要注意的是:与 Java 8 的 Math.floorMod() 不同,除数(第二个参数)不能为负数。

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.

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.

习惯成性 2024-10-13 22:20:28

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

=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 period_length是-45分钟,你会说15分钟,即使两个答案都满足基本方程。

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

because 180 * (-1) + 176 = -4 the same as 180 * 0 + 176 = 176

Using the clock example here, http://mathworld.wolfram.com/Congruence.html
you 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.

江湖彼岸 2024-10-13 22:20:28

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 之间均匀分布code> 而不是确切的 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 处的结果将会太少。

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.

The fastest way to do this without floorMod is like some other answers here, but with no conditional branches and only one slow % op.

Assuming n is positive, and x may be anything:

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:

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.

return ((x >> 31) & (n - 1)) + (x % n)

The results for the above with 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.

旧情勿念 2024-10-13 22:20:28

这是一个替代方案:

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

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

(编辑:修正了公式。)

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]. Unlike the other formula, it contains a branch, but uses one less modulo operation. Probably a win if the computer can predict a < 0 correctly.

(Edit: Fixed the formula.)

蒲公英的约定 2024-10-13 22:20:28

FloorMod 方法是最好的方法。

我很惊讶没有人发布明显的信息。

Math.abs(a) % b

The floorMod method is the best way to go.

I am surprised no one posted the obvious.

Math.abs(a) % b
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文