Forum: Compiler & IDEs C und Wertebereiche


von Micha (Gast)


Lesenswert?

ich werkel gerade an einem Projekt mit einem Attiny, in AVR-GCC. Bis ich 
alles im Verbund testen kann wirds noch ein Weilchen dauern, zu dem 
folgenden Detail hab ich eine Unklarheit. Mein code enthält folgende 
Zuweisung:
1
t1 = (stunde % 12) * 3600 + minute * 60 + sekunde;
t1 ist vom Typ uint16_t, die Variablen auf der rechten Seite sind alle 
uint8_t. Für sich ist jede Variable geeignet für ihren Wertebereich, 
aber wird die Zuweisung so funktionieren wie beabsichtigt? Ich vermute 
"ja", da die Konstante 3600 auf der rechten Seite dem Compiler einen 
Wertebereich größer 8 Bit signalisiert. Sicher bin ich aber nicht.

von (prx) A. K. (prx)


Lesenswert?

Die Rechnung wird in "int" stattfinden, und das sind mindestens 16 Bits. 
Sie würde aber per Sprachdefinition auch dann in "int" stattfinden, wenn 
dort 36 statt 3600 stünde. Erst bei 36000 könnte sich das je nach 
Zielmaschine ändern.

Da allerdings 11 * 3600 auf deinem AVR nicht in "int" passt ist das 
Ergebnis undefiniert. Korrekt wäre beispielsweise
  t1 = (stunde % 12) * 3600U + minute * 60 + sekunde;

: Bearbeitet durch User
von Irgendwer (Gast)


Lesenswert?

A. K. schrieb:
> Da allerdings 11 * 3600 auf deinem AVR nicht in "int" passt ist das
> Ergebnis undefiniert. Korrekt wäre beispielsweise
>   t1 = (stunde % 12) * 3600U + minute * 60 + sekunde;

"minute * 60" ist auch nicht ganz ungefährlich weil hier erstmal eine 
uint8_t mit einem int8_t multiplziert wird bevor der Ergebnis davon zu 
den anderen hinzuaddiert wird.

  t1 = (uint16_t)(stunde % 12) * 3600U + (uint16_t)minute * 60U + 
(uint16_t)sekunde;

von Rolf M. (rmagnus)


Lesenswert?

Irgendwer schrieb:
> A. K. schrieb:
>> Da allerdings 11 * 3600 auf deinem AVR nicht in "int" passt ist das
>> Ergebnis undefiniert. Korrekt wäre beispielsweise
>>   t1 = (stunde % 12) * 3600U + minute * 60 + sekunde;
>
> "minute * 60" ist auch nicht ganz ungefährlich weil hier erstmal eine
> uint8_t mit einem int8_t multiplziert wird bevor der Ergebnis davon zu
> den anderen hinzuaddiert wird.

Doch, das ist ungefährlich, denn 60 ist kein int8_t, sondern ein int. 
minute wird zunächst auf int erweitert, und dann wird die Multiplikation 
durchgeführt. Einen Überlauf kann es nicht geben. Danach wird das 
Ergebnis für die Addition nach unsigned int konvertiert, da bei einer 
Rechenoperation zwischen signed und unsigned gleicher Größe unsigned 
"gewinnt". Somit gibt's keine Probleme. Die von dir angegebenen Casts 
sind nicht nötig, schaden aber auch nicht.

von (prx) A. K. (prx)


Lesenswert?

Irgendwer schrieb:
> "minute * 60" ist auch nicht ganz ungefährlich weil hier erstmal eine
> uint8_t mit einem int8_t multiplziert

Nein. Selbst wenn 60 nicht "int" wäre, würde die Multiplikation mit 
mindestens "int" gerechnet.

von Takao K. (takao_k) Benutzerseite


Lesenswert?

A. K. schrieb:
> Irgendwer schrieb:
>> "minute * 60" ist auch nicht ganz ungefährlich weil hier erstmal eine
>> uint8_t mit einem int8_t multiplziert
>
> Nein. Selbst wenn 60 nicht "int" wäre, würde die Multiplikation mit
> mindestens "int" gerechnet.

und wo ist das definiert?

Wenn du ein unsigned char mit 2 multiplizierst und dass in Software 
machen musst (8 bit controller), dauert dass doch sehr lange.

Glaube schon dass der Cast notwending ist (unsigned int x)*60

60 wird dann aber schon richtig erweitert.

Mal austesten ob entweder (unigned int) oder 60U schon ausreichen?

Wenn du zwei 8bit Werte multiplizierst ist dass Ergebniss auch 8bit, 
zumindest auf einem 8bit controller.

von Stefan E. (sternst)


Lesenswert?

Takao K. schrieb:
> und wo ist das definiert?

Im C-Standard.

Takao K. schrieb:
> Wenn du zwei 8bit Werte multiplizierst ist dass Ergebniss auch 8bit,
> zumindest auf einem 8bit controller.

Nein. Es spielt überhaupt keine Rolle, ob es ein 8-Bit oder sonstwas-Bit 
Controller ist. Der Compiler hat sicherzustellen, dass sich der Code dem 
C-Standard entsprechend verhält.

von (prx) A. K. (prx)


Lesenswert?

Takao K. schrieb:
> und wo ist das definiert?

Im C Standard gibt es keine Rechnungen kleiner als "int". Was das 
Ergebnis angeht. Wie der Compiler dazu kommt ist seine Angelegenheit.

> Wenn du zwei 8bit Werte multiplizierst ist dass Ergebniss auch 8bit,
> zumindest auf einem 8bit controller.

Nicht in C. Und auch nicht auf dem AVR, denn das Ergebnis des 
Multiplikationsbefehls ist dort 16 Bits breit. Der kann also verwendet 
werden, und IIRC wird er das in dieser Situation mittlerweile auch.

: Bearbeitet durch User
von Steffen R. (steffen_rose)


Lesenswert?

Naja, gerade auf 8bit Prozessoren fällt es den Compilern schwer mit 
größeren Wertebereichen zu arbeiten. Mit den falschen Optionen im 
Zusammenhang mit dem Optimierer kann man schon mal den C-Standard an 
solchen Stellen (normalerweise bewußt) verletzen.

Auch für eine spätere Codedurchsicht ist die Ausnutzung impliziter Casts 
ungünstig. Warum nicht den Cast hinschreiben, wenn man bereits 
durchdacht hat, welchen Wertebereich man benötigt?

Edit:
Beispiel -mint8

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Steffen Rose schrieb:
> Auch für eine spätere Codedurchsicht ist die Ausnutzung impliziter Casts
> ungünstig. Warum nicht den Cast hinschreiben, wenn man bereits
> durchdacht hat, welchen Wertebereich man benötigt?

Kann man machen, wenn man partout dokumentieren will, dass man an dieser 
Stelle eklatante Schwächen hat. ;-)

Im Ernst: Casts sind Brechstangen. Denn sie nehmen dem Compiler etliche 
Chancen, auf Fehler hinzuweisen. Daher empfehle ich, Casts sparsam 
einzusetzen.

Zudem können explizite 16-Bit Casts auf 32-Bit Maschinen recht ungünstig 
sein. Wenn so ein Szenario nicht völlig ausgeschlossen ist, dann dann 
sollte man also mindestens etwas wie uint_fast16_t verwenden.

von Steffen R. (steffen_rose)


Lesenswert?

Ich stimme dem zu, das cast sparsam einzusetzen sind. Genauso gefährlich 
finde ich es aber auch, sich auf die Integer Promition zu verlassen.

von (prx) A. K. (prx)


Lesenswert?

Steffen Rose schrieb:
> Beispiel -mint8

Yep. Aber bei AVRs problematisch. Bei 51ern und PICs wohl öfter zu 
finden. Aber wer das macht, der sollte wissen was er tut, man sich damit 
also vom C Standard entfertn.

von Steffen R. (steffen_rose)


Lesenswert?

Wer arbeitet schon alleine an einem Projekt...

Es sind auf jedenfall auch solche Stellen, wo man später nach Problemen 
sucht. Und oft passieren Überläufe erst beim Kunden :-(

von Gustav (Gast)


Lesenswert?

Steffen Rose schrieb:
> Genauso gefährlich
> finde ich es aber auch, sich auf die Integer Promition zu verlassen.

Ungefähr so gefährlich, wie sich auf den Überlauf bei unsigned ints zu 
verlassen.

Oder sich darauf zu verlassen, daß nur entweder der if- oder der 
else-Zweig genommen werden.

von (prx) A. K. (prx)


Lesenswert?

Gustav schrieb:
> Oder sich darauf zu verlassen, daß nur entweder der if- oder der
> else-Zweig genommen werden.
Du denkst da bestimmt an so etwas: ;-)
1
if (n == 0) {
2
} else if (n != 0) {
3
}

von Steffen R. (steffen_rose)


Lesenswert?

> t1 = (stunde % 12) * 3600 + minute * 60 + sekunde;

A.K. -
Da die Konstanten nicht mit 'u' erweitert wurden, sind sie als 'signed 
int' zu interpretieren, richtig?
Die 'Integer Promotion' verlangt somit die Berechnung als 'signed', 
richtig?
Damit führt die Multiplikation 11*3600 zu einem Überlauf von 16bit 
signed int.

Oder liege ich jetzt falsch?

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Steffen Rose schrieb:
> Da die Konstanten nicht mit 'u' erweitert wurden, sind sie als 'signed
> int' zu interpretieren, richtig?

Richtig.

> Die 'Integer Promotion' verlangt somit die Berechnung als 'signed',
> richtig?

Richtig.

> Damit führt die Multiplikation 11*3600 zu einem Überlauf von 16bit
> signed int.

Richtig. Hatte ich oben ja schon erwähnt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.