Optimizing is-multiple checks with modular arithmetic

clang

gcc

unsigned divide(unsigned x) { return x / 679; }

divide: mov eax, edi imul rax, rax, 1619310203 # multiply by ceil(1/679 * 2**40) == 1619310203 shr rax, 40 # divide by 2**40 via right shift ret

bool is_multiple(unsigned x) { return x % 679 == 0; }

clang

gcc

is_multiple: mov eax, edi imul rax, rax, 1619310203 # multiply by ceil(1/679 * 2**40) == 1619310203 shr rax, 40 # divide by 2**40 via right shift imul ecx, eax, 679 # multiply the result by 679 again cmp edi, ecx # is it equal to the original value? sete al ret

double-width

3 * 2_863_311_531 == 0x2_0000_0001 == 1 (mod 2**32) 679 * 2_068_415_767 == 0x147_0000_0001 == 1 (mod 2**32) 65_535 * 4_294_901_759 == 0xfffe_0000_0001 == 1 (mod 2**32)

3 * 2_863_311_531 == 1 (mod 2**32) 6 * 2_863_311_531 == 2 (mod 2**32) 9 * 2_863_311_531 == 3 (mod 2**32) 36_912 * 2_863_311_531 == 12304 (mod 2**32) 679 * 2_068_415_767 == 1 (mod 2**32) 1358 * 2_068_415_767 == 2 (mod 2**32) 2037 * 2_068_415_767 == 3 (mod 2**32) 1_180_102 * 2_068_415_767 == 1738 (mod 2**32)

is_multiple

bool is_multiple(unsigned x) { // return x % 679 == 0; return x * 2068415767u < 6325431u; }

is_multiple: imul ecx, edi, 2068415767 cmp ecx, 6325431 setb al ret

Even numbers do not have a multiplicative inverse modulo 2ⁿ, but we can still use this trick, since they can be factored into a power of two and an odd number. For instance, 1738 = 869 × 2, and a number is a multiple of 1738 if it is a multiple of both 869 and 2. Furthermore, after dividing by the odd factor, the remaining quotient will be a multiple of the power of two if the original number was. Testing whether something is a multiple of 2 is done easily by bitwise-and of the low bits, and we can use the above trick to test whether something is a multiple of the odd factor. So:

bool is_multiple(unsigned x) { return x % 1738 == 0; }

bool is_multiple(unsigned x) { unsigned inv = x * 148272749u; // inverse of 869 mod 2**32 return inv < 4942425u // ceil(2**32/869) && (inv & 1) == 0; }

bool is_multiple(unsigned x) { unsigned inv = x * 148272749u; // inverse of 869 mod 2**32 inv = (inv >> 1u) | (inv << 31u); // rotate by one bit return inv <= 2471212u; // floor(2**32/869) >> 1 }

probably not.