I have an array in C that I want to address in manner similar to a circular buffer, so for example: a[-1]
would return me the last element of the array.
To do that I tried to use modulo arithmetic (obviously), problem is, I'm getting quite weird results when negative numbers are involved:
-1 % 4 = -1
-1 % 4U = 3
So far, so good.
-1 % 4000 = -1
(-1+4000U) % 4000U = 3999
(-1) % 4000U = 3295
Question: The value (3295) does hold for the (a/b)*b + a%b shall equal a, truncated towards zero
(for a=-1, b=4000
) from C standard (6.5.5#6) so it's not a bug per se, but why is the standard defined this way?! Surely, there must be some logic in this...
How do I have to write a%b
to get sensible results for negative a
(as (a+b)%b
stops working when abs(a)>b
)?
Test application:
#include <stdio.h>
int main(int argc, char **argv) {
int i=0;
#define MAX_NUM 4000U
int weird = (i-1)%MAX_NUM;
printf("%i\n", weird);
printf("%i\n", (i-1+MAX_NUM))%MAX_NUM);
printf("a: %i, b: %i, a from equation: %i\n", i-1, MAX_NUM,
((i-1)/MAX_NUM)*MAX_NUM + weird);
return 0;
}
Arithmetic in C always (short of some oddities with bit shift operators) promotes all operands to a common type before performing the operation. Thus:
(-1) % 4000U
is promoted as (assuming 32 bit ints):
0xffffffffu % 4000u
which yields 3295.
If you want to use modular arithmetic for array offsets that could be negative, you first need to abandon using unsigned arithmetic on the offsets. As such, your results will now be in the range -MAX_NUM+1
to MAX_NUM-1
, due to C's ugly definition of signed integer division and remainder. If the code is not performance critical, just add if (result<0) result+=MAX_NUM;
and be done with it. If you really need to avoid the branch (and you've measured to determine that you need to avoid it) then ask again how to optimize this computation and myself or someone brighter than me on SO will surely be able to help. :-)
As 6.5.3 says, "The usual arithmetic conversions are performed on the operands." In the case of your example:
(-1) % 4000U
that means converting the -1 into an unsigned int
. Your -1, therefore, is really being interpreted as 4294967295... for which the remainder is exactly what you're seeing: 3295.
The "Usual arithmetic conversions" are described in 6.3.1.8.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With