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.
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;
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;
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.
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.
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.
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.
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.
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
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.
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.
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 :-(
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.
> 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?
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.