Java 中的无符号模

Unsigned modulo in Java

我正在移植一些对 uint32_t 求模的 C 代码。 uint32_t 按位适合 Java int,但我不知道如何在不转换为 long 的情况下对其执行模运算。这是我现在的代码:

int i = 0xffffffff;
long asUnsigned = Integer.toUnsignedLong(i);
int mod = (int) (asUnsigned % 42L);

我可以在不转换为 long 的情况下执行此模计算吗?

@那个人的答案更适合 Java 8+。我会把它留在这里,以防它对使用旧版本 Java.

的任何人有用

转换为 long 几乎可以肯定是最好的方法。

否则需要分支;要获得负 int 值的正确结果,您需要调整 int 表示的无符号数被 232 偏移的事实,并且当您除以模数时,此偏移量通常不会有 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

使用Integer.remainderUnsigned(int dividend, int divisor) (javadoc):

Returns the unsigned remainder from dividing the first argument by the second where each argument and the result is interpreted as an unsigned value.