使Java';s模的行为应该是负数吗?

使Java';s模的行为应该是负数吗?,java,modulo,negative-number,Java,Modulo,Negative Number,在java中,当您这样做时 a % b 如果a是负数,它将返回负数结果,而不是像它应该的那样环绕b。解决这个问题的最好方法是什么?我唯一能思考的是 a < 0 ? b + a : a % b a =MOD(-4180)=176 =MOD(176180)=176 因为180*(-1)+176=-4与180*0+176=176相同 使用这里的时钟示例, 你不会说时间模周期的持续时间是-45分钟,你会说15分钟,即使两个答案都满足基本方程。这里有一个替代方案: a < 0 ? b-1

在java中,当您这样做时

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

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

它的行为应该是a%b=a-a/b*b;i、 这是剩下的

你可以做(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开始,您可以使用和。这两种方法返回的结果与Peter的答案相同

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
对于那些还没有使用(或不能使用)Java8的人,Guava提供了自Guava11.0以来的帮助

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

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

在数论中,结果总是正的。我猜计算机语言中并不总是这样,因为并非所有的程序员都是数学家。我的两分钱,我会认为这是语言的设计缺陷,但你现在不能改变它。< /P> =MOD(-4180)=176 =MOD(176180)=176

因为180*(-1)+176=-4与180*0+176=176相同

使用这里的时钟示例,
你不会说时间模周期的持续时间是-45分钟,你会说15分钟,即使两个答案都满足基本方程。

这里有一个替代方案:

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


(编辑:修正了公式。)

Java8有
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
x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
 5| 2
如果您只需要
0
n-1
之间的均匀分布,而不需要精确的mod运算符,并且您的
x
不会聚集在
0
附近,则以下操作会更快,由于存在更多指令级并行性,且缓慢的
%
计算将与其他部分并行进行,因为它们不依赖于其结果

返回((x>>31)和(n-1))+(x%n)

上述
n=3
的结果:

x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
 5| 2

如果输入在整数的整个范围内是随机的,则两个解的分布将是相同的。如果输入集群接近零,则在后一种解决方案中,
n-1的结果将太少。

在处理负数时,没有“正确”的模行为-许多语言都是这样做的,许多语言做得不同,还有一些语言做得完全不同。至少前两个有其优点和缺点。这对我来说很奇怪。我认为只有当b为负数时,它才应该返回负数。它的可能副本为。但这个问题的标题应该改名。如果我在搜索这个问题,我不会点击这个问题,因为我已经知道java模数是如何工作的。我只是将它重命名为“Why is-13%64=51?”,它在一百万年内永远不会成为人们搜索的对象。所以这个问题的标题要好得多,而且在模数、负数、计算、数字等关键词上更容易搜索。谢谢。它也适用于比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
值)。@eitanfar我已经在答案中加入了您出色的解释(对
a<0
进行了轻微的更正,也许您可以看一看)我刚才看到这个评论是关于同一个话题的另一个问题;可能值得一提的是,
(a%b+b)%b
分解为非常大的
a
b
。例如,使用
a=Integer.MAX_VALUE-1
b=Integer.MAX_VALUE
将得到
-3
,这是一个负数,这就是你想要避免的。@Mikepote使用
while
如果你真的需要它会慢一些,除了你只需要
if
在这种情况下它实际上更快。Java 8+的最佳答案很酷,我不知道这个。Java8最终修复