Bitoperationen an float



  • Oder versuchs mit unions.

    union floatint{
        float f;
        int i;
    };
    
    union floatint fi;
    fi.f = 3.14;
    fi.i<<=2;
    printf("%f\n", fi.f);
    

    Edit: Wobei du sehr aufpassen musst, das bitshiften ist leider kein echtes bitshiften, es wird häufig verfälscht. Wahrscheinlich gehts mit 4 chars die dann geshiftet werden besser.



  • nwp2 schrieb:

    Edit: Wobei du sehr aufpassen musst, das bitshiften ist leider kein echtes bitshiften, es wird häufig verfälscht.

    was genau meinst du damit?



  • Ich habe zum Beispiel einen little Endian 32 Bit Intel Prozessor. Eine 1 (int i = 1) wird demzufolge durch eine 1 und 31 Nullen dargestellt. Wenn ich jetzt einen Linksshift mache (i<<=1) fällt die 1 links runter und ich habe nur noch Nullen. Tatsächlich aber kommt 01 und 30 Nullen heraus, was einem Rechtsshift entspricht.
    Eine -2 (i = -2) wäre bei mir 10 und 30 Einsen. Ein Rechtsshift (i>>=1) sollte eigentlich 010 und 29 Einsen ergeben, was -5 entspricht. Tatsächlich aber kommt -1 heraus.

    Es ist logisch dass bei einem Linksshift von 1 2 rauskommen sollte, aber streng genommen stimmt das nicht, das ist je nach Repräsentation der Zahl verschieden. Allerdings wird sowas rausgerechnet, nach einem Linksshift von 1 kommt immer 2 raus, egal wie die interne Repräsentation aussieht, deshalb sollte man aufpassen wenn man echte Bitoperationen will, dass da nicht irgendwas wegen internen Repräsentationen umgerechnet wird.

    Ich glaube unsigned char shiftet echt ohne Umrechnungen, aber ich bin mir da nicht sicher.



  • Bevor du hier anfängst wirres Zeugs zu erzählen, solltest du lieber nochmal die Basics nachlesen...



  • @nwp2: wovon redest du? Was soll ich mir unter dem Ausdruck i<<=1 vorstellen?



  • Schreibe ich so kryptisch? http://home.fhtw-berlin.de/~junghans/cref/CONCEPT/bit_shift.html#shift

    #include <stdio.h>
    int main(){
    	int i = 1;
    	i<<=1; //Linksshift um 1 Bit
    	printf("%d\n", i); //hier kommt 2 raus, nicht 0
    	return 0;
    }
    


  • nwp2 schrieb:

    Ich habe zum Beispiel einen little Endian 32 Bit Intel Prozessor. Eine 1 (int i = 1) wird demzufolge durch eine 1 und 31 Nullen dargestellt. Wenn ich jetzt einen Linksshift mache (i<<=1) fällt die 1 links runter und ich habe nur noch Nullen. Tatsächlich aber kommt 01 und 30 Nullen heraus, was einem Rechtsshift entspricht.

    Du meinst, Linksshiften shiftet nach rechts?

    Eine -2 (i = -2) wäre bei mir 10 und 30 Einsen. Ein Rechtsshift (i>>=1) sollte eigentlich 010 und 29 Einsen ergeben, was -5 entspricht. Tatsächlich aber kommt -1 heraus.

    Und Rechtsshiften shiftet nach links? Wie könnte das zu erklären sein, mal nachdenken ...

    Es ist logisch dass bei einem Linksshift von 1 2 rauskommen sollte, aber streng genommen stimmt das nicht, das ist je nach Repräsentation der Zahl verschieden. Allerdings wird sowas rausgerechnet, nach einem Linksshift von 1 kommt immer 2 raus, egal wie die interne Repräsentation aussieht, deshalb sollte man aufpassen wenn man echte Bitoperationen will, dass da nicht irgendwas wegen internen Repräsentationen umgerechnet wird.

    Also mal zusammengefasst: 1<<1 sollte 2 ergeben, aber streng genommen stimmt das nicht, aber das wird rausgerechnet, und deshalb kommt 2 raus? Ein bisschen wirr, meinst du nicht? Wie wärs mit der einfachen Erklärung, dass 1<<1 2 sein soll und deshalb auch 2 ergibt?

    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.



  • ^^ shifts und logik-operatoren verhalten sich immer wie erwartet, auch mit multibyte-typen, unabhängig von der endianess. aufpassen musste allerding mit verrückten casts (z.b float in uint32_t), da kann's natürlich schief gehen.
    🙂



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



  • 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)?
    🙂


Anmelden zum Antworten