Bitoperationen an float
-
Bashar schrieb:
Um die folgende Diskussion mal etwas abzukürzen: Was Links- und Rechtsshiften bedeutet, hat absolut nichts nichts mit der Endianness zu tun. Die 1 ist im Binärsystem mit 32 Stellen 00000000000000000000000000000001, um 1 nach links geshiftet ist das 00000000000000000000000000000010, also 2. Immer.
er geht irrtümlicherweise davon aus, dass eine 1 auf einem little-endian system als 10000000000000000000000000000000 gespeichert wird.
-
nwp2 schrieb:
Schreibe ich so kryptisch? http://home.fhtw-berlin.de/~junghans/cref/CONCEPT/bit_shift.html#shift
sorry, da es keine Leerzeichen gab, hat mich das ein bisschen verwirrt (was äquivalent zu
i = i << 1
.Ich habe auch einen little endian intel pc:
#include <stdio.h> #include <stdint.h> /* shift => 0 ==> rechtsshift */ void print_shift(int x, int shift) { int i; uint8_t *p = (void*) &x; printf(" x = 0x%x\n", x); if(shift < 0) x <<= -shift; else x >>= shift; printf("sx = 0x%x (%d)\n", x, x); for(i = 0; i < sizeof x; ++i) printf("p[%d] = 0x%x\n", i, p[i]); printf("\n\n"); } int main(void) { print_shift(0xdeadbeef, 0); print_shift(1, -1); print_shift(-2, -1); return 0; }
Ausgabe:
x = 0xdeadbeef sx = 0xdeadbeef (-559038737) p[0] = 0xef p[1] = 0xbe p[2] = 0xad p[3] = 0xde x = 0x1 sx = 0x2 (2) p[0] = 0x2 p[1] = 0x0 p[2] = 0x0 p[3] = 0x0 x = 0xfffffffe sx = 0xfffffffc (-4) p[0] = 0xfc p[1] = 0xff p[2] = 0xff p[3] = 0xff
Da ist alles in Ordnung. Die interne Bitanordnung des Register spielt bei Shiften keine Rolle.
-
;fricky schrieb:
er geht irrtümlicherweise davon aus, dass eine 1 auf einem little-endian system als 10000000000000000000000000000000 gespeichert wird.
Stimmt, ich habe Bits und Bytes durcheinander gebracht. Das Problem bleibt aber bestehen.
supertux schrieb:
Da ist alles in Ordnung. Die interne Bitanordnung des Register spielt bei Shiften keine Rolle.
Das spielt in deinem Beispiel keine Rolle, weil du alle Daten als Zahlen interpretierst. Wenn du aber Binärdaten und Zahlen durcheinanderwürfelst klappt das nicht mehr. Ich versuche mal ein Beispiel zu bauen wo das nicht mehr klappt.
-
--
-
nwp2 schrieb:
Bashar schrieb:
Die 1 ist im Binärsystem mit 32 Stellen 00000000000000000000000000000001, um 1 nach links geshiftet ist das 00000000000000000000000000000010, also 2. Immer.
Korrekt, das habe ich auch geschrieben. Das ist aber nicht das was der Threadersteller wollte. Wenn man 10000000000000000000000000000000 binär hat und einen Linksshift um ein Bit macht und es kommt 01000000000000000000000000000000 raus, dann wundert man sich schon etwas, oder?
Da würde ich mich in der Tat wundern. Es sollte 0 rauskommen. Ist das auf deinem System nicht so?
Es sollen doch keine Zahlen geshiftet werden sondern Binärdaten, und wenn diese Binärdaten als Zahlen interpretiert werden kommt halt teilweise Quatsch raus und darauf muss man aufpassen.
Von "Shiften" war eigentlich nicht die Rede, aber egal. Das Problem hat man, wenn es für den float-Typ keinen passenden int-Typ gibt (z.b. mit double). Dann muss man in der Tat aufpassen, dass man die richtige Hälfte erwischt. Sobald man aber einmal einen int mit den Bits 0 bis 31 oder 32 bis 63 hat, kann man darin wie erwartet operieren, ohne auf die Endianness achten zu müssen.
-
Also vielen dank an euch.
Ich werde union nehmen, das macht genau das, was ich will.
Meine Erfahrung mit Bitoperationen auf einem Intel C2Q Q9550 waren übrigens bisher gut, er hat immer getan, was ich wollte. :p
Solange ich unsinged int und float, die bei mir beide 4 Byte lang sind, nehme, sollte ich da auf der sicheren Seite sein.
Grüße, Bommelmütze
-
nwp2 schrieb:
Das Problem bleibt aber bestehen.
aber nur bei typumwandlungen, z.b. uint32_t in char[4] oder sowas, sonst nicht. wenn du schreibst: int x = ...; x = x | 0x8000;, dann wird bit 15 immer gesetzt, das ist total portabel. völlig egal, wie die maschine intern die 'ints' speichert.
-
rofliger thread^^ :>
-
Bommelmutze schrieb:
Also vielen dank an euch.
Ich werde union nehmen, das macht genau das, was ich will.Bei einer union ist nicht definiert, was passiert, wenn man einen Member schreibt und dann einen anderen liest.
Das solltest du also nicht tun, es sei denn, das ist dir egal. Die Variante mit dem Zeigercast hat dagegen übrigens definiertes Verhalten.
-
@ Bashar: Ich werds ausprobieren, aber ich dachte, wenn der Speicher gemeinsam ist und die Länge gleich, sollte der jeweilige Wert von den Typenstandards festgelegt sein.
-
auch mal ne frage in der richtung: wenn ich eine little-endian maschine habe, werden dann auch floats/doubles zwangsläufig falsch herum gespeichert, oder gibt's da keinen zusammenhang (bzw. der c-standard sagt nix dazu)?
-
also das einzige was ich jetzt auf die schnelle finden konnte war das, aber das kennt ihr sicher schon
ANSI-C sieht vor, daß in der Header-Datei float.h symbolische Konstanten bereitgestellt werden, die Auskunft über die Parameter der Arithmetik geben:
xxx_DIG Genauigkeit: Anzahl der Dezimalstellen
xxx_EPSILON kleinste Zahl mit 1.0 + xxx_EPSILON != 1.0
xxx_MANT_DIG Anzahl der Bits in der Mantisse
xxx_MAX größster Wert
xxx_MAX_10_EXP größster Exponent (dezimal)
xxx_MAX_EXP größster Exponent (binär)
xxx_MIN kleinster Wert
xxx_MIN_10_EXP kleinster Exponent (dezimal)
xxx_MIN_EXP kleinster Exponent (binär)
-
;fricky schrieb:
nwp2 schrieb:
Das Problem bleibt aber bestehen.
aber nur bei typumwandlungen, z.b. uint32_t in char[4] oder sowas, sonst nicht.
Richtig! Und worum geht es hier? Typumwandlungen! Wir schmeißen ints, floats und chars munter durcheinander.
IEEE 754, 23 Bits Mantisse, 8 Bit Exponent und irgendwo schwirrt noch ein Vorzeichenbit rum. Wenn man int i = 1 als float interpretiert, hat man dann den Exponenten, die Mantisse oder das Vorzeichen getroffen? Wenn man Mantisse 1.1 mit hidden Bit hat und macht einen Linksshift, kommt dann 1.01 raus? Oder schieben wir das Bit ins Vorzeichen rein?
;fricky schrieb:
auch mal ne frage in der richtung: wenn ich eine little-endian maschine habe, werden dann auch floats/doubles zwangsläufig falsch herum gespeichert, oder gibt's da keinen zusammenhang (bzw. der c-standard sagt nix dazu)?
Gute Frage. Ich würde meinen der C-Standard sagt dazu nichts weiter als dass sich die Operatoren gleich verhalten müssen, egal welche Rehenfolge die Bits und Bytes haben. Wenn floats nicht "falschrum" gespeichert werden wird es ziemlich irritierende Ergebnisse beim Floatshiften im Intstyle geben. Es ist halt nicht vorgesehen dass man floats shiftet.
Aber schön dass ich noch nicht komplett verwirrt bin und das Problem doch noch erkannt wurde.
-
noobLolo schrieb:
also das einzige was ich jetzt auf die schnelle finden konnte war das, aber das kennt ihr sicher schon
ja danke, hift aber leider nix. mir (und dem op wird sicherlich nutzen) geht's um die interne speicherung als folge von bytes.
-
nwp2 schrieb:
;fricky schrieb:
nwp2 schrieb:
Das Problem bleibt aber bestehen.
aber nur bei typumwandlungen, z.b. uint32_t in char[4] oder sowas, sonst nicht.
Richtig! Und worum geht es hier? Typumwandlungen! Wir schmeißen ints, floats und chars munter durcheinander.
IEEE 754, 23 Bits Mantisse, 8 Bit Exponent und irgendwo schwirrt noch ein Vorzeichenbit rum. Wenn man int i = 1 als float interpretiert, hat man dann den Exponenten, die Mantisse oder das Vorzeichen getroffen? Wenn man Mantisse 1.1 mit hidden Bit hat und macht einen Linksshift, kommt dann 1.01 raus? Oder schieben wir das Bit ins Vorzeichen rein?
deshalb habe ich auch gesagt, das bei floats die Bitoperationen keinen Sinn machen. Wenn ich dem OP verstanden habe, will er die Bits ohne einen festen Sinn (wie etwas bei ints dass ein link shift eine 2^x Multiplikation gleichkommt) manipulieren.
-
;fricky schrieb:
auch mal ne frage in der richtung: wenn ich eine little-endian maschine habe, werden dann auch floats/doubles zwangsläufig falsch herum gespeichert...
ja scheint so als wenns so wär
hab mal dieses bild verwendet http://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Float_example.svg/590px-Float_example.svg.png
und der code schaut so aus
unsigned int swap32(unsigned int x) { return x<<24 | x>>24 | (x & (unsigned int)0x0000ff00UL)<<8 | (x & (unsigned int)0x00ff0000UL)>>8; } int main(void) { float x = 0.15625; int *z = (int*)&x; *z = swap32(*z); return 0; }
vor dem swap32 steht in *z 0x0000203E und danach 0x3E200000 und das ist laut meinem rechner 1111100010000000000000000000000000 sollt schon stimmen oder?
-
supertux schrieb:
Wenn ich dem OP verstanden habe, will er die Bits ohne einen festen Sinn (wie etwas bei ints dass ein link shift eine 2^x Multiplikation gleichkommt) manipulieren.[/quote]
naja, der sinn ist wohl, mit dem IEEE-format ein bisschen rumzuspielen, mantisse, exponent usw. verändern und so.
-
Bommelmutze schrieb:
@ Bashar: Ich werds ausprobieren, aber ich dachte, wenn der Speicher gemeinsam ist und die Länge gleich, sollte der jeweilige Wert von den Typenstandards festgelegt sein.
Ausprobieren hat da nicht viel Sinn, es dürfte in der Regel funktionieren, es ist nur formal nicht definiert. Deshalb sag ich ja, "es sei denn das ist dir egal".
-
Bashar schrieb:
Bei einer union ist nicht definiert, was passiert, wenn man einen Member schreibt und dann einen anderen liest.
Das solltest du also nicht tun, es sei denn, das ist dir egal. Die Variante mit dem Zeigercast hat dagegen übrigens definiertes Verhalten.Das ist mir in dem Fall wirklich egal, da es funktioniert und für einen Informatikkurs aus 9. - 13. Klässlern leicht nachvollziehbar ist.
;fricky schrieb:
naja, der sinn ist wohl, mit dem IEEE-format ein bisschen rumzuspielen, mantisse, exponent usw. verändern und so.
Das trifft ungefähr was ich vor hatte.
-
Bommelmutze schrieb:
... 9. - 13. Klässlern leicht nachvollziehbar ist.
das lass ich mal so dahin gestellt nicht das die dich dann alle so
anschauen, denk das geht mit ein paar bildchen ala wikipedia schon besser die würd ich auf vorrat mal mitnehmen
lg lolo