Einige Fragen zu Bitoperationen



  • Hi,

    also ich hab ziemliche Probleme mit Bitoperationen !
    Verstehe das irgendwie alles nicht oder nur teilweise !
    Ja, ich hab schon den Wiki-Artikel und andere Seiten dazu gelesen, aber es fällt mir trotzdem noch schwer.

    Also:
    Angenommen ich hab eine Zahl 22530 (dez.), die binär so 01011000 00000010 aussieht.
    Unter Win liegt die Zahl ja als Little Endian im Speicher, also so: 00000010 01011000 ??
    Also das Byte mit der "kleinsten" Wertigkeit sozusagen liegt an der niedrigsten ("ersten") Adresse.

    Wenn ich jetzt das Highbyte der Zahl haben will, dann würde der Code (C#) so aussehen:

    private byte HiByte(short val)
    {
        return (byte)(((short)(val) >> 8) & 0xFF);
    }
    

    Ich schiebe also per >> 8 8 Bit nach rechts, die Zahl würde dann also so aussehen: 00000000 01011000 ??
    Und durch Casten nach byte krieg ich dann 01011000 ??
    Und wozu noch das bitweise Und mit 0xFF ?

    Kann mir das alles bitte jemand erklären ?
    Weitere Fragen tauchen bestimmt noch auf ! 🙂

    Ich danke schonmal !



  • Bits Bytes ... schrieb:

    ... C# ...

    Ich schiebe also per >> 8 8 Bit nach rechts, die Zahl würde dann also so aussehen: 00000000 01011000 ??
    Und durch Casten nach byte krieg ich dann 01011000 ??
    Und wozu noch das bitweise Und mit 0xFF ?

    Erstamal: Hier bist Du falsch, hier gibt's des hohe C nur ohne #! 😉

    Trotzdem:
    Der innere short- Cast scheint mir unnötig, weil ja in die Funktion nur short reinkommen kann.
    Zaweitens: short kenn' ich meist als 16 Bit, deswegen der finale cast nach byte.
    Darittens: Es ist in C zumindest nicht ganz eindeutig, was mit Vorzeichenbits beim Shiften passieren soll, weiß nicht, wie's mit C# steht. Aber wenn das gesetzt wird und Du willst eine 2K- Darstellung vorzeichenrichtig dividieren, ziehst Du "1"er von "oben" über Bit 15 nach. Die werden mit & 0xff einfach ausgelöscht.

    Wenn's nicht ganz stimmt -> I'm not designed for C#! 😃

    EDIT: Jetzt bin ich selbst schon ganz wirr im Kopf, natürlich ist hier nicht das ANSI-C- Forum ... trotzdem bist Du falsch! :p



  • Hi,

    zu 1.: Ok gut, bin nur davon ausgegangen, dass die Bitoperationen ja auf alle Sprachen zutreffen !
    Deshalb der Post hier.

    zu 2.: Gut, also kriege ich durch den Cast nach byte dann das 2.Byte sozusagen ?

    zu 3.: und damit kann ich das Problem mit dem Vorzeichen lösen sozusagen !?

    Und sonst kann man sagen, dass ich per Rechtsshift von 8 eines short immer das Highbyte kriege (bei nem int ein Rechtsshift von 16 ... ??) und über das bitweise Und mit 0xFF bei nem short kriege ich immer das Lowbyte !?

    Und noch was zu deinem Edit: Ich mache C# auch nur "nebenbei".
    Sonst fühle ich mich in der C/C++ Ecke definitiv wohler !

    So, das wären noch n paar Fragen ! 🙂
    und ich danke dir erstmal !!



  • Bits Bytes ... schrieb:

    zu 2.: Gut, also kriege ich durch den Cast nach byte dann das 2.Byte sozusagen ?

    Ne, Vorsicht, der cast macht zweierlei. Zum einen stellst Du damit die Typenrichtigkeit zum Funktionstypen sicher (C macht das implizit, C# - keine Ahnung obs das braucht). Zum zweiten müssen ja ein paar Bits weggesägt werden, die oberen sind's üblicherweise. Deswegen hast Du sie ja "in Sicherheit" runtergeshiftet.

    Bits Bytes ... schrieb:

    zu 3.: und damit kann ich das Problem mit dem Vorzeichen lösen sozusagen !?

    Wie gesagt, ist nicht allgemeingültig, bei C ist das "implementation defined", glaube ich. Aber die meisten machen das so, daß ein Rechtsshift das Vorzeichenbit von oben nachzieht. Also, wenn B15 gesetzt war (= 2K- Darstellung, negativ), wird es nach dem Shift nachbesetzt. So wird aus -4000 durch shiften -2000, -1000 usw.

    Bits Bytes ... schrieb:

    Und sonst kann man sagen, dass ich per Rechtsshift von 8 eines short immer das Highbyte kriege (bei nem int ein Rechtsshift von 16 ... ??) und über das bitweise Und mit 0xFF bei nem short kriege ich immer das Lowbyte !?

    Beim short sollte es stimmen, beim int (32 Bit) mußt Du aber 24 mal shiften. Das unterste Byte schnibbelt man zuverlässig so frei.

    Zurück zu 1.: Zumindest bei C- Compilern sind die Bitbreiten der Typen und das Verhalten beim Shiften nicht eindeutig definiert. Deswegen bitte: Doku lesen oder per Debugger probieren, leider, leider ... 🙄

    EDIT: Nachtrag - eigentlich müßte das da genau das Gleiche abliefern:

    return (val >> 8);
    

    😉



  • Bits Bytes ... schrieb:

    Und sonst kann man sagen, dass ich per Rechtsshift von 8 eines short immer das Highbyte kriege (bei nem int ein Rechtsshift von 16 ... ??) und über das bitweise Und mit 0xFF bei nem short kriege ich immer das Lowbyte !?

    kommt darauf an, wiel lang so'n datentyp ist. so in etwa geht das:

    lsb_von_x = x&0xff;
    byte_links_daneben (x>>8)&0xff;
    byte_links_von_links_daneben = (x>>16)&0xff;
    ...usw..
    msb_von_x = (x>>(anzahl_bits_von_x-8))&0xff;
    

    🙂



  • Hi,
    ok ich sehe schon, dass ich da noch sehr viel Übung und Praxis brauche.
    Mein Problem ist denk ich auch, dass ich mir das alles sehr schlecht vorstellen kann. 😞

    Ok, also könnte es in der Praxis z.b. so aussehen:
    int val;
    byte low = val & 0xff;
    byte high = (val >> 24) & 0xff;

    Und der entsprechende Rechtsshift beim Highbyte deswegen, weil auf der Windowskiste Little Endian gilt.
    Auf ner Big-Endian-Kiste würde es dann andersrum aussehen (was für mich jetzt aber 2.rangig ist, erstmal Bitoperationen allgemein verstehen):
    byte low = (val >> 24) & 0xff;
    byte high = val & 0xff;

    So, und mit & 0xff stell ich sozusagen sicher, dass ich das Vorzeichen auch wieder korrekt übernehme !?
    Also durch die bitweise Und-Verknüpfung "füge" ich sozusagen dass Vorzeichen wieder zur neuen Zahl hinzu.

    pointercrash() schrieb:

    Ne, Vorsicht, der cast macht zweierlei. Zum einen stellst Du damit die Typenrichtigkeit zum Funktionstypen sicher (C macht das implizit, C# - keine Ahnung obs das braucht).

    Ok, ja klar, muss ja so sein.

    pointercrash() schrieb:

    Zum zweiten müssen ja ein paar Bits weggesägt werden, die oberen sind's üblicherweise. Deswegen hast Du sie ja "in Sicherheit" runtergeshiftet.

    Gut, also kriege ich mit dem Cast in einer Hinsicht das 1.Byte. Also z.B. bei 1000 0010 wäre es dann das 1000 !?

    Aber wie sieht das dann beim Highbyte aus ? Ausgangszahl: 1000 0010, und per Rechtsshift sieht dann die Zahl so aus: 0010 0000 !?
    Kann das sein ? Das verstehe ich grad noch nicht.

    Und wiedermal ein Danke für die guten Erklärungen und die große Geduld mit mir ! 👍



  • Da ich hier ja nix editieren kann:
    Ich meine natürlich als Ausgangszahl für meine Beispiele oben 10000000 00100000 !

    Sonst wäre es wohl etwas sinnfrei. 🙂



  • Bits Bytes ... schrieb:

    int val;
    byte low = val & 0xff;
    byte high = (val >> 24) & 0xff;
    

    Ja, aber Du unterschlägst die mittleren Bytes (Du siebst nur das oberste und das unterste Byte aus). Da fehlen ja noch zwei:

    int val;
    byte midhigh = (val >> 16) & 0xff;
    byte midlow = (val >> 8) & 0xff;
    

    Bits Bytes ... schrieb:

    So, und mit & 0xff stell ich sozusagen sicher, dass ich das Vorzeichen auch wieder korrekt übernehme !? Also durch die bitweise Und-Verknüpfung "füge" ich sozusagen dass Vorzeichen wieder zur neuen Zahl hinzu.

    Nö, Du verundest nur, d.h. Du setzt alle Bits, die nicht eins sind auf 0:

    0xFF = 1111'1111b
      0101'1010'1011'1110
    & 0000'0000'1111'1111
    =====================
      0000'0000'1011'1110
    

    Hat also mit dem Vorzeichen nichts zu tun.

    Bits Bytes ... schrieb:

    pointercrash() schrieb:

    Zum zweiten müssen ja ein paar Bits weggesägt werden, die oberen sind's üblicherweise. Deswegen hast Du sie ja "in Sicherheit" runtergeshiftet.

    Gut, also kriege ich mit dem Cast in einer Hinsicht das 1.Byte. Also z.B. bei 1000 0010 wäre es dann das 1000 !?

    Nein, nehmen wir an, Du hast 'nen 16 Bit short und 8 Bit Byte, dann wird mittels cast abgeschnitten:

    (byte) 0101'1010'1011'1110
    ==========================
                     1011'1110
    

    Das obere Byte gibt es durch den cast nicht mehr, du hast jetzt nur den 8-Bit- Typen.

    Bits Bytes ... schrieb:

    Aber wie sieht das dann beim Highbyte aus ? ... Kann das sein ? Das verstehe ich grad noch nicht.

    >> 8  0101'1010'1011'1110
    ==========================
          0000'0000'0101'1010
    (byte)0000'0000'0101'1010
    ==========================
                    0101'1010
    

    Also hast Du so das HighByte des short runtergeshiftet und mittels cast freigeschnitten. Bißchen weiter jetzt?



  • ^^eigentlich ist so'n cast doch garnicht nötig. das plätten der oberen bits (mit & 0xff) sollte ausreichen.
    🙂



  • Ja super ! 👍
    Ich verstehe bei jedem Mal immer mehr ! 🙂

    Gut, also:
    Zahl: 0000 0010 1111 1100

    per cast auf byte von dieser Zahl kriege ich dann 1111 1100 (also das Lowbyte)

    und per Shiften >> 8 von dieser Zahl da erhalte ich dann 0000 0000 0000 0010 und per Cast auf byte erhalte ich dann am Ende 0000 0010 (also das Highbyte)

    Stimmt das so jetzt ?? 🙂

    Also was hier die ganze Zeit rechnen, ist Big-Endian !?
    Aber ich denke bei Win (bzw. Intel, AMD-CPUs) ist Little-Endian !?

    Genau das hat mich auch extra noch verwirrt.



  • Bits Bytes ... schrieb:

    Also was hier die ganze Zeit rechnen, ist Big-Endian !?
    Aber ich denke bei Win (bzw. Intel, AMD-CPUs) ist Little-Endian !?

    shifts schieben immer in die gewünschte richtung, unabhängig von der endianess.
    🙂



  • Ach so is das ! 👍

    Ok, hab im Hinterkopf immer daran gedacht und war deswegen auch teilweise durcheinander !

    Und wenn ich jetzt Daten (hm z.B. von der RS232) empfange, dann liegen die in Big-Endian vor !?
    Und ich muss die in Little-Endian umwandeln (ich hab als Bsp. halt mal n intel-cpu und windows = Little-Endian), um sie z.B. korrekt darzustellen.

    Ist das richtig so ?

    Zusammenfassendes Bsp. (hoffentlich ist das richtig):
    Ich erhalte Daten von der seriellen Schnittstelle (shorts). Dadurch liegen die Daten ja in Big-Endian vor, also sieht die Beispiel so aus: val - 00110010 11001001 (am Ende muss sie wegen Little-Endian so aussehen: 11001001 00110010 (beide Bytes genau umgedreht)).

    Dafür hab ich mir eine "Swap"-Funktion (halt mal in C#) geschrieben:

    private short Swap(short value)
    {
        return (short)((byte)(HiByte(value) & 0xFF) | ((LoByte(value) & 0xFF) << 8));
    }
    

    Hibyte-Code:

    private byte HiByte(short val)
    {
        return (byte)(((val) >> 8) & 0xFF);
    }
    

    Diese Funktion würde mir also 00110010 zurückgeben.

    LoByte-Code:

    private byte LoByte(short val)
    {
        return (byte)(val & 0xFF);
    }
    

    11001001 wird mir davon zurückgeliefert.

    Durch den Linksshift von 8 habe ich dann 11001001 00000000.

    Und dann wird 00110010 und 11001001 00000000 oder-verknüpft:
    00000000 00110010 (lieferte mir HiByte)
    11001001 00000000 (lieferte mir LoByte)
    = 11001001 00110010 Ergebnis (meine Ausgangszahl jetzt in Little-Endian)

    Die 1-Million-Euro-Frage: Stimmt meine Beispiel ??? 😕



  • Bits Bytes ... schrieb:

    Und wenn ich jetzt Daten (hm z.B. von der RS232) empfange, dann liegen die in Big-Endian vor !?
    Und ich muss die in Little-Endian umwandeln (ich hab als Bsp. halt mal n intel-cpu und windows = Little-Endian), um sie z.B. korrekt darzustellen.
    Ist das richtig so ?

    Nicht ganz. Das CPU- Endian- Format ist unabhängig davon, was über die RS232 reinkommt, das ist ja eigens definiert. Vielleicht meinen wir doch das Gleiche und Dir geht es nur um das little/big- swapping eines short (16 Bit)?

    Bits Bytes ... schrieb:

    Dafür hab ich mir eine "Swap"-Funktion (halt mal in C#) geschrieben:

    private short Swap(short value)
    {
        return (short)((byte)(HiByte(value) & 0xFF) | ((LoByte(value) & 0xFF) << 8));
    }
    

    Da sehe ich ein Problem.

    ((LoByte(value) & 0xFF) << 8)
    

    Erstmal brauchst Du "& 0xFF" gar nicht, da LoByte ohnehin nur 8 Bit zurückliefert. Zweitens liefert LoByte nur 8 Bit zurück, die Du mit << 8 ins Nirvana schiebst 😮 . Also mußt Du erstmal mit 'nem cast oben Platz schaffen.

    (((short)LoByte(value)) << 8)
    

    Das mit dem & 0xFF gilt auch für die Subs
    Hibyte-Code:

    private byte HiByte(short val)
    {
        return (byte)((val) >> 8);
    }
    

    LoByte-Code:

    private byte LoByte(short val)
    {
        return (byte)(val);
    }
    

    ... sollte genügen.

    Bits Bytes ... schrieb:

    Die 1-Million-Euro-Frage: Stimmt meine Beispiel ??? 😕

    Krieg' ich die dann? 😉
    Wenn Du beachtest, daß Du vorher (s.o. ^^^^^^^^^) nach short casten mußt, könnte es funktionieren. Aber eigentlich wär' jetzt die Zeit, einen Debugger anzuwerfen. Wenn C# structs und unions beherrscht, geht's überdies viel eleganter ...



  • Ok, das sind alles Sachen, die mir einleuchten. Zum Glück 🙂
    Ich glaube, wir meinen tatsächlich das Gleiche.

    Ja, debuggt hab ich vorher schon einige Male, da hat alles gepasst und jetzt aber auch noch ! 🙂
    Also das Vorgehen stimmt, hab schon einige Testläufe gemacht !

    Werde das jetzt aber alles so lassen, weil ich damit mein eigenes struct-union-Bitfeld Konstrukt "versorge".
    Und C# kennt leider keine unions, deshalb habe ich mir den ganzen Spaß selber zusammengebastelt.

    So also vielen vielen Dank für eure Hilfe !!!!! 👍
    Und wenn mir noch was einfällt, dann melde ich mich sicherlich wieder ! 😃
    Außer ihr habt noch was zu bemerken...

    Der Fred hier hat sehr geholfen !
    Schön Abend !


Anmelden zum Antworten