algorithmmodulo

Is there any easy way to do modulus of 2³² - 1 operation?


I just heard about that x mod (2^32-1) and x / (2^32-1) would be easy, but how?

to calculate the formula:

xn = (xn-1 + xn-1 / b)mod b.

For b = 2^32, its easy, x%(2^32) == x & (2^32-1); and x / (2^32) == x >> 32. (the ^ here is not XOR). How to do that when b = 2^32 - 1.

In the page https://en.wikipedia.org/wiki/Multiply-with-carry. They say "arithmetic for modulus 2^32 − 1 requires only a simple adjustment from that for 2^32". So what is the "simple adjustment"?


Solution

  • (This answer only handles the mod case.)

    I'll assume that the datatype of x is more than 32 bits (this answer will actually work with any positive integer) and that it is positive (the negative case is just -(-x mod 2^32-1)), since if it at most 32 bits, the question can be answered by

    x mod (2^32-1) = 0 if x == 2^32-1, x otherwise
    x / (2^32 - 1) = 1 if x == 2^32-1, 0 otherwise
    

    We can write x in base 2^32, with digits x0, x1, ..., xn. So

      x = x0 + 2^32 * x1 + (2^32)^2 * x2 + ... + (2^32)^n * xn
    

    This makes the answer clearer when we do the modulus, since 2^32 == 1 mod 2^32-1. That is

      x == x0 + 1 * x1 + 1^2 * x2 + ... + 1^n * xn (mod 2^32-1)
        == x0 + x1 + ... + xn (mod 2^32-1)
    

    x mod 2^32-1 is the same as the sum of the base 2^32 digits! (we can't drop the mod 2^32-1 yet). We have two cases now, either the sum is between 0 and 2^32-1 or it is greater. In the former, we are done; in the later, we can just recur until we get between 0 and 2^32-1. Getting the digits in base 2^32 is fast, since we can use bitwise operations. In Python (this doesn't handle negative numbers):

    def mod_2to32sub1(x):
        s = 0 # the sum
    
        while x > 0: # get the digits
            s += x & (2**32-1)
            x >>= 32
    
        if s > 2**32-1:
            return mod_2to32sub1(s)
        elif s == 2**32-1:
            return 0
        else:
            return s
    

    (This is extremely easy to generalise to x mod 2^n-1, in fact you just replace any occurance of 32 with n in this answer.)

    (EDIT: added the elif clause to avoid an infinite loop on mod_2to32sub1(2**32-1). EDIT2: replaced ^ with **... oops.)