2012-01-29 10 views
12

Mam tablicę w C, którą chcę adresować w sposób podobny do bufora kołowego, więc na przykład: a[-1] zwróci mi ostatni element tablicy.Dziwne wyniki arytmetyki modulo z liczbami ujemnymi i unsigned mianownikami

Aby to zrobić Próbowałem użyć arytmetyki modulo (oczywiście), problemem jest to, Dostaję dość dziwne wyniki, gdy zaangażowane są liczby ujemne:

-1 % 4 = -1 
-1 % 4U = 3 

Tak daleko, tak dobrze.

-1 % 4000 = -1 
(-1+4000U) % 4000U = 3999 
(-1) % 4000U = 3295 

Pytanie: Wartość (3295) nie trzymać za (a/b)*b + a%b shall equal a, truncated towards zero (dla a=-1, b=4000) od standardu C (6.5.5 # 6), więc nie jest to bug per se, ale dlaczego jest standard zdefiniowany w ten sposób ?! Na pewno musi być jakaś logika w tym ...

Jak mam napisać a%b uzyskać sensownych wyników dla ujemny a (jak (a+b)%b przestaje działać, gdy abs(a)>b)?

aplikacja testowa:

#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; 
} 

Odpowiedz

9

arytmetyczne w C zawsze (krótka niektórych dziwactw z operatorami nieco przesunięcia) promuje wszystkie argumenty do wspólnego typu przed wykonaniem operacji. Zatem:

(-1) % 4000U 

jest promowany jako (przy założeniu 32-bitowych ints):

0xffffffffu % 4000u 

co daje 3295.

Jeśli chcesz użyć arytmetykę modularną dla offsetu tablicowych, które mogłyby mieć negatywny, to najpierw trzeba zrezygnować z niepodpisanej arytmetyki na offsetach. Jako takie, twoje wyniki będą teraz w zakresie od -MAX_NUM+1 do MAX_NUM-1, ze względu na brzydką definicję dzielenia liczb całkowitych ze znakiem i resztę. Jeśli kod nie jest krytyczny dla wydajności, po prostu dodaj if (result<0) result+=MAX_NUM; i gotowe. Jeśli naprawdę potrzebujesz omijać gałąź (i masz zmierzoną, aby ustalić, że musisz tego uniknąć), to spytaj ponownie, jak zoptymalizować to obliczenie i ja lub ktoś jaśniejszy ode mnie na SO z pewnością będzie w stanie ci pomóc. :-)

5

Zgodnie z 6.5.3, "Zwykłe konwersje arytmetyczne są wykonywane na operandach." W przypadku swojej przykład:

(-1) % 4000U 

to znaczy konwersja do -1 z unsigned int. Twoja wartość -1 jest więc interpretowana jako 4294967295 ... dla której pozostała część jest dokładnie taka, jaką widzisz: 3295.

"Zwykłe konwersje arytmetyczne" opisano w 6.3.1.8.

Powiązane problemy