Include the tests
No tests were provided, so I had to write my own test functions:
#include <stdio.h>
int expect_eq(const char* file, const int line,
const char* sa, const char *sb,
int a, int b)
{
if (a == b) { return 0; }
fprintf(stderr, "%s:%d: error: %s (=%d) != %s (=%d)\n",
file, line, sa, a, sb, b);
return 1;
}
#define EXPECT_EQ(a, b) expect_eq(__FILE__, __LINE__, #a, #b, a, b)
int main(void)
{
return EXPECT_EQ(isDivby9(0), 1)
| EXPECT_EQ(isDivby9(1), 0)
/* TODO: add more tests */
;
}
The very first test I tried failed: isDivby9(0)
returned a false value, but 0 is exactly 0✕9. That's a failed review right there.
Implement the algorithm as written
The referenced code uses a recursive call of isDivby9()
, but this implementation does not. If it's to be a direct implementation, make it true to the source (which we could do by directly copying the C++ function, if we include <stdbool.h>
first).
It may be slightly more useful if we divide it into two functions:
#include <stdbool.h>
int mod9(int x)
{
if (x < 9) { return x; }
x = (x & 07) - mod9(x >> 3);
if (x >= 9) x -= 9; /* range correction for subtraction */
return x;
}
bool isDivby9(int x)
{
return mod9(x) == 0;
}
Consider a simpler algorithm
We know that to find the residue of a number modulo m, we can express it in base (m+1) and simply add the digits (this is how the well-known check for multiples of 9 works in decimal). If we choose base-64 for our representation, that's convenient for bitwise operations and 63 is a multiple of 9.
Once we have reduced the number modulo 63, then we have two octal digits. The "eights" digit can be subtracted from the units digit (since 8✕n ≡ -1✕n, mod 9).
That gives us a nice iterative algorithm:
int mod9(int x)
{
/* add base-64 digits */
while (x >= 0100) {
x = (x & 077) + (x >> 6);
}
/* we now have two octal digits - subtract the eights from the units */
x = (x & 07) - (x >> 3);
x += x < 0 ? 9 : x >= 9 ? -9 : 0; /* bring subtraction into range */
return x;
}
It still gives us incorrect answers for negative values, except on rare platforms where int
has a multiple of 6 bits.
Handle negative values correctly
This function accepts an int
, which has a range of at least [-32767,32767]. But we produce incorrect answers for much of that range.
A simple way to get the right results for negative numbers is to add a large multiple of 9 to make a positive number without changing the result. To create a large multiple of 9, the easiest way is to take a smaller one (no more than the smallest INT_MAX
we are guaranteed), and shift it left as far as we are able. Then we should only have to add this value twice at most:
static const int adjustment =
/* A large multiple of 9 */
07777 << (CHAR_BIT * sizeof adjustment - 1 - 12);
while (x < 0) { x += adjustment; } /* max 2 iterations */
Modified code
Applying the above changes, and including the tests that motivate them, we get:
#include <limits.h>
#include <stdbool.h>
int mod9(int x)
{
static const int adjustment =
/* A large multiple of 9 */
07777 << (CHAR_BIT * sizeof adjustment - 1 - 12);
while (x < 0) { x += adjustment; } /* max 2 iterations */
/* add base-4096 digits */
while (x >= 010000) {
x = (x & 07777) + (x >> 12);
}
/* add base-64 digits */
x = (x & 077) + (x >> 6);
/* we now have two octal digits, plus possibly one overflow - subtract the eights from the units */
x = 9 + (x & 07) - (x >> 3); /* adding 9 ensures a positive result */
x = (x & 07) - (x >> 3);
return x;
}
bool isDivby9(int x)
{
return mod9(x) == 0;
}
#include <stdio.h>
int expect_eq(const char* file, const int line,
const char* sa, const char *sb,
int a, int b)
{
if (a == b) { return 0; }
fprintf(stderr, "%s:%d: error: %s (=%d) != %s (=%d)\n",
file, line, sa, a, sb, b);
return 1;
}
#define EXPECT_EQ(a, b) expect_eq(__FILE__, __LINE__, #a, #b, a, b)
int main(void)
{
return EXPECT_EQ(mod9(0), 0)
| EXPECT_EQ(mod9(1), 1)
| EXPECT_EQ(mod9(8), 8)
| EXPECT_EQ(mod9(9), 0)
| EXPECT_EQ(mod9(10), 1)
| EXPECT_EQ(mod9(32075), 8)
| EXPECT_EQ(mod9(32076), 0)
| EXPECT_EQ(mod9(32767), 7)
| EXPECT_EQ(mod9(-1), 8)
| EXPECT_EQ(mod9(-8), 1)
| EXPECT_EQ(mod9(-9), 0)
| EXPECT_EQ(mod9(-10), 8)
| EXPECT_EQ(mod9(-17), 1)
| EXPECT_EQ(mod9(-18), 0)
| EXPECT_EQ(mod9(-19), 8)
| EXPECT_EQ(mod9(-32075), 1)
| EXPECT_EQ(mod9(-32076), 0)
| EXPECT_EQ(mod9(-32767), 2)
;
}
Performance-wise, this falls roughly halfway between JS1 and Gnasher implementations, at 3¾ times JS1(multiply) on my system. But be aware that JS1 requires a positive 32-bit int
, whereas mine is fully portable.
Interestingly, with my compiler, a simple x%9
beats all bitwise implementations presented in answers here - I confirmed that GCC uses a "magic" multiplication internally (and, obviously, tailored to its target architecture, so totally portable at the source-code level).
Exercise
Use this technique to test whether a number is a multiple of 11.
Hint: 2⁵ + 1 = 3 ✕ 11