Seltsames Verhalten von shift



  • Hallo,
    das Problem ist ein wenig schwer zu beschreiben. Wenn ihr also mehr Kontext braucht, sagt Bescheid!
    Im Prinzip geht es um den shift operator. Vorab: ich benutze den Microsoft Visual C++ Express Compiler.
    Innerhalb eines komplexeren Algorithmus' liegt folgendes Stück Code vor

    arr[x]>>(tmp-a);
    
    • arr ist ein statisches array aus unsigned int's.
    • tmp ist eine static const variable vom typ unsigned int mit dem wert CHAR_BIT * sizeof(unsigned int) (in meinem Fall 32).
    • a ist vom typ unsigned int und ein Parameter der Funktion, also erst zur Compilezeit bekannt.

    Das Ergebnis des Algorithmus wird "komisch", wenn a == 0 ist. In dem Falle würde der Wert an der Stelle x um 32 bit geshiftet, was ja bekanntlich undefiniert ist (weshalb ich diesen Fall jetzt extra behandele).
    Das Seltsame ist nun, dass bei diesem Code:

    arr[x]>>(32);
    


    Ich habe es bisher noch nicht geschafft, diesen "Fehler" zu reproduzieren. Er scheint nur in meinem Algorithmus aufzutreten.

    Woran kann das liegen? Optimiert der Compiler im zweiten Fall den Ausdruck in arr[x]=0, was er im ersten Fall nicht kann und das Verhalten dort undefiniert bleibt?



  • Mehr Code. Hier ist ja noch nicht mal ne Zuweisung



  • undefiniertes Verhalten bleibt undefiniertes Verhalten.



  • Warum ist das eigentlich undefiniert? Beim Shiften wird doch immer mit 0 aufgefüllt. Warum soll das für 32 oder 2000 nicht mehr gehen?



  • ?????????????? schrieb:

    Warum ist das eigentlich undefiniert? Beim Shiften wird doch immer mit 0 aufgefüllt. Warum soll das für 32 oder 2000 nicht mehr gehen?

    Weil ein bedeutender Prozessorbauer nur die niederwertigen fünf Bits des Schiebearguments überhaupt verdrahtet hatte, weshalb ein a>>b nur ein a>>(b%32) war.



  • Hier ein bisschen mehr Code (vereinfacht):

    static const size_t BITS = CHAR_BIT*sizeof(size_t);
    
    template<size_t Bits> // Bits ist immer > BITS und ein Vielfaches von BITS
    class Bitset
    {
        size_t field[array_size];
    
       public: 
        static const size_t array_size = N/BITS;
    
        Bitset& operator<<=(size_t a)
        {
            size_t tmp = a/BITS;
    
            /*
                Hier: Bits mittels memmove verschieben
            */
    
            a%=BITS; //Die "übrigen" Bits behandeln
    
            //if(a) return *this; // normalerweise diesen Fall abfangen
            for(size_t i(array_size-1);i!=tmp;--i)
                 (field[i]<<=a)|=(field[i-1]>>(BITS-a));
            field[tmp]<<=a;
    
            return *this;
        }
    };
    

    Dieser Algorithmus funktioniert, wenn man die for Schleife nur ausführen lässt, wenn a != 0 ist (also a weder 0 noch ein Vielfaches von BITS ist).
    Wenn man das (wie oben) nicht macht, dann ergibt sich Folgendes:

    int main()
    {
       Bitset<64> obj; //obj initialisieren
       obj.print();
       obj<<=0;
       obj.print();
    }
    

    Output:

    10010010010010010010010010010010 01001001001001001001001001001001
    10010010010010010010010010010010 [b]11011011011011011011011011011011[/b]
    

    Wenn ich aber die Zeile 23 so ändere:

    (field[i]<<=a)|=(field[i-1]>>[b]32[/b]);
    

    Dann sieht der Output wie folgt aus:

    10010010010010010010010010010010 01001001001001001001001001001001
    10010010010010010010010010010010 [b]01001001001001001001001001001001[/b]
    

    Bei mir ist CHAR_BIT == 8 und sizeof(size_t) == 4. Also ist BITS-a == 32, wenn a == 0 ist.
    Obwohl um den selben Wert geshiftet wird, bekomme ich ein anderes Ergebnis, wenn ich BITS-a mit 32 in dem Shift Algorithmus ersetze.



  • (field[i]<<=a)|=...
    ist eh nicht definiert.



  • Zwinge ich den Compiler durch die Klammern nicht, field[i]<<=a zuerst auszuwerten?



  • shifter schrieb:

    Zwinge ich den Compiler durch die Klammern nicht, field[i]<<=a zuerst auszuwerten?

    ja, das Auswerten, das Berechnen des Wertes, geschieht zuerst. Aber nicht die Wirkung der eingebauten Zuweisung. Die passiert irgendwann innerhalb des Gesamtausdrucks. Bei zwei Zuweisungen kann die Reihenfolge anders sein, als Du haben wolltest.



  • Du hattest recht volkard.
    Wenn ich das ganze so ändere

    field[i]<<=a;
    field[i]|=(field[i-1]>>(BITS-a));
    

    dann ist es egal, ob BITS-a oder 32 da steht. Das Ergebns ist immer "richtig".



  • Trotzdem gilt

    5.8 Shift operators schrieb:

    The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.


Anmelden zum Antworten