Hallo, ich habe folgende Beispielformel zusammengefrickelt um meine Frage zu verdeutlichen... int8_t Beispielrechnung (....) { //Formel mul*0.3-50+r1 uint8_t mul=133; //0-255 uint16_t r1=30; //0-99 uint16_t c1=(uint16_t)(0.3*(1<<9)); return (int8_t)(((((int16_t)(c1*(uint16_t)mul))-(50*1<<9))>>9)+(int16_t)r1); } Diese Geschichte rechnet richtig. Bitte vernachlässigt den Sinn und teilweise auch die Wahl der Zahlen. Ich möchte folgende Frage hervorheben: Mit dem ersten Typecast (int16_t) an der Multiplikation möchte ich sicherstellen das die weitere Rechnung (Subtraktion) auf basis int16_t stattfindet, das geht auch. Nun die Frage: An der Stelle >>9 schifte ich eine vorzeichenbehaftete int16_t um 9 Bit nach rechts, warum geht das mit dem Vorzeichen nicht in die Grütze? Müsste ich nicht viel eher schreiben /(int16_t)(1<<9) ? Gruß, Thorsten
Thorsten S. schrieb: > An der Stelle >>9 schifte ich eine vorzeichenbehaftete int16_t um 9 Bit > nach rechts, warum geht das mit dem Vorzeichen nicht in die Grütze? Ohne deinen Klammernsalat aufgedröselt zu haben: >> bewahrt das Vorzeichen, mindestens üblicherweise. Es muss wohl vor Jahrzehnten Maschinen gegeben haben, die damit Probleme hatten, daher ist (war?) C laxer definiert, aber das ist Vergangenheit. Wobei >> und / nur dann äquivalent sind, wenn kein negatives Vorzeichen mitmischt. Bei negativem Zähler kommen verschiedene Ergebnisse raus.
Bei vorzeichenbehafteten Zahlen wird beim Rechtshiften von links mit dem bisherigen Vorzeichen aufgefüllt, also mit 1, wenn die Zahl negativ ist. Negativ bleibt dabei also negativ.
A. K. schrieb: > Wobei >> und / nur dann äquivalent sind, wenn kein negatives Vorzeichen > mitmischt. Bei negativem Zähler kommen verschiedene Ergebnisse raus. Ja, aber nur in der niedrigstwertigen Stelle (Rundung nach unten bzw. zu 0 hin)
A. K. schrieb: > Es muss wohl vor Jahrzehnten Maschinen gegeben haben, die damit > Probleme hatten, daher ist (war?) C laxer definiert, aber das ist > Vergangenheit. Es ist an der Stelle immer noch "lax" definiert: "The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined." (Quelle: ISO/IEC 9899:TC3) Hat der Prozessor, wie fast jeder, einen ASR-Befehl (arithmetic shift right), werden beim Rechtsshift von vorzeichenbehafteten Zahlen die frei werdenden Bits üblicherweise mit dem höchstwertigen Bit von E1 aufgefüllt, das Vorzeichen bleibt dadurch erhalten.
Hallo, vielen Dank für die schnelle Antwort. .. so das klingt logisch, 1)also kann ich es bedenkenlos so lassen? Mir ist beim Rumpobieren mit dem Atmega168 noch aufgefallen das mir die Formel mit >>9, 19 als Ergebnis und die Variante /(int16_t)(1<<9), 20 als Ergebnis liefert. Ergebnis ist eigentlich 0.3*133-50+30=19,9 2)Habe ich in der Shift Geschichte noch einen Denkfehler, oder wie kann ich mir das erklären? Gruß, Thorsten
Wobei >> und / nur dann äquivalent sind, wenn kein negatives Vorzeichen mitmischt. Bei negativem Zähler kommen verschiedene Ergebnisse raus. ist das nicht genau das was hier eben doch funktioniert? oder verstehe ich das falsch? Gruß, Thorsten @AK, ich habe mich gestern dran gestetzt, die Formel ist lauffähig, und liefert für den SHT bereits passende Werte (ca. 30 verglichen mit float Rechnung)...
Thorsten S. schrieb: > Mir ist beim Rumpobieren mit dem Atmega168 noch aufgefallen das mir die > Formel mit >>9, 19 als Ergebnis und die Variante /(int16_t)(1<<9), 20 > als Ergebnis liefert. / rundet gegen 0 >> rundet gegen -unendlich Der Compiler wird ggf. dennoch den Shift an Stelle einer Division verwenden, aber bei negativem Wert vorher Nenner-1 addieren.
Klaus Wachtler schrieb: > Ja, aber nur in der niedrigstwertigen Stelle (Rundung nach unten bzw. zu > 0 hin) Auf die es in dem hier betrachteten Fall nicht ankommt, wenn Thorsten in den Hunderstel% Feuchte weiterrechnet, die aus der vorigen Formel rauskommen.
...kann man so aus dem Ärmel sagen, welche Lösung den genaueren Wert liefert oder hängt es von den Multiplikatoren ab. Gruß, Thorsten
Thorsten S. schrieb: > ...kann man so aus dem Ärmel sagen, welche Lösung den genaueren Wert > liefert oder hängt es von den Multiplikatoren ab. Im Mittel sind beide Ergebniss gleich ungenau. Echte Rundung ist genauer. Aber wie eben schon geschrieben: Wenn das weiterhin Hunderstel %RH sind, dann interessiert dieser Unterschied nur pathologische Pedanten.
:-) das ist richtig, es sind in diesem fall Hunderstel, aber mir ging es bei der "Kleinigkeit" nun nur noch ums Verständnis. Will ja nicht dumm sterben. Der Kernpunkt war Punkt 1, und das ist nun klar. Vielen Dank für die ausführliche Hilfe. Gruß, Thorsten
Thorsten S. schrieb: > An der Stelle >>9 schifte ich eine vorzeichenbehaftete int16_t um 9 Bit > nach rechts, warum geht das mit dem Vorzeichen nicht in die Grütze? > > Müsste ich nicht viel eher schreiben /(int16_t)(1<<9) ? Eigentlich ganz einfach: Wenn du schieben willst, dann schiebe. Wenn du dividieren willst, dann dividiere. Offenbar willst du hier eine Division ausführen, also...? Je nach Compiler und Hardware wird er eine signed Division y = x/(1<<n) so umsetzen:
1 | x1 = x; |
2 | if (x < 0) |
3 | x1 += (1<<n)-1 |
4 | y = x1 >> n // signed |
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.