让Java的模数表现得像负数一样的最佳方法是什么?

r7s23pms  于 2023-03-16  发布在  Java
关注(0)|答案(7)|浏览(118)

在java中,当您这样做时

a % b

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

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

yduiuuwa1#

它的行为应该是:a % b = a - a / b * b,也就是余数
你可以做(a % b + b) % b
这个表达式的作用是,无论a是正的还是负的,(a % b)都必须小于b。加上b会处理a的负值,因为(a % b)-b0之间的负值。(a % b + b)必须小于b且为正。如果a开始为正,则存在最后一个模,因为如果a为正,则(a % b + b)将变得大于b。因此,(a % b + b) % b将其再次变为小于b(并且不影响负a值)。

polkgigr

polkgigr2#

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

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

mqxuamgl3#

对于那些还没有使用(或不能使用)Java8的人,Guava用IntMath.mod()来拯救他们,从Guava 11.0开始就可以使用。

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

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

0ejtzxu1

0ejtzxu14#

在数论中,结果总是正的。我猜计算机语言中并不总是这样,因为并不是所有的程序员都是数学家。恕我直言,我会认为这是语言的设计缺陷,但你现在不能改变它。
=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分钟,即使两个答案都满足基本方程。

5f0d552i

5f0d552i5#

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

如果输入在整数的整个范围内是随机的,那么两个解的分布将是相同的,如果输入聚类接近零,那么在后一个解中n - 1处的结果将太少。

agxfikkp

agxfikkp6#

下面是一个替代方案:

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

这可能比另一个公式[(a % b + b)% b]快,也可能不快。与另一个公式不同,它包含一个分支,但使用的模运算少一个。如果计算机能正确预测〈0,可能会成功。
(Edit:修正了公式。)

z9ju0rcb

z9ju0rcb7#

floorMod方法是最好的方法。
我很惊讶没有人张贴明显的。

Math.abs(a) % b

相关问题