Setze die letzten „n“ Bits in unsigned int

Lesezeit: 2 Minuten

Wie man (am elegantesten) genau einstellt n niedrigstwertige Bits von uint32_t? Das heißt, eine Funktion zu schreiben void setbits(uint32_t *x, int n);. Funktion sollte jeden behandeln n aus 0 zu 32.

Besonders wertvoll n==32 gehandhabt werden soll.

  • Meinst du mit zuletzt hohe oder niedrige Ordnung?

    – Jim Rhodes

    14. November 2011 um 21:52 Uhr

  • Diese Frage verarbeitet alle Werte im Bereich [0, 64]: Erstellen einer Maske mit N niederwertigsten Bits gesetzt

    – phuklv

    19. Juli 2019 um 5:00 Uhr

Benutzer-Avatar
Erich

Hier ist eine Methode, die keine Arithmetik erfordert:

~(~0u << n)

  • +1, kein Speicherzugriff, keine Verzweigungen und keine Sonderfälle. Das halte ich für die eleganteste Lösung.

    – Dunkler Staub

    2. Februar 2013 um 13:29 Uhr

  • @DarkDust: Außer dass dies (manchmal) für n = 32 fehlschlägt, da a verschoben wird uint_32t durch 32 ist undefiniertes Verhalten.

    – Erich

    2. Februar 2013 um 15:52 Uhr

Benutzer-Avatar
Marcelo Cantos

Wenn Sie die niederwertigsten n Bits meinten:

((uint32_t)1 << n) - 1

Auf den meisten Architekturen funktioniert dies nicht, wenn n 32 ist, daher müssen Sie möglicherweise einen Sonderfall dafür festlegen:

n == 32 ? 0xffffffff : (1 << n) - 1

Auf einer 64-Bit-Architektur besteht eine (wahrscheinlich) schnellere Lösung darin, nach oben und dann nach unten zu werfen:

(uint32_t)(((uint64_t)1 << n) - 1)

Tatsächlich kann dies auf einer 32-Bit-Architektur sogar schneller sein, da Verzweigungen vermieden werden.

  • @Marcelo: Ich denke, es funktioniert auch ohne u64-Test, nur die erste Version, oder?

    – Cartesius00

    14. November 2011 um 22:02 Uhr


  • @ TonyK: Nein, es funktioniert nicht, da das Verschieben einer 32-Bit-Ganzzahl um 32 Bit auf den meisten Architekturen nicht unterstützt wird (sicherlich nicht auf Intel).

    – Marcelo Cantos

    14. November 2011 um 22:03 Uhr

  • Rechts: “Das Verhalten ist undefiniert, wenn der rechte Operand … gleich der Länge des heraufgestuften linken Operanden in Bits ist.” [C++11 §5.8/1]

    – James McNellis

    14. November 2011 um 22:03 Uhr

  • @MarceloCantos, IRC-Linksverschiebung mit 31 bei einer 32-Bit-Ganzzahl führt zu Überlauf => undefiniertes Verhalten. Und dann int ist durch den Standard garantiert nur 16 Bit breit. Zusammenfassend kann Ihre Version für Werte von fehlschlagen 16 zu 31.

    – Jens Gustedt

    14. November 2011 um 22:54 Uhr

  • @MarceloCantos, auch in deiner schnelleren Version, was ist der Zweck von Cast n zu uint64_t? Sie sollten die “casten”. 1 Dies bestimmt die Art des Ausdrucks.

    – Jens Gustedt

    14. November 2011 um 22:58 Uhr

Benutzer-Avatar
Oliver Charlesworth

Die anderen Antworten behandeln den Sonderfall von nicht n == 32 (Verschieben um größer oder gleich der Breite des Typs ist UB), also hier ist eine bessere Antwort:

(uint32_t)(((uint64_t)1 << n) - 1)

Alternative:

(n == 32) ? 0xFFFFFFFF : (((uint32_t)1 << n) - 1)

  • Die anderen Antworten handhaben es perfekt. (1 << 32) - 1 ist 0xFFFFFFFF.

    – TonyK

    14. November 2011 um 21:58 Uhr


  • @Tony: Nein, tun sie nicht. Eine Verschiebung über die Schriftlänge hinaus ist undefiniert.

    – Oliver Charlesworth

    14. November 2011 um 21:58 Uhr

  • @Tony: Sie stimmen ab, weil ich eine Antwort gegeben habe, die portablen Code mit genau definiertem Verhalten enthält?

    – Oliver Charlesworth

    14. November 2011 um 22:00 Uhr


  • @OliCharlesworth, wirfst du eine Konstante? Das sollte einfach sein UINT64_C(1). Und dann funktioniert Ihre alternative Lösung nicht auf Architekturen mit 16 Bit intfunktioniert aber nicht für 32 Bit int entweder, weil für 31 du hast einen überlauf.

    – Jens Gustedt

    14. November 2011 um 23:01 Uhr

  • @OliCharlesworth, warum eine Konstante casten, wenn die Sprache Ihnen das richtige Werkzeug gibt, um einfach die richtige anzugeben?

    – Jens Gustedt

    15. November 2011 um 7:26 Uhr

Benutzer-Avatar
TJD

const uint32_t masks[33] = {0x0, 0x1, 0x3, 0x7 ...

void setbits(uint32_t *x, int n)
{
   *x |= masks[n];
}

Wenn Sie die höchstwertigen n Bits meinen:

-1 ^ ((1 << (32 - n)) - 1)

  • Das -1 ^ part ist dasselbe wie eine NOT-Operation, also verwenden Sie einfach ~((1 << (32 - n)) - 1)

    – phuklv

    17. Oktober 2020 um 14:03 Uhr

Benutzer-Avatar
Jim Rhodes

Wenn n null ist, sollten basierend auf der Frage keine Bits gesetzt werden.

const uint32_t masks[32] = {0x1, 0x3, 0x7, ..., 0xFFFFFFFF};

void setbits(uint32_t *x, int n)
{
    if ( (n > 0) && (n <= 32) )
    {
        *x |= masks[--n];
    }
}

  • Das -1 ^ part ist dasselbe wie eine NOT-Operation, also verwenden Sie einfach ~((1 << (32 - n)) - 1)

    – phuklv

    17. Oktober 2020 um 14:03 Uhr

Ziele:

  • keine Verzweigungen (einschließlich Parameterprüfung von n)
  • keine 64-Bit-Konvertierungen
void setbits(uint32_t *x, unsigned n) {
    // As @underscore_d notes in the comments, this line is
    // produces Undefined Behavior for values of n greater than
    // 31(?). I'm ok with that, but if you're code needs to be
    // 100% defined or you're using some niche, little-used
    // compiler (perhaps for a microprocesser?), you should
    // use `if` statements. In fact, this code was just an
    // an experiment to see if we could do this in only 32-bits
    // and without any `if`s. 
    *x |= (uint32_t(1) << n) - 1;
    // For any n >= 32, set all bits. n must be unsigned
    *x |= -uint32_t(n>=32);
}

Hinweis: Wenn Sie brauchen n Typ sein intfügen Sie dies am Ende hinzu:

    // For any n<=0, clear all bits
    *x &= -uint32_t(n>0);

Erläuterung:

    *x |= -uint32_t(n>=32);

Wann n>=32 ist wahr, x wird mit 0xFFFFFFFF bitweise ODER-verknüpft, was ein ergibt x mit allen Bits gesetzt.

    *x &= -uint32_t(n>0);

Diese Zeile besagt, dass solange irgendein Bit gesetzt sein soll, n>0bitweises UND x mit 0xFFFFFFFF, was zu keiner Änderung führt x. Wenn n<=0, x werden bitweise mit 0 UND-verknüpft und ergeben folglich den Wert 0.

Beispielprogramm, um zu zeigen, dass der Algorithmus funktioniert:

#include <stdio.h>
#include <stdint.h>

void print_hex(int32_t n) {
  uint32_t x = (uint32_t(1) << n);
  printf("%3d:  %08x  |%08x  |%08x  &%08x\n",
         n, x, x - 1,
         -uint32_t(n>=32),
         -uint32_t(n>0));
}

void print_header() {
  //        1:  00000002  |00000001  |00000000  &ffffffff
  printf("  n:   1 << n    (1<<n)-1   n >= 32     n <= 0\n");
}

void print_line() {
  printf("---------------------------------------------\n");
}

int main() {
  print_header();
  print_line();
  for (int i=-2; i<35; i++) {
    print_hex(i);
    if (i == 0 || i == 31) {
      print_line();
    }
  }
  return 0;
}

Ausgabe (aufgebrochen und kommentiert):

Zum n < = 0der letzte Schritt UND mit 0, um sicherzustellen, dass das Ergebnis 0 ist.

  n:   1 << n    (1<<n)-1   n >= 32     n <= 0
---------------------------------------------
 -2:  40000000  |3fffffff  |00000000  &00000000
 -1:  80000000  |7fffffff  |00000000  &00000000
  0:  00000001  |00000000  |00000000  &00000000

Zum 1 <= n <= 31, die letzten beiden Schritte “OR 0, AND 0xffffffff” bewirken keine Änderung der Zahl. Der einzige Schritt, der zählt, ist das “ODER (1<

  n:   1 << n    (1<<n)-1   n >= 32     n <= 0
---------------------------------------------
  1:  00000002  |00000001  |00000000  &ffffffff
  2:  00000004  |00000003  |00000000  &ffffffff
  3:  00000008  |00000007  |00000000  &ffffffff
  4:  00000010  |0000000f  |00000000  &ffffffff
  5:  00000020  |0000001f  |00000000  &ffffffff
  6:  00000040  |0000003f  |00000000  &ffffffff
  7:  00000080  |0000007f  |00000000  &ffffffff
  8:  00000100  |000000ff  |00000000  &ffffffff
  9:  00000200  |000001ff  |00000000  &ffffffff
 10:  00000400  |000003ff  |00000000  &ffffffff
 11:  00000800  |000007ff  |00000000  &ffffffff
 12:  00001000  |00000fff  |00000000  &ffffffff
 13:  00002000  |00001fff  |00000000  &ffffffff
 14:  00004000  |00003fff  |00000000  &ffffffff
 15:  00008000  |00007fff  |00000000  &ffffffff
 16:  00010000  |0000ffff  |00000000  &ffffffff
 17:  00020000  |0001ffff  |00000000  &ffffffff
 18:  00040000  |0003ffff  |00000000  &ffffffff
 19:  00080000  |0007ffff  |00000000  &ffffffff
 20:  00100000  |000fffff  |00000000  &ffffffff
 21:  00200000  |001fffff  |00000000  &ffffffff
 22:  00400000  |003fffff  |00000000  &ffffffff
 23:  00800000  |007fffff  |00000000  &ffffffff
 24:  01000000  |00ffffff  |00000000  &ffffffff
 25:  02000000  |01ffffff  |00000000  &ffffffff
 26:  04000000  |03ffffff  |00000000  &ffffffff
 27:  08000000  |07ffffff  |00000000  &ffffffff
 28:  10000000  |0fffffff  |00000000  &ffffffff
 29:  20000000  |1fffffff  |00000000  &ffffffff
 30:  40000000  |3fffffff  |00000000  &ffffffff
 31:  80000000  |7fffffff  |00000000  &ffffffff

Zum n >= 32, sollten alle Bits gesetzt sein und der “OR ffffffff”-Schritt bewerkstelligt dies unabhängig davon, was der vorherige Schritt getan haben mag. Das n <= 0 step ist dann auch ein noop mit AND ffffffff.

  n:   1 << n    (1<<n)-1   n >= 32     n <= 0
---------------------------------------------
 32:  00000001  |00000000  |ffffffff  &ffffffff
 33:  00000002  |00000001  |ffffffff  &ffffffff
 34:  00000004  |00000003  |ffffffff  &ffffffff

  • Ihr Fall für n >= 32 hilft nicht, da die vorherige ungeprüfte Verschiebung noch durchgeführt wird, und wenn n >= sizeof(TheType) * CHAR_BITdas ist UB, das nicht davon beeinflusst wird, ob Sie versuchen, es nachträglich zu überkleben.

    – Unterstrich_d

    31. Oktober 2017 um 0:06 Uhr

  • Vielleicht sollte “Ziele” auch “lesbarer/verständlicher Code” enthalten 🙂

    – paxdiablo

    31. Oktober 2017 um 1:12 Uhr

  • @paxdiablo Ja, deshalb habe ich die Ziele angegeben: um anzuzeigen, dass lesbarer Code NICHT das Ziel war. 🙂

    – Harvey

    31. Oktober 2017 um 13:11 Uhr

  • @Harvey Undefiniertes Verhalten vergiftet das gesamte Programm, sobald Sie es aufrufen. Sie können es nicht “korrigieren”, indem Sie es nachträglich überschreiben. Und es bedeutet absolut nichts, wenn Sie diesen Code finden das passiert um beim Testen die richtigen Ergebnisse zu erzielen, denn das UB bedeutet, dass dies nicht der Fall ist erforderlich zu und kann jederzeit brechen (bei Portierung auf eine andere Architektur, einen anderen Compiler, eine andere Standardrevision usw.). Code, der UB aufruft, ist schlecht und sollte nicht als Empfehlung gepostet werden, und das ist das Ende.

    – Unterstrich_d

    31. Oktober 2017 um 13:38 Uhr


  • @Harvey Da gibt es nichts zu widersprechen. Der Buchstabe des Standards besagt, dass das Verhalten des gesamten Programms undefiniert ist, sobald UB auftritt. Es spielt keine Rolle, ob Sie dann etwas tun, das das Objekt vollständig ersetzt; UB ist aufgetreten und kann nicht rückgängig gemacht werden. Sicher, praktisch wird wahrscheinlich nichts schief gehen, da Compiler es sich nicht zur Aufgabe machen, Ihr Leben zu ruinieren. Aber es ist formal ein schlecht geformter Code, also ist es unverantwortlich, ihn als in Ordnung darzustellen.

    – Unterstrich_d

    31. Oktober 2017 um 14:07 Uhr

1093340cookie-checkSetze die letzten „n“ Bits in unsigned int

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy