Forum: Compiler & IDEs Signed korrektes Rechtsschieben


von Adib (Gast)


Lesenswert?

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.
--

von Rolf Magnus (Gast)


Lesenswert?

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?

von Rolf Magnus (Gast)


Lesenswert?

Ok, hier muss ich nochmal korrigieren. War irgendwie vom Linksschieben 
ausgegangen. Beim Rechtsschieben von negativen Werten ist das Ergebnis 
implementationsabhängig.

von Rainer V. (rudi994)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?


von Adib (Gast)


Lesenswert?

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.
--

von Rolf Magnus (Gast)


Lesenswert?

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.

von compiler (Gast)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

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.

von AntiHeiner (Gast)


Lesenswert?

Rainer V. schrieb:

> Aber wo ist nun die Standart-Definition zu lesen
Hier: http://www.k-faktor.com/standart/

von Rolf Magnus (Gast)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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.
1
int x = ADC_Wert;
2
3
x >>= 2;
4
if (x & 0x2000) x |= 0xC000;

von Adib (Gast)


Lesenswert?

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.
--

von Rolf Magnus (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Julius Z. (Gast)


Lesenswert?

Warnt GCC beim Versuch, eine signed bzw. float Variable zu shiften? Wenn 
nein, wie lässt sich diese Warnung einschalten?

von Rolf Magnus (Gast)


Lesenswert?

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.

von Julius Z. (Gast)


Lesenswert?

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...

von (prx) A. K. (prx)


Lesenswert?

Für positive Werte ist es klar definiert.
Beitrag "Re: Signed korrektes Rechtsschieben"

: Bearbeitet durch User
von schiebedediebe (Gast)


Lesenswert?

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.

von Julius Z. (Gast)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.