2009-04-26 9 views
6

Mam następujący kod C:Język C: wartość #DEFINEd powoduje 8-bitowe mnożenie. Czemu?

#define PRR_SCALE 255 
... 
uint8_t a = 3; 
uint8_t b = 4; 
uint8_t prr; 
prr = (PRR_SCALE * a)/b; 
printf("prr: %u\n", prr); 

Jeśli mogę skompilować to (przy użyciu kompilatora platformy MSP430, za niewielką wbudowaną OS o nazwie contiki) wynik jest 0, a ja spodziewałem 191. (uint8_t jest typedef” ed jako unsigned char)

Gdybym go zmienić na:

uint8_t a = 3; 
uint8_t b = 4; 
uint8_t c = 255; 
uint8_t prr; 
prr = (c * a)/b; 
printf("prr: %u\n", prr); 

działa poprawnie i drukuje 191.

Skompilowanie prostej wersji tego "normalnie" przy użyciu gcc na systemie Ubuntu wyświetla prawidłową wartość w obu przypadkach.

Nie jestem do końca pewien, dlaczego tak jest. Mogłem go obejść, przypisując wcześniej wartość DEFINED do zmiennej, ale wolałbym tego nie robić.

Czy ktoś wie, dlaczego tak jest? Być może z linkiem do jakiegoś więcej informacji na ten temat?

+0

Z pewnością spodziewałbym się wydrukować 191. W drugim przypadku pierwsze c i a są promowane do int niezależnie, więc ich mnożenie nie może przepełnić. To samo dzieje się w pierwszym przypadku (chociaż PRR_SCALE jest już int - ale to nie zmieni również promocji dla int). twój gcc na twoim polu zachowuje się dokładnie dobrze. –

+0

sprawdź, czy posiadasz nagłówek stdio.h. Wiem, że jeden kompilator dla msp430 pozwala na deklaracje funkcji domniemanych: w takim przypadku wywołanie printf spowodowałoby niezdefiniowane zachowanie, a wynik "0" zostałby wyjaśniony w ten sposób. tylko moje dwa centy. nie myśl, że to jest warte odpowiedzi :) –

+0

@litb: stdio.h jest wliczony w cenę. – Rabarberski

Odpowiedz

10

Krótka odpowiedź: kompilator jest błędny. (Nie ma problemu z przepełnieniem, jak sugerowali inni.)

W obu przypadkach arytmetyka jest wykonywana w int, z gwarancją co najmniej 16 bitów. W poprzednim fragmencie jest to, ponieważ 255 jest int, w tym ostatnim jest to ze względu na integral promotion.

Jak zauważyłeś, gcc obsługuje to poprawnie.

+0

"arytmetyka jest wykonywana w int, z gwarancją co najmniej 16-bitowej długości" - Czy możesz podać na to źródło? Pytam o jego słuszność ... – strager

+1

"arytmetyka jest wykonywana w int": wynik integralnej promocji. "[int] ma co najmniej 16-bitową długość": gwarantowane pośrednio, INT_MAX musi wynosić co najmniej 32767, a INT_MIN co najwyżej -32767 (zobacz sekcję o limitach.h w standardzie). – avakar

+0

+1, tego właśnie spodziewałem się po debugowaniu. Czy możesz podać link, który określa sposób konwersji wartości? A może to tylko arytmetyczna budowa? – JaredPar

2

255 jest przetwarzany jako liczba całkowita i powoduje, że całe wyrażenie ma postać opartą na int, a nie unsigned char. Drugi przypadek zmusza typ do poprawności. Spróbuj zmienić #define następująco:

#define PRR_SCALE ((uint8_t) 255) 
+0

Nie działa, już próbowałam tego (dobrze, zrobiłem obsadę bezpośrednio w wierszu obliczeń, ponowiłem go zgodnie z sugestią, bez różnicy). – Rabarberski

+0

Oba przykłady kodu, arytmetyka jest wykonywana w int.Jest to spowodowane promocjami całkowitymi, patrz http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf, Rozdział 6.3.1.1, akapit 2. –

1

Nie jestem pewien, dlaczego określenie nie działa, ale może być uruchomiony na najazdy z uint8_t zmiennych. 255 jest maksymalną wartością dla uint8_t (2^8 - 1), więc jeśli pomnożysz tę liczbę przez 3, będziesz musiał napotkać pewne subtelne problemy z przewracaniem.

Kompilator może optymalizować kod i wstępnie obliczać wynik wyrażenia matematycznego i przesuwać wynik w prr (ponieważ pasuje, nawet jeśli wartość pośrednia nie pasuje).

Sprawdź, co się dzieje, jeśli rozbić swój wyraz tak (to nie będzie zachowywać się jak co chcesz):

prr = c * a; // rollover! 
prr = prr/b; 

może trzeba po prostu użyć większej typ danych.

+1

Technicznie c * a nie przelewowy. Standard C określa, że ​​przepełnienie nie może się zdarzyć w przypadku niepodpisanych typów całkowitych. – JaredPar

+0

Fajnie, to ma sens. Sądzę, że "rollover" byłby lepszym terminem. –

0

Jedna różnica może myślę, że w przypadku-1 jest

PRR_SCALE wartość dosłowna może przejść do ROM lub obszaru kodu. I może być pewna różnica w opcjach MUL, powiedzmy,

case-1: [register], [rom] 
case -2: [register], [register] 

To może nie mieć żadnego sensu.

2

Jeśli dany kompilator to mspgcc, powinien umieścić listę asemblera skompilowanego programu razem z plikiem binarnym/hex. Inne kompilatory mogą wymagać dodatkowych flag kompilatora, aby to zrobić. A może nawet osobny dezasembler działa na binarnym.

To jest miejsce, w którym należy szukać wyjaśnienia. Z powodu optymalizacji kompilacji, rzeczywisty kod przedstawiony procesorowi może nie mieć dużego podobieństwa do oryginalnego kodu C (ale zwykle wykonuje to samo zadanie).

Przejście przez kilka instrukcji asemblera reprezentujących wadliwy kod powinno ujawnić przyczynę problemu.

Domyślam się, że kompilator w jakiś sposób optymalizuje cały kod obliczeniowy, którego zdefiniowana stała jest znaną częścią podczas kompilacji. 255 * x może być zoptymalizowany do x < < 8-x (który jest szybszy i mniejszy) Być może coś pójdzie nie tak ze zoptymalizowanym kodem asemblera.

Poświęciłem czas na skompilowanie obu wersji w moim systemie. Przy aktywnym optymalizacji, mspgcc produkuje następującą kod:

#define PRR_SCALE 255 
uint8_t a = 3; 
uint8_t b = 4; 
uint8_t prr; 
prr = (PRR_SCALE * a)/b; 
    40ce: 3c 40 fd ff  mov #-3, r12 ;#0xfffd 
    40d2: 2a 42   mov #4, r10 ;r2 As==10 
    40d4: b0 12 fa 6f  call __divmodhi4 ;#0x6ffa 
    40d8: 0f 4c   mov r12, r15 ; 
printf("prr: %u\n", prr); 
    40da: 7f f3   and.b #-1, r15 ;r3 As==11 
    40dc: 0f 12   push r15  ; 
    40de: 30 12 c0 40  push #16576  ;#0x40c0 
    40e2: b0 12 9c 67  call printf  ;#0x679c 
    40e6: 21 52   add #4, r1 ;r2 As==10 

Jak widać, kompilatora oblicza wynik 255 * 3 do 3 (0xfffd). I tu jest problem. W jakiś sposób 255 zostaje zinterpretowane jako -1-podpisane 8-bitowe zamiast 255 bez znaku 16-bitowego. Lub najpierw jest przetwarzany na 8 bitów, a następnie rozszerzany na 16 bitów. lub cokolwiek.

Dyskusja na ten temat została już rozpoczęta na liście mailingowej mspgcc.