Hallo Leute,
Ist das Rechtsschieben von signed eigentlich im Standard definiert?
Kann ich davon aus gehen, dass folgendes definiert ist:
???
Uint16 foo = 0x8000;
Foo>>=2; // 0x2000
Int16 foo = 0x8000;
Foo >>= 2; // 0xe000
Fur welche Operanden ist eigetlich der Shiftoperator definiert?
Ich möchte einen linksbündigen 14bit A/D Wert normieren.
Danke, Adib.
--
Adib schrieb:> Hallo Leute,>> Ist das Rechtsschieben von signed eigentlich im Standard definiert?
In welchem?
> Kann ich davon aus gehen, dass folgendes definiert ist:> ???> Uint16 foo = 0x8000;> Foo>>=2; // 0x2000>> Int16 foo = 0x8000;> Foo >>= 2; // 0xe000
Falls es um C gehen sollte, nein.
> Fur welche Operanden ist eigetlich der Shiftoperator definiert?
Beide Operanden dürfen nicht negativ sein, und bei einem
vorzeichenbehafteten Typ muss das Ergebnis (linker Wert mal zwei hoch
rechter Wert) darstellbar sein.
> Ich möchte einen linksbündigen 14bit A/D Wert normieren.
Und der ist vorzeichenbehaftet?
Ok, hier muss ich nochmal korrigieren. War irgendwie vom Linksschieben
ausgegangen. Beim Rechtsschieben von negativen Werten ist das Ergebnis
implementationsabhängig.
Rolf Magnus schrieb:
> Beim Rechtsschieben von negativen Werten ist das Ergebnis> implementationsabhängig.
Und: Wenn beim Linksschieben einer vorzeichenbehafteten Ganzzahl das
Vorzeichen betroffen ist, dann ist das Ergebnis undefiniert.
https://msdn.microsoft.com/de-de/library/336xbhcz.aspx
Aber wo ist nun die Standart-Definition zu lesen, ohne sich die Bücher
kaufen zu müssen?
Also laut pdf C99: 6.5 Expressions, Absatz 4
Some operators (the unary operator ~, and the binary operators <<, >>,
&, ^, and |,
collectively described as bitwise operators) are required to have
operands that have
integer type. These operators yield values that depend on the internal
representations of
integers, and have implementation-defined and undefined aspects for
signed types.
undefined behavour :-(
ok. Danke das wars, was ich wissen wollte. In meinem Code schiebe ich
jetzt nicht mehr die Daten nach rechts um 2, sondern dividiere durch 4.
Der Compiler macht dann ein "Arithmetic right shift".
Grüße, Adib.
--
Da gibt's noch eine etwas konkretere Passage in 6.5.7:
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
If E1 has a signed type and a negative value, the resulting value is
implementation-defined.
Shift ist ein Bitoperator. Dabei spielt die Interpretation keine Rolle.
Egal ob int float char oder sonst was.
Du möchtest aber dividieren. Also schreib das einfach hin und lass den
Compiler das für dein Target optimal umsetzen.
compiler schrieb:> Shift ist ein Bitoperator. Dabei spielt die Interpretation keine Rolle.> Egal ob int float char oder sonst was.
In Assembler vielleicht, aber nicht in C. Für float ist eine
Schiebeoperation nicht mal definiert.
> Du möchtest aber dividieren.
Eigentlich nicht. Das ADC-Ergebnis ist links ausgerichtet, und Adib
möchte es so verschieben, dass es rechts ausgerichtet ist.
Das Problem besteht darin, dass der ADC-Wert vorzeichenlos ist, aber
offenbar fälschlicherweise in einer vorzeichenbehafteten Variable
gespeichert ist.
Im übrigen kann ich mir auch nicht vorstellen, dass eine Division durch
4 hier korrekt ist.
Im obigen Beispiel
Adib schrieb:> Int16 foo = 0x8000;
hat foo hier den Wert -32768. Nach der Division ist er -8192, was dann
0xE000 entspricht. Eigentlich sollte aber 0x2000 rauskommen.
Es gibt AD-Wandler, die ihre Ergebnisse im Zweierkomplement ausgeben.
Wenn man nicht sicher ist, ob der Compiler vorzeichenrichtig rechts
schieben kann bzw. aus einer /4 Division ein schnelles Shift macht, dann
halt so.
Hallo Falk, & andere
der Sensor liefert einen 14bit vorzeichenbehafteten Wert. BMA280 Bosch
Bechleunigungssensor.
Der ist linksbündig. Die 2 bit LSB sind einfach Flags, die man
irgnorieren kann.
bei einem Sensorresultat von 0x8000 lautet das normalisierte Ergebnis
0xe000
Ich hätte das Vorzeichen testen müssen und entsprechend 0xc000 bitwise
or machen müssen.
C hat leider kein Arithmetic shift oder irgendsoeinanders shift vom
Prozessor.
oben steht bei signed typ und negativen Wert ist das Verhalten
implementation specific.
Ich hatte schon mal beobachtet, das das mit dem Schieben sogar
vorzeichenkorrekt funktioniert. Wusste aber nicht, ob ich mich darauf
verlassen kann, oder ob bei der nächsten Compilerversion oder bei einem
anderen Compiler das genau anders ist.
Bei / 4 ist die Sache eigentlich klar. Wenn ich den Code auf den AVR
übertrage muss ich dann halt schauen, ob er auch shiftet oder eine div
Subroutine aufruft.
GRüße, Adib.
--
Adib schrieb:> der Sensor liefert einen 14bit vorzeichenbehafteten Wert. BMA280 Bosch> Bechleunigungssensor.
Ah, ok. Dann ist das natürlich was anderes.
> C hat leider kein Arithmetic shift oder irgendsoeinanders shift vom> Prozessor.
Kann sein, dass der Compiler den nutzt. Es ist ja immerhin
implementation-defined, muss also vom Compiler festgelegt und in dessen
Handbuch dokumentiert sein, wie er es macht. Wenn's unabhängig vom
Prozessor und Compiler sein soll, kannst du dich natürlich nicht drauf
verlassen.
> Ich hatte schon mal beobachtet, das das mit dem Schieben sogar> vorzeichenkorrekt funktioniert.
Ich würde vermuten, dass das sogar eher die Regel als die Ausnahme ist.
> Bei / 4 ist die Sache eigentlich klar.
Ja. Das muss der Compiler vorzeichenrichtig machen.
> Wenn ich den Code auf den AVR übertrage muss ich dann halt schauen, ob er> auch shiftet oder eine div Subroutine aufruft.
Ein halbwegs optimierender Compiler sollte das hinbekommen.
Adib schrieb:> sondern dividiere durch 4.> Der Compiler macht dann ein "Arithmetic right shift".
Dann macht der Compiler es falsch.
-1 / 2 = 0
-1 >> 1 = -1
Julius Z. schrieb:> Warnt GCC beim Versuch, eine signed bzw. float Variable zu shiften? Wenn> nein, wie lässt sich diese Warnung einschalten?
Beim Versuch, eine float-Variable zu shiften, gibt es einen Fehler. Bei
vorzeichenbehafteten Integern wüßte ich nicht, dass man eine Warnung
einstellen könnte. Wäre vermutlich auch recht sinnlos, da vor jeder
Operation alle Operanden, die kleiner als int sind, erstmal auf int
erweitert werden und damit ein Vorzeichen bekommen.
Adib schrieb:> Some operators (the unary operator ~, and the binary operators <<, >>,> &, ^, and |,> collectively described as bitwise operators) are required to have> operands that have> integer type. These operators yield values that depend on the internal> representations of> integers, and have implementation-defined and undefined aspects for> signed types.
Wenn GCC z.B. ein uint8_t, das man shiften möchte, zu int promoted, wird
dann damit nicht undefiniertes Verhalten absichtlich provoziert?
Klar, wenn vorher kein Vorzeichen drin war und die Bitbreite
anschließend höher ist, kann theoretisch nichts passieren... aber laut
Dokumentation ist es undefined...
Julius Z. schrieb:> aber laut> Dokumentation ist es undefined...
Nein, es ist implementation defined. Das ist ein Unterschied. Und das
auch nur für negative Zahlen.
Wenn ein uint8_t oder uint16_t auf int promoted wird, ist es immer ein
positives int. Also völlig ok.
Habe es für den avr-gcc mal getestet:
Dort funktioniert es so, wie man es sich mathematisch korrekt vorstellt.
Schiebt man z.B. -1 nach links, ergibt das -2. Schiebt man -2 nach
rechts, ergibt das -1. Das Vorzeichen-Bit wird also nicht
unkontrollierbar in den Zahlenwert hineingemurkst, sondern behandelt.
Das gilt, wie gesagt, für den avr-gcc. Woanders sieht es sicher anders
aus.