GNU ARM GCC ignoriert "signed" Variablen

OP #6759872
Lesenswert?

Guten Abend zusammen,

ich habe vor Jahren ein Projekt entwickelt um den BMP180-Druck- und 
Temperatursensor in eine Telemetrieeinheit für einen Stratosphärenballon 
zu integrieren. Jetzt habe ich den Code auf einen STM32F4 portiert und 
stehe vor einem kleinen Problem:

Ich lese die Kalibrierdaten aus dem EEPROM mit I2C aus, unter denen sich 
auch einige negative Werte befinden.

Z. B. hat die Variable "ac2" aus dem Parametersatz den Wert -1056. 
Diesen habe ich ermittelt, indem ich den Sensor an den AVR angeschlossen 
habe, um mir die Werte aus dem EEPROM anzeigen zu lassen. Auf dem AVR 
ist die Ausgabe korrekt ("-1056"), auf dem STM32 nicht. Beide Compiler 
(jeweils aus der GCC) laufen auf dem gleichen Rechner unter Linux Mint 
18.

Das Auslesen der Daten aus dem Sensor beim STM32 funktioniert 
fehlerfrei. Wenn ich die Software auf diesem Kontroller laufen lasse, 
erhalte ich aus den beiden Registern die zur Variablen "ac2" gehörenden 
Werte 0xAC=251 und 0xAD=224. Nach der entsprechenden Addition von MSB 
und LSB ergibt sich ein Wert von 64480 also genau das, was dem Wert 
-1056 als "signed" Variable entspricht.

Hier der relevante Codeauszug:

int ac2;
...
int msb, lsb;

msb = i2c_read(0xAC);
lsb = i2c_read(0xAD);
ac2 = (msb << 8) + lsb;

Die Funktion zum Lesen der Daten aus dem Sensor ist ebenfalls als 
"signed" definiert:

int i2c_read(uint8_t regaddr)
{....}

Wie bekomme ich den Compiler dazu, das 2er-Komplement korrekt zu 
behandeln? Eine explizite Typumwandlung hat keinen Erfolg gebracht.

Vielen Dank für's Lesen!

Peter
Moderator Persönliche Seite #6759929
Lesenswert?

Al Fine schrieb:

>> int msb, lsb;
>>
>> msb = i2c_read(0xAC);
>> lsb = i2c_read(0xAD);
>> ac2 = (msb << 8) + lsb;
>
> Das ist ein potentieller Overflow.

Insbesondere ist es undefined behaviour, wenn "msb" eine negative Zahl 
ist.

"If E1 has a signed type and nonnegative value, and E1 × 2 E2 is
representable in the result type, then that is the resulting value; 
otherwise, the behavior is undefined."
#6760091
Lesenswert?

Du hast da vollkommen recht.

Aber ich hatte es so verstanden, daß die Lesefunktion unabänderlich 
vorgegeben ist und unsinnigerweise ein Byte als int liefert.

Wenn man einen Daumen auf der Funktion hat, sollte man sie in der Tat zu 
uint8_t abändern.
Dann würden meine beiden 0xFF&... entfallen.
Gast #6760839
Lesenswert?

mh schrieb:

> Kann man machen. Man kann es aber auch einfach richtig machen.

Das ist "richtig". Deswegen wird das im Linuxkernel auch so gemacht. 
Schließlich sind CPUs, auf denen das Zweierkomplement nicht genutzt 
wird, ausgesprochen unüblich geworden - und nur das war überhaupt der 
Grund für das UB. Mal abgesehen davon, daß das von Anfang an nicht UB, 
sondern IB hätte sein sollen.
Moderator Persönliche Seite #6760845
Lesenswert?

Nop schrieb:
> Das ist "richtig".

Für den hier genannten Fall nicht, denn es geht auch anders (und damit 
durchaus schöner und standardkonform portabel). Auch würde ich sowas wie 
einen Kernel (egal von wem) nicht als Referenz für "das ist richtig" 
benutzen, wenn es um Features einer Programmiersprache geht.

Nicht-2er-Komplement-Maschinen könnten perspektivisch im C-Standard 
keine Berücksichtigung mehr finden, wenn ich die Diskussionen in WG14 
richtig aufgefasst habe, über das, was UB darf und was nicht, wird 
gerade heiß diskutiert.
Gast #6760914
Lesenswert?

Jörg W. schrieb:

> Für den hier genannten Fall nicht, denn es geht auch anders

Naja, man kann natürlich einfach entsprechend aufaddieren, um im 
Wertebereich eines uint16_t zu sein. Oder, sofern man die Endianess im 
Griff hat, kann man auch mit memcpy arbeiten (wird ja wegoptimiert), 
oder zumindest für C mit einer union.

> Auch würde ich sowas wie einen Kernel (egal von wem) nicht als Referenz
> für "das ist richtig" benutzen, wenn es um Features einer
> Programmiersprache geht.

Der Hinweis ist dahingehend zu verstehen, daß die leitenden Entwickler 
des Linuxkernels kompetenter sind als jeder Poster hier und insofern 
diese Lösung nicht pauschal als Anfängerquark zu verwerfen ist.

Schlußendlich geht es nicht darum, den C-Standard zu befriedigen, 
sondern richtig arbeitende Software zu schreiben. Wenn man dabei um den 
teilweise ziemlich kaputten Standard herumarbeitet und sich dessen 
bewußt ist, kann das durchaus vertretbar sein.

> Nicht-2er-Komplement-Maschinen könnten perspektivisch im C-Standard
> keine Berücksichtigung mehr finden, wenn ich die Diskussionen in WG14
> richtig aufgefasst habe

Wäre sinnig.

> über das, was UB darf und was nicht, wird gerade heiß diskutiert.

Da ist man damals einfach zu sehr mit der Gießkanne durchmarschiert - 
bis dahin, daß simple Compiletime-Fehler als UB definiert wurden 
(fehlendes abschließendes ' oder " etwa).
Gast #6760962
Lesenswert?

Jörg W. schrieb:

> Nein, eine Behauptung. Es deklariert eine Art Gottheit

Nein, tut es nicht. In einer Meritokratie stehen Leute nämlich nicht aus 
Dogma an der Spitze, sondern weil sie mehr zuwege gebracht haben als 
jeder hier im Forum.

Sofern Du keine vergleichbare Meriten vorzuweisen hast, zeugt es 
lediglich von Dunning-Kruger Deinerseits, wenn Du mit religiösen 
Begriffen ankommst.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren