w &= 0xfffffffc



  • Hallo,
    bin neu hier und hoffe hier Hilfe zu finden. Versuche im Gegenzug natürlich auch zu helfen, wenn ich kann. Aber nun zu meinem Problem:

    In einem Sourcecode bin ich über folgende kleine Methode gestolpert:

    void TruncateWidth(int& w)
    {
       w &= 0xfffffffc;
    }
    

    Es handelt sich beim übergebenen 'w' um eine Referenz auf die Breite eines geladenen Bildes. Was macht nun der '&='-Operator mit dieser Referenz?
    Bei einem Aufruf der Methode hat sich bisher nichts am mitgegebenem Wert geändert. Wann wäre dem denn nicht so?
    Schonmal vielen Dank im voraus!



  • Die Funktion löscht die unteren beiden Bits.


  • Mod

    Jup. Und zur Erklärung:

    0xfffffffc = 11111111111111111111111111111100 in Binärschreibweise



  • wenn ich nicht ganz falsch liege könnte man sagen das statement macht w durch 4 teilbar.



  • Vielen Dank für die schnelle Hilfe.
    Habe mich bisher nie wirklich mit den Bit-Operatoren beschäftigt.
    Sie ersparen zwar Schreibarbeit, machen den Code aber (für mich) unübersichtlich.


  • Mod

    void TruncateWidth(unsigned& w)
    {
       w -= w % 4;
    }
    

    erzeugt den gleichen Code, wenn der Compiler was taugt. Beachte die Verwendung von unsigned hier, denn bei negativen ints ist das Ergebnis des and (bei 2er Komplement) nicht durch 4 teilbar.



  • Sieht auf jeden Fall übersichtlicher aus.
    Da w die Breite eines Bildes angibt, sollten negative Werte ausgeschlossen werden. Daher ist unsigned natürlich auch eine gute Idee.
    Danke.



  • camper schrieb:

    void TruncateWidth(unsigned& w)
    {
       w -= w % 4;
    }
    

    erzeugt den gleichen Code, wenn der Compiler was taugt. Beachte die Verwendung von unsigned hier, denn bei negativen ints ist das Ergebnis des and (bei 2er Komplement) nicht durch 4 teilbar.

    hm ok stimmt, daran habe ich gar nicht gedacht.
    bin aufgrund der breitenangabe eines bildes von positiven werten ausgegangen.



  • camper schrieb:

    void TruncateWidth(unsigned& w)
    {
       w -= w % 4;
    }
    

    erzeugt den gleichen Code, wenn der Compiler was taugt.

    void TruncateWidth (unsigned& w)
    {
       w = (w>>2)<<2;
    }
    

    erzeugt den gleichen Code, wenn der Compiler was taugt.



  • C++Fan 2009 schrieb:

    camper schrieb:

    void TruncateWidth(unsigned& w)
    {
       w -= w % 4;
    }
    

    erzeugt den gleichen Code, wenn der Compiler was taugt.

    void TruncateWidth (unsigned& w)
    {
       w = (w>>2)<<2;
    }
    

    erzeugt den gleichen Code, wenn der Compiler was taugt.

    Bei unsigned kann man ja wieder ohne Bedenken bzgl Portabilität

    void TruncateWidth (unsigned & w)
    {
       return w & -4;
    }
    

    schreiben.



  • Jetzt, wo ich verstehe was da abläuft, kann ich's ja so lassen.
    Aber vielen Dank an Alle.



  • Sebastian Pizer schrieb:

    Bei unsigned kann man ja wieder ohne Bedenken bzgl Portabilität

    void TruncateWidth (unsigned & w)
    {
       return w & -4;
    }
    

    schreiben.

    bloß das

    void TruncateWidth (unsigned & w)
    

    keinen rückgabewert hat...
    sry für [klugscheiß].



  • Keithy schrieb:

    Sebastian Pizer schrieb:

    Bei unsigned kann man ja wieder ohne Bedenken bzgl Portabilität

    void TruncateWidth (unsigned & w)
    {
       return w & -4;
    }
    

    schreiben.

    bloß das

    void TruncateWidth (unsigned & w)
    

    keinen rückgabewert hat...
    sry für [klugscheiß].

    Richtig. Ich hatte es zwischendurch zu einer Funktion a la
    unsigned TruncateWidth (unsigned w)
    geändert und dann nur zur Hälfte wieder zurück. So muss es natürlich
    w &= -4;
    heißen.

    Gruß,
    SP



  • Sebastian Pizer schrieb:

    Bei unsigned kann man ja wieder ohne Bedenken bzgl Portabilität

    void TruncateWidth (unsigned & w)
    {
       return w & -4;
    }
    

    schreiben.

    wieso? muss -4 auf jedem system dieser erde 0xfffffffc sein?
    🙂



  • Basher schrieb:

    wieso? muss -4 auf jedem system dieser erde 0xfffffffc sein?
    🙂

    Das nicht, aber es wird ja implizit zu einem unsigned konvertiert, bevor & angewendet wird. unsigned(-4) liefert garantiert eine vorzeichenlose Zahl mit Bitmuster "11...1100" -- auch, wenn Du einen Exoten hast, der für vorzeichenbehaftete Ganzzahlen nicht das 2er-Komplement benutzt.



  • Sebastian Pizer schrieb:

    Basher schrieb:

    wieso? muss -4 auf jedem system dieser erde 0xfffffffc sein?
    🙂

    Das nicht, aber es wird ja implizit zu einem unsigned konvertiert, bevor & angewendet wird. unsigned(-4) liefert garantiert eine vorzeichenlose Zahl mit Bitmuster "11...1100".

    wenn nun aber für negative zahlen kein zweier-, sondern z.b. einerkomplement benutzt wird (sowas solls ja geben), dann ist -4 111...1011. damit löschste nur bit 2 weg, nicht bit 1 und 0, wie geplant.
    btw, noch 'ne variante:

    unsigned TruncateWidth (unsigned w)
    {
       return (w/4)*4;
    }
    

    🙂



  • Basher schrieb:

    wenn nun aber für negative zahlen kein zweier-, sondern z.b. einerkomplement benutzt wird (sowas solls ja geben), dann ist -4 111...1011.

    Spielt keine Rolle. Der C++ Standard sagt genau, was passiert, wenn Du eine vorzeichenbehaftete Zahl zu einer vorzeichenlosen Zahl konvertierst. Das Bitmuster, was Du danach erhälst entspricht immer dem 2er-Komplement.



  • Sebastian Pizer schrieb:

    Der C++ Standard sagt genau, was passiert, wenn Du eine vorzeichenbehaftete Zahl zu einer vorzeichenlosen Zahl konvertierst. Das Bitmuster, was Du danach erhälst entspricht immer dem 2er-Komplement.

    biste sicher? was passiert bei:

    // angenommen: negative zahlen sind einerkomplement, 'signed magnitude' oder was anderes
    unsigned a = (unsigned)-4;  // <-- a enthält jetzt bitmuster von -4 im zweierkomplement, nach deiner aussage
    int b = (int)a; // bekommen wir hier unsere -4 zurück? wohl kaum.
    

    🙂



  • Basher schrieb:

    biste sicher?

    Ja. Soll ich jetzt noch die Stelle aus dem Standard raussuchen? Müste irgendwo bei "standard conversions" sein. Das mit dem Bitmuster ergibt sich aus der Regel, dass bei "u=s;" -- wobei u eine vorzeichenlose Integervariable und s irgend eine andere Integervariable (meinetwegen auch long) ist -- folgendes gilt:
    u = s modulo (2 hoch N), N ist hier die Zahl der Bits in u.

    Basher schrieb:

    was passiert bei:

    // angenommen: negative zahlen sind einerkomplement, 'signed magnitude'
    // oder was anderes
    unsigned a = (unsigned)-4;  // <-- a enthält jetzt bitmuster von
                                // -4 im zweierkomplement, nach deiner aussage
    int b = (int)a; // bekommen wir hier unsere -4 zurück? wohl kaum.
    

    Nein. Konvertieren in eine vorzeichenbehaftete Zahl ist "implementation-defined", falls der zugewiesene Wert nicht im Wertebereich liegt. Steht auch so im Standard. Guck mal selbst nach. :p

    Gruß,
    SP



  • msvc++

    ohne probleme kommt da -4 raus. bitmuster bei beiden: 0xfffffffc

    🙂


  • Mod

    Für meinen Geschmack ist das diese Variante (x & -4u) ein bisschen zu subtil, auch wenn ich zugeben muss, dass ich das früher auch so geschrieben habe.
    x & ~(4u-1) ist vielleicht ein bisschen besser. Bei heutigen Compilern gibt es aber eigentlich nur noch selten einen Grund, das im Code stehen zu lassen.
    Sowas hab ich in der Wildnis auch schon gesehen:

    int foo;
    while ( foo % 4 != 0 )
        foo--;
    

    Da ziehe ich dann sogar ein einfaches & vor 😉


Anmelden zum Antworten