C und Wertebereiche

Gast #3775995
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.
#3776000
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;
Gast #3776444
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;
#3776484
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.
Persönliche Seite #3776528
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.
#3776545
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.
#3776556
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
#3776566
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.
#3776644
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?

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