Frage

Ich habe den folgenden C-Code:

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

Wenn ich das kompiliere (einen MSP430-Plattform-Compiler für ein kleines eingebettetes O namens contiki das Ergebnis) 0, während ich 191 erwartet. (Uint8_t wird als unsigned char typedef)

Wenn ich ändern Sie es an:

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

es funktioniert richtig aus und druckt 191.

Kompilieren eine einfache Version dieses ‚normalerweise‘ mit gcc auf einer Ubuntu-Box druckt den richtigen Wert in beiden Fällen.

Ich bin nicht ganz sicher, warum das so ist. Ich konnte es umgehen, indem sie den definierten Wert einer Variablen zugewiesen vorher, aber ich möchte lieber nicht tun.

Weiß jemand, warum das so ist? Vielleicht mit einem Link zu mehr Informationen über das?

War es hilfreich?

Lösung

Die kurze Antwort: Sie Compiler ist fehlerhaft. (Es gibt kein Problem mit Überlauf, wie andere vorgeschlagen.)

In beiden Fällen wird die Arithmetik in int getan, die mindestens 16 Bit gewährleistet ist lang. Im ersteren Schnipsel ist es, weil 255 ein int ist, im letzteren ist es wegen der integraler Förderung .

Wie Sie bemerkt, gcc behandelt dies richtig.

Andere Tipps

255 als eine ganze Zahl wird wörtliche verarbeitet und bewirkt, dass der gesamte Ausdruck int zu beruhen, anstatt unsigned char basiert. Der zweite Fall zwingt den Typen richtig. Versuchen Sie, Ihre #define wie folgt:

 #define PRR_SCALE ((uint8_t) 255)

Wenn der Compiler in Frage die MSPGCC ist, sollte es ein Assemblerlisting des kompilierten Programms zusammen mit der Binär / Hex-Datei ausgegeben. Andere Compiler können zusätzlichen Compiler-Flags erforderlich, dies zu tun. Oder vielleicht sogar ein separater Disassembler auf dem binären ausgeführt werden.

Dies ist der Ort, an dem nach einer Erklärung zu suchen. Aufgrund Compiler-Optimierungen, könnte der eigentliche Code für den Prozessor vorgestellt haben nicht viel Ähnlichkeit mit dem ursprünglichen C-Code (aber normalerweise macht die gleiche Arbeit).

zu den wenigen Assembleranweisungen Stepping durch den fehlerhaften Code, der sollte die Ursache des Problems offenbart.

Meine Vermutung ist, dass der Compiler irgendwie die ganze Berechnung optimiert sice der definierte Konstante ein bekannter Teil bei der Kompilierung ist. 255 * x x optimiert werden << 8-x (das ist schneller und kleiner) etwas schief läuft mit dem optimierten Assembler-Code Vielleicht.

habe ich die Zeit, beide Versionen auf meinem System zu kompilieren. Bei aktiver Optimierung erzeugt die MSPGCC den folgenden Code:

#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

Wie wir sehen können, berechnet die Compiler direkt das Ergebnis von 255 * 3 bis -3 (0xFFFD). Und hier ist das Problem. Irgendwie wird die 255 interpretiert als -1 8-Bit statt 255 unsigned 16-Bit unterzeichnet. Oder ist es zunächst auf 8 Bit geparst und dann vorzeichenerweitert auf 16 Bit. oder was auch immer.

Eine Diskussion zu diesem Thema bereits auf der MSPGCC Mailingliste gestartet wurde.

Ich bin mir nicht sicher, warum das nicht funktioniert definieren, aber Sie könnten mit den uint8_t Variablen in Rollovers ausgeführt werden. 255 ist der höchste Wert für uint8_t (2^8 - 1), wenn Sie also von 3, sind Sie verpflichtet, laufen in einige subtile Roll Probleme vervielfachen das.

Der Compiler kann Code und Pre-Berechnung das Ergebnis der mathematischen Ausdruck und schob das Ergebnis in PRR werden Optimierung (da es passt, auch wenn der Zwischenwert passt nicht).

Überprüfen Sie, was, wenn Sie so brechen Ihren Ausdruck geschieht (dies wird nicht so verhalten, wie das, was Sie wollen):

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

Sie müssen nur einen größeren Datentyp verwenden.

Ein Unterschied, den ich in denken kann, Fall-1 ist,

Der PRR_SCALE Literalwert kann in ROM oder Code-Bereich gehen. Und vielleicht gibt es für etwa einen gewissen Unterschied in der MUL opecode sein

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

Es kann überhaupt keinen Sinn.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top