Verständnisfrage zu shift Operatoren in vorzeichenbehafteten Rechnungen in c (Festkommarechnung)

OP #1704704
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
#1704714
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.
Moderator #1704720
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.
OP #1704732
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
OP #1704733
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)...
#1704734
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.
#1704744
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.
OP #1704747
:-) 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
Persönliche Seite #1704800
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

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 16 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.