0

I'm porting some c code which is doing a modulo on an uint32_t. An uint32_t fits in a Java int bit-wise, but I cannot figure out how to perform a modulo operation on it without converting to a long. This is the code I have right now:

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

Can I perform this modulo calculation without converting to long?

Alexander Torstling
  • 18,552
  • 7
  • 62
  • 74

2 Answers2

6

Use 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.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Thanks mate, you're a saviour! Couldn't find this method for the life of me :) – Alexander Torstling Oct 19 '21 at 22:17
  • 1
    [Although that method actually converts to `long` for you](https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Integer.java#L1566) – dave_thompson_085 Oct 19 '21 at 23:19
  • It might, but these types of methods also serve as [substitution points for JITs](https://github.com/oracle/graal/blob/bf790bb46e11de9afa4446e63136b96722ab330f/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java#L659). From what I can tell, the JDK17 default C2 does not currently substitute it the way it does [other Integer methods](https://hg.openjdk.java.net/jdk/jdk/file/1fa1ec0e7048/src/hotspot/share/opto/library_call.cpp#l2281), but using this method directly is how you ensure you take advantage if/when it gets added. – that other guy Oct 20 '21 at 00:05
2

@that other guy's answer is preferred for Java 8+. I'll leave this here in case it's useful for anyone using older versions of Java.


Converting to a long is almost certainly the best way to do this.

Otherwise, you will need to branch; to get the correct result for a negative int value, you will need to adjust for the fact that the unsigned number that the int represents is offset by 232, and this offset generally won't have a remainder of 0 when you divide by the modulus. If the modulus is a constant (42 in your example) then you can hardcode the offset:

static int unsignedMod42(int x) {
    if(x >= 0) {
        return x % 42;
    } else {
        // 2**32 = 4 (mod 42)
        return ((x % 42) + 42 + 4) % 42;
    }
}

If the modulus is a variable then you have to compute the correct offset at runtime:

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;
    }
}

Note that because the offset here is being computed by repeated squaring, we can't allow a modulus for which the multiplication might overflow. There is presumably a better way to compute the correct offset - by repeated multiplication would allow a modulus up to Integer.MAX_VALUE / 2, for example.

kaya3
  • 47,440
  • 4
  • 68
  • 97