PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Integer overflow. aber wo?



yaro
06.06.2009, 01:11
Hallo Leute,
ich habe hier eine Rechnung, die aufgrund eines Zahlenüberlaufs nicht richtig gerechnet wird. Aber ich kann mir einfach nicht vorstellen, wo dieser Zahlenüberlauf sein soll. Kann vielleicht jemand helfen?

((int32_t)((150*150)+(130*130)-(151*151)))*1024/(2*130*150)

Ich habe extra viel Klammern gesetzt (auch unnötige), um Missverständnisse auszuschließen.

selbst in dieser Form wird ein Integer overflow vom Compiler angezeigt (aber jetzt nur 1 mal, statt 2):
((int32_t)((uint16_t)(150*150)+(uint16_t)(130*130)-(uint16_t)(151*151)))*1024/(uint16_t)(2*130*150)

Achja, ich führ die Rechnung auf einem 8bit AVR aus. Anstatt der Zahlen stehen eigentlich Variablen.

Danke im Voraus, Yaro

SprinterSB
06.06.2009, 10:10
Poste einfach mal ein Stück übersetzbaren Originalcode. Da sieht man besser was geht oder nicht. Oder den Cosinussazu oder was auch immer in mehrere Teilen aufteilen.

yaro
06.06.2009, 13:52
Genau, es ist der Cosinussatz! Umgeformt nach dem Winkel und mit 1024 multipliziert. Das arccos habe ich hier mal ausgelassen, da es nichts mit dem Problem zutun hat, dass es einen Integer-overflow gibt.

Der Code (für die rechnung) sieht folgendermaßen aus:



char buf[7];
uint8_t us = 150, os = 130, d123 = 151; //d123 ist eigentlich unterschiedlich, die Berechnung ist aber
//etwas kompilzierter, und tut nix zur Sache.

itoa(((int32_t)((us*us)+(os*os)-(d123*d123)))*1024/(2*us*os), buf, 10);

//anschließend buf ausgeben



Die Warnung wird meißt nur dann angezeigt, wenn man die Konstanten direkt in die Rechnung einsetzt, wenn sie als Variablen eingesetzt werden, dann gibt es (fast immer) keine Warnungen.
Wenn ich diese Rechnung mit dem PC ausführe, dann liefert sie das richtige Ergebniss 435, mit dem AVR bekomme ich -640 bei raus.
Ich hätte gedacht, dass das Problem ist, dass z.B. us*us größer ist, als ein signed int (16bit) speichern kann (ist es aber nicht). d123 könnte aber groß genug werden... . Wie kann ich dieses Problem beheben? habe (wie zuvor schon gepostet) auch versucht, jede einzelne Klammer in uint16_t zu casten. Hat anscheinend was gebracht, aber immer noch nicht genug. Es wird anstatt 2 overflows dann nur noch einer vom compiler angezeigt.
Die Rechnung wird dann allerdings richtig ausgeführt (zumindest mit diesen Werten). Das Problem ist damit trotzdem nicht gelöst.

Gruß, Yaro

CsT
06.06.2009, 15:06
Hi yaro, teste doch mal ltoa() anstatt itoa(). itoa ist für Integer und der ist doch auf dem AVR nur 8 bit breit, oder?

Hoffe das hilft ;)

Grüße Tobi

SprinterSB
06.06.2009, 17:00
uint8_t us = 150, os = 130, d123 = 151;

int32_t bar (void)
{
uint16_t usus = us*us;
uint16_t osos = os*os;
uint16_t usos = us*os;
uint16_t d123d123 = d123*d123;
return ((int32_t) usus+osos-d123d123)*512 / usos;
}


Die Quadrate werden berechnet als 8*8 = 16, denn int ist bei avr-gcc 16 Bits breit. Daher wird bei den Operationen implizit zu 16 Bits erweitert.

Bei der 32-Bit OP muss der Cast innerhalb der Klammern stehen. Ansonsten wird mit 16 Bits gerechnet und erst danchauf 32 Bits erweitert. Die Strichrechnungen sollen aber auf 32 Bit signed erfolgen. Wenn die 2 nicht gekürzt wird, dann muss auch in den Nenner ein Cast, weil sonst das *2 auch auf 16 Bit gemacht wird. Ausserdem kann dann ein Überlauf problematisch werden, der einen positiven Wert zu einem negativen macht.

Du hast hier 3 Effekte, die zusammenspielen:
-- Explizite Casts
-- Implizite Casts
-- Übergang von Unsigned- zu Signed-Arithmetik

Wenn du dir in Bezug auf die C-Spez nicht sicher bist, dann mach es häppchenweise und nicht in einem großen Klumbatsch. Viel Klammern helfen net viel ;-)

yaro
07.06.2009, 14:35
Alles klar =)
Das mit dem cast funktioniert jetzt auch richtig. Ich habe vorher immer angenommen, dass wenn ich eine Klammer caste, sie auch in dem Format ausgerechnet wird. Aber wenn man recht darüber nachdenkt, ist das eigentlich unlogisch.

Ein weiteres mal vielen Dank für deine Hilfe =)