我正在移植一些在 uint32_t 上进行模运算的 c 代码。uint32_t 按位适合 Java int,但我无法弄清楚如何在不转换为 long 的情况下对其执行模运算。这是我现在拥有的代码:
int i = 0xffffffff;
long asUnsigned = Integer.toUnsignedLong(i);
int mod = (int) (asUnsigned % 42L);
我可以在不转换为 long 的情况下执行此模计算吗?
我正在移植一些在 uint32_t 上进行模运算的 c 代码。uint32_t 按位适合 Java int,但我无法弄清楚如何在不转换为 long 的情况下对其执行模运算。这是我现在拥有的代码:
int i = 0xffffffff;
long asUnsigned = Integer.toUnsignedLong(i);
int mod = (int) (asUnsigned % 42L);
我可以在不转换为 long 的情况下执行此模计算吗?
使用Integer.remainderUnsigned(int dividend, int divisor)
(javadoc):
返回第一个参数除以第二个参数得到的无符号余数,其中每个参数和结果都被解释为无符号值。
@that other guy's answer是Java 8+的首选。我将把它留在这里,以防它对使用旧版本 Java 的人有用。
转换为 along
几乎可以肯定是最好的方法。
否则,您将需要分支;要获得负值的正确结果int
,您需要调整表示的无符号数int
偏移 2 32的事实,并且当您除以模数时,该偏移量通常不会有余数 0。如果模数是常数(在您的示例中为 42),那么您可以对偏移量进行硬编码:
static int unsignedMod42(int x) {
if(x >= 0) {
return x % 42;
} else {
// 2**32 = 4 (mod 42)
return ((x % 42) + 42 + 4) % 42;
}
}
如果模数是一个变量,那么您必须在运行时计算正确的偏移量:
static int unsignedMod(int x, int y) {
if(y <= 0 || y * y <= 0) {
throw new IllegalArgumentException("y = " + y);
} else if(x >= 0) {
return x % y;
} else {
// compute 2**32 mod y, by repeated squaring
int offset = 2;
for(int i = 0; i < 5; ++i) { offset = (offset * offset) % y; }
return ((x % y) + y + offset) % y;
}
}
请注意,因为这里的偏移量是通过重复平方计算的,所以我们不能允许乘法可能溢出的模数。可能有一种更好的方法来计算正确的偏移量 - 例如,通过重复乘法将允许模数达到Integer.MAX_VALUE / 2
。