I'm trying to mod an integer to get an array position so that it will loop round. Doing i %
arrayLength
works fine for positive numbers but for negative numbers it all goes wrong.
4 % 3 == 1
3 % 3 == 0
2 % 3 == 2
1 % 3 == 1
0 % 3 == 0
-1 % 3 == -1
-2 % 3 == -2
-3 % 3 == 0
-4 % 3 == -1
so i need an implementation of
int GetArrayIndex(int i, int arrayLength)
such that
GetArrayIndex( 4, 3) == 1
GetArrayIndex( 3, 3) == 0
GetArrayIndex( 2, 3) == 2
GetArrayIndex( 1, 3) == 1
GetArrayIndex( 0, 3) == 0
GetArrayIndex(-1, 3) == 2
GetArrayIndex(-2, 3) == 1
GetArrayIndex(-3, 3) == 0
GetArrayIndex(-4, 3) == 2
I've done this before but for some reason it's melting my brain today :(
I always use my own
mod
function, defined asOf course, if you're bothered about having two calls to the modulus operation, you could write it as
or variants thereof.
The reason it works is that "x%m" is always in the range [-m+1, m-1]. So if at all it is negative, adding m to it will put it in the positive range without changing its value modulo m.
ShreevatsaR's answer won't work for all cases, even if you add "if(m<0) m=-m;", if you account for negative dividends/divisors.
For example, -12 mod -10 will be 8, and it should be -2.
The following implementation will work for both positive and negative dividends / divisors and complies with other implementations (namely, Java, Python, Ruby, Scala, Scheme, Javascript and Google's Calculator):
Test suite using xUnit:
Single-line implementation using
%
only once:Please note that C# and C++'s % operator is actually NOT a modulo, it's remainder. The formula for modulo that you want, in your case, is:
You have to recode this in C# (or C++) but this is the way you get modulo and not a remainder.
Comparing two predominant answers
and
Nobody actually mentioned the fact that the first one may throw an
OverflowException
while the second one won't. Even worse, with default unchecked context, the first answer may return the wrong answer (seemod(int.MaxValue - 1, int.MaxValue)
for example). So the second answer not only seems to be faster, but also more correct.I like the trick presented by Peter N Lewis on this thread: "If n has a limited range, then you can get the result you want simply by adding a known constant multiple of [the divisor] that is greater that the absolute value of the minimum."
So if I have a value d that is in degrees and I want to take
and I want to avoid the problems if d is negative, then instead I just do this:
This assumes that although d may be negative, it is known that it will never be more negative than -720.