Hallo zusammen, Ich habe eine Frage zum Shiften. Nehmen wir mal an ich habe die eine int8_t variable "i". Wenn "i" den Wert 2 enthält und ich shifte um 1 nach rechts, dann hat "i" den Wert 1. Soweit so gut, wenn ich nun den Wert "-2" in "i" stehen habe und ebenfalls um eins nach rechts shifte, was steht dann in der Variablen "i"? Meine überlegung ist folgende. i = -2 = 1111 1110 i >>= 1 => 0111 1111 = 127
kommt wohl auf deinen Compiler an (siehe link). https://stackoverflow.com/questions/4009885/arithmetic-bit-shift-on-a-signed-integer
Edwin schrieb: > was steht dann in der Variablen "i"? Das ist implementationsabhängig, soweit es um C geht.
Wie ist das gemeint, es ist Implementierungsabhängig? Wie kann ich das beeinflussen?
Edwin schrieb: > Wie ist das gemeint, es ist Implementierungsabhängig? Es haengt davon ab, wie es im Compiler implementiert ist. d.h.: Mit Compiler A bekommst du Ergebnis X, und mit Compiler B kann das Ergebnis ebenfalls X sein, es kann aber auch Y sein. Edwin schrieb: > Wie kann ich das > beeinflussen? Gar nicht.
Edwin schrieb: > Wie ist das gemeint, es ist Implementierungsabhängig? Der C- bzw. C++-Standard definiert hier kein Verhalten sondern überlässt es dem Compiler. Das heißt, je nach Compiler -Hersteller, -Version, -Optionen und Plattform kann sich das Verhalten ändern. Daher lässt man solche Dinge am Besten ganz bleiben. Was möchtest du denn erreichen? Wahrscheinlich gibt es eine andere, wohldefinierte Möglichkeit ans Ziel zu kommen.
Edwin schrieb: > Wie ist das gemeint, es ist Implementierungsabhängig? ISO 9899:1999 6.5.7 Bit-wise shift operators §3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
Plauzi schrieb: > If the value of the > right operand is negative or is greater than or equal to the width of > the promoted left operand, the behavior is undefined. Das ist wohl nicht ganz die richtige Stelle... Das wäre wohl die hier, insb. der letzte Satz: "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 / 2^E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined."
Plauzi schrieb: > ISO 9899:1999 6.5.7 Bit-wise shift operators §3 Falsche Stelle. Das Verhalten bei unpassender Anzahl Bits ist undefiniert, nicht implementierungsabhängig. Die richtige Stelle: 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 / 2 E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined. ALlerdings ist eine Implementierung, die bei Typen mit Vorzeichen links Nullen reinschiebt, sehr exotisch.
:
Bearbeitet durch User
Wenn du definiertes Verhalten möchtest, mach die ein oder zwei Zeilen für die Shiftoperation in Inline-Assembler. Da kannst du dir aussuchen ob du ein arithmetisches shift, ein logisches shift oder ein rotate nimmst. Vergiss nicht einen Kommentar in den Quelltext zu schreiben, sonst wunderst du dich in einem halben Jahr, was du da gemacht hast.
1234567890 schrieb: > Wenn du definiertes Verhalten möchtest, mach die ein oder zwei Zeilen > für die Shiftoperation in Inline-Assembler. Ist dann leider nur auf genau der einen Plattform definiert. Die Inline-Assembler-Anweisung so korrekt zu benutzen dass sie auch bei aktivierter Compiler-Optimierung funktioniert ist auch gar nicht so einfach. Für so etwas Primitives wie eine Shift-Operation auf Inline-Assembler zurückzugreifen ist schon sehr schlechter Stil. Da sollte man sich lieber Gedanken machen wie man das korrekt in C macht, z.B. indem man eine positive Zahl shiftet und dann nach signed castet.
Dr. Sommer schrieb: > Ist dann leider nur auf genau der einen Plattform definiert. Ja, das ist ein ganz großer Nachteil. Dr. Sommer schrieb: > Die > Inline-Assembler-Anweisung so korrekt zu benutzen dass sie auch bei > aktivierter Compiler-Optimierung funktioniert ist auch gar nicht so > einfach. Naja, so schwierig ist das nicht, wenn man weiß was der Compiler macht. Kenne deine Werkzeuge :-) Dr. Sommer schrieb: > Für so etwas Primitives wie eine Shift-Operation auf > Inline-Assembler zurückzugreifen ist schon sehr schlechter Stil. Das kommt drauf an. Es gibt einige Situationen, da lohnt sich sowas richtig. Es kommt halt auf den Kontext an. Dr. Sommer schrieb: > Da > sollte man sich lieber Gedanken machen wie man das korrekt in C macht, > z.B. indem man eine positive Zahl shiftet und dann nach signed castet. Ja, sollte man sogar, wenn es portierbar sein soll. Allerdings achtet man sehr oft darauf, dass der Code portierbar ist und bleibt. 90% der geschriebenen Codes für kleine Mikrocontroller werden aber selten bis nie in eine andere Familie portiert und innerhalb der Familie funzt es mit Inline-Assembler sehr gut.
1234567890 schrieb: > Naja, so schwierig ist das nicht, wenn man weiß was der Compiler macht. Es fängt schon damit an dass z.B. beim GCC für Cortex-M die Modifier für die Register nicht dokumentiert sind und aus dem Internet zusammengesucht werden müssen. Dann muss man genau die richtigen für die jeweiligen Instruktionen nehmen, dafür sorgen dass sich Input/Output-Register nicht überlappen falls beide gleichzeitig genutzt werden usw. Was heute ohne Optimierungen funktioniert, klappt morgen mit Optimierungen vielleicht unbemerkt nicht mehr weil der Compiler die Register-Zuordnung geändert hat. 1234567890 schrieb: > Das kommt drauf an. Es gibt einige Situationen, da lohnt sich sowas > richtig. Es kommt halt auf den Kontext an. Ja. Man sollte aber vorher genau wissen ob der Shift wirklich der Flaschenhals ist. Und vieles können Compiler auch optimieren, obwohl es in C kompliziert aussieht. 1234567890 schrieb: > 90% der geschriebenen Codes für kleine Mikrocontroller werden > aber selten bis nie in eine andere Familie portiert und innerhalb der > Familie funzt es mit Inline-Assembler sehr gut. Allein schon das Wechseln des Compilers ist problematisch, weil die Syntax für Inline-Assembly unterschiedlich ist. Das ist für mich ein großes No-Go.
Dr. Sommer schrieb: > Was heute ohne Optimierungen funktioniert, klappt morgen mit > Optimierungen vielleicht unbemerkt nicht mehr weil der Compiler die > Register-Zuordnung geändert hat. Dr. Sommer schrieb: > Allein schon das Wechseln des Compilers ist problematisch, weil die > Syntax für Inline-Assembly unterschiedlich ist. Innerhalb eines Produktzyklusses wechseln wir weder die Compiler noch updaten wir sie. Die Toolchain bleibt bei einem Produkt von Anfang an für das gesamte Team die selbe. Wir leben mit den Bugs. Sie stören uns nicht, da wir sie kennen und umgehen. Nach einem Update oder Wechsel fängt der ganze Lernzyklus von vorne an und solange der Lernzyklus anhält kommen meist nur böse Überraschungen. Update oder Wechsel kommt erst beim nächsten Produkt. Bei sicherheitskritischen Anwendungen (Gefahr für Leib und Leben) oder sehr energiesparenden Anwendungen mögen wir es nicht, wenn der Kunde mit einem nicht funktioniernenden Produkt in 5 Jahren auf der Matte steht. Wir haben hier ein paar Produkte, die schon seit einigen Jahren laufen und immernoch weiterentwickelt werden: Und das mit einer hornalten Toolchain. Am Anfang fand ich das alles lächerlich, aber mittlerweile haben wir durch solche Updates auch schon einige Male ordentlich Lehrgeld bezahlt. Dr. Sommer schrieb: > Man sollte aber vorher genau wissen ob der Shift wirklich der > Flaschenhals ist. Hab ja gesagt, dass es auf die Situation ankommt.
1234567890 schrieb: > Innerhalb eines Produktzyklusses wechseln wir weder die Compiler noch > updaten wir sie. Das kann man aber nur innerhalb eines Teams so definieren... Wen man Code weitergibt/verkauft, freut sich der Kunde nicht sehr wenn er nur genau einen Compiler nutzen kann. 1234567890 schrieb: > aber mittlerweile > haben wir durch solche Updates auch schon einige Male ordentlich > Lehrgeld bezahlt. Na, dann wird's Zeit für Standard-Konformen Code, der geht nicht kaputt... 1234567890 schrieb: > Bei sicherheitskritischen Anwendungen 1234567890 schrieb: > Wir leben mit den Bugs. Sie stören uns > nicht, da wir sie kennen und umgehen. Bei sicherheitskritischen Anwendungen akzeptiert ihr Bugs? Das finde ich irgendwie gewagt.
GCC:
1 | • Whether signed integer types are represented using sign and |
2 | magnitude, two's complement, or one's complement, and whether the |
3 | extraordinary value is a trap representation or an ordinary value |
4 | (C99 and C11 6.2.6.2). |
5 | |
6 | GCC supports only two's complement integer types, and all bit patterns |
7 | are ordinary values. |
1 | • The results of some bitwise operations on signed integers |
2 | (C90 6.3, C99 and C11 6.5). |
3 | |
4 | Bitwise operators act on the representation of the value including |
5 | both the sign and value bits, where the sign bit is considered |
6 | immediately above the highest-value value bit. Signed ‘>>’ acts on |
7 | negative numbers by sign extension. |
Intern verwendet GCC "Arithmetic Shift Right", d.h. auf Backend-Ebene die ashr<mode>3 Pattern. http://gcc.gnu.org/onlinedocs/gccint/Standard-Names.html#index-ashlm3-instruction-pattern Das Verhalten ist also zumindest innerhalb von GCC portierbar. Beispielsweise erzeugt avr-gcc folgenden Code:
1 | #include <stdint.h> |
2 | |
3 | int8_t ashr1 (int8_t x) |
4 | {
|
5 | return x >> 1; |
6 | }
|
7 | |
8 | int8_t ashr7 (int8_t x) |
9 | {
|
10 | return x >> 7; |
11 | }
|
12 | |
13 | int8_t ashr_m128 (void) |
14 | {
|
15 | return -128 >> 4; |
16 | }
|
1 | ashr1: |
2 | asr r24 |
3 | ret |
4 | |
5 | ashr7: |
6 | lsl r24 |
7 | sbc r24,r24 |
8 | ret |
9 | |
10 | ashr_m128: |
11 | ldi r24,lo8(-8) |
12 | ret |
Also kein Anlass, die Inline-Assembler Keule auszupacken, wohl auch nicht bei anderen ernstzunehmenden Compilern.
Johann L. schrieb: > Also kein Anlass, die Inline-Assembler Keule auszupacken,.. http://www.c-jump.com/CIS77/ASM/Flags/F77_0160_sar_instruction.htm mir geht es nur darum zu sagen, das Assembler eher mit Freiheit zu tun hat. Wäre schön, wenn so ein C-Compiler auch so etwas wie CPUID ( https://de.wikipedia.org/wiki/CPUID ) hätte bzw. auf einer schnell einzusehenden Karte angeben könnte, mit welchen Tatsachen bei bestimmten Fragen zu rechnen ist. Die Autoren von C selbst sagen auch nur, dass bei denen der Shift nach rechts bei negativen Werten einer Division durch 2 entsprechen soll. (oder eben -> Implementierungsfeinheit im Einzelfall) Die (Intel-)Assemblerbefehle sagen: Du kannst beides machen, sowohl Division (im mathematischen (?) Sinne als auch elegant rückwärts gehen, wie auf z.B. einem Bankkonto.
Die praktische Antwort auf diese theoretisch sicher interessante Frage lautet: egal. Vorzeichenbehaftete Integer shiftet man einfach nicht. Oliver
rbx schrieb: > Wäre schön, wenn so ein C-Compiler auch so etwas wie CPUID > ( https://de.wikipedia.org/wiki/CPUID ) > hätte Tja, die Idee der meisten "höheren" Programmiersprachen ist, dass man das gewünschte Verhalten auf einer abstrakten Ebene beschreibt, und der Compiler dafür sorgt dass dieses auf der jeweiligen Plattform erreicht wird. Es ist nicht so gedacht dass man explizit abfragt wie die Plattform sich verhält, und sich das Programm daran anpasst. Das ist durchaus unintuitiv und oft ärgert man sich, dass es nicht so geht wie gewünscht, obwohl man genau weiß dass die konkrete Plattform das gewünschte kann - aber anscheinend hat sich dieses Modell durchgesetzt. In C und C++ hat man leider viele Elemente ohne definiertes Verhalten, sodass man hier leicht Fehler machen kann, wie eben Shifts auf negativen Zahlen. Ein klassisches Beispiel für das Modell besteht in der Endian-Konvertierung. Die direkte Herangehensweise ist es, die Byte-Reihenfolge der Plattform abzufragen, ggf. die Bytes zu tauschen und dann einen Integer daraus zu machen (wir gehen mal von char=8Bit, short=16Bit aus):
1 | unsigned char bytes [2]; |
2 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
3 | bytes[0] = highByte; |
4 | bytes[1] = lowByte; |
5 | #endif
|
6 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
7 | bytes[1] = highByte; |
8 | bytes[0] = lowByte; |
9 | #endif
|
10 | unsigned short res; |
11 | memcpy (res, bytes, 2); |
Ist zwar kein CPUID aber eine andere Art des direkten Abfragens der Eigenschaften der Zielplattform. Das _BYTE_ORDER_ -Makros ist aber GCC-spezifisch - in Standard-C lässt sich so etwas gar nicht portabel schreiben. Lustig wirds auch, wenn noch andere Reihenfolgen wie PDP-Endian hinzukommen. Die Lösung auf Basis des abstrakten Modells lautet:
1 | uint16_t res = (((uint16_t) highByte) << 8) | lowByte; |
Diese erscheint umständlich und es ist nicht so direkt klar was ein Bitshift mit dem Tauschen von Bytes zu tun hat, und letztendlich passiert in der Hardware genau das gleiche wie bei der ersten Variante. Die 2. Variante ist aber die portable und zu bevorzugende - man passt nicht das Programm an die Plattform an, sondern beschreibt abstrakt und lässt den Compiler anpassen. Das ist einer der Haupt-Unterschiede zwischen maschinennaher Programmierung (zB Assembler) und in Hochsprachen (zB C). Insbesondere bei Embedded-Architekturen klappt das natürlich nie vollständig, und man muss oft auf plattformspezifische Dinge zurückgreifen. Aber es erklärt, warum es in C kein CPUID gibt!
Dr. Sommer schrieb: > Na, dann wird's Zeit für Standard-Konformen Code, der geht nicht > kaputt... Dein ganzer Beitrag suggeriert, dass du eher weniger mit Microcontroller zu tun hast. Das findet sich aber in der Webadresse.
Achim S. schrieb: > Dein ganzer Beitrag suggeriert, dass du eher weniger mit Microcontroller > zu tun hast. Doch, hab ich. Man kann auch für Controller guten Code schreiben. Nur weil keiner die Innereien sieht ist das kein Argument das irgendwie zu machen. Achim S. schrieb: > Das findet sich aber in der Webadresse. Welche jetzt?
A. K. schrieb: > Falsche Stelle. Das Verhalten bei unpassender Anzahl Bits ist > undefiniert, nicht implementierungsabhängig. Natürlich ist das Verhalten implementierungsabhängig. Oder meinst du, das bei Verwendung eines bestimmten Compilers beim Programmlauf mit gleichen Eingangsbedingungen mal dies und mal das rauskommt? Da die Bits einer Variablen nur zwei Zustände annehmen können, ist das Ergebnis auch irgendwie definiert.
u/i schrieb: > Da die Bits einer Variablen nur zwei Zustände annehmen können, ist das > Ergebnis auch irgendwie definiert. Du kennst den Unterschied zwischen "undefined" und "implementation defined" nicht: * Implementation defined heißt, dass das Rechenergebnis vom Standard nicht vorgegeben ist, das Programm aber sonst normal weiterläuft * Undefined Behaviour heißt, es darf irgendwas passieren, z.B. Programmabsturz, Formatieren der Festplatte, Funktioniert wie erwartet, Zeile wird übersprungen weil der Compiler annehmen darf, dass undefiniertes Varhalten nie auftritt, ... Das ist logischerweise von der Plattform, Compiler, genauen Umständen usw abhängig. Shiften negativer Zahlen ist implementation defined, Shiften mit zu viel Bits ist Undefined Behaviour. Zu letzterem gehören z.B. auch Dinge wie Dereferenzieren eines Null-Pointers oder mancher(!) umgecasteter Pointer, Signed Integer Overflow usw.
Hier noch die Quelle: Im C-Standard, 3.4.1: 1 implementation-defined behavior unspecified behavior where each implementation documents how the choice is made 2 EXAMPLE An example of implementation-defined behavior is the propagation of the high-order bit when a signed integer is shifted right. 3.4.3 1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements 2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). 3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.
Beitrag #5302663 wurde von einem Moderator gelöscht.
@ Edwin (Gast)
>Ich habe eine Frage zum Shiften.
Shiften? Ist das sowas wie voten und liken?
AUA!!!!
Heiner schrieb im Beitrag #5302663: > Absoluter Unsinn. Kauf doch mal eine aktuelle CPU ohne Bugs! > -> gibt es nicht. Ob man gezwungenermaßen Produkte mit Bugs kauft, oder man bekannte behebbare Bugs im eigenen Code lässt, ist etwas völlig anderes.
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.