binäre Quersumme einer Zahl ermitteln

Gast #156774
Lesenswert?

Hallo zusammen,

ich möchte die Quersumme, also die Anzahl von Einsen in einem Byte
ermitteln. Gibt's dafür eine einfachere Möglichkeit als die Bits zu
zählen?

01110000b --> 3
10010101b --> 4

Für so eine kurze Zahl gehts ja noch, aber wenn die Variablen größer
werden, z.B. uint32_t oder gar uint64_t kann das zusammenzählen eine
ganze Weile dauern.

Bisherige Lösung:
Zahl=0xaa;
for (i=0x01, z=0; i; i=i<<1)
  if (i&Zahl)
    z++;
Gast #156775
Lesenswert?

Schnellste Lösung, wenn Du Platz im Flash übrig hast:
Ein Flash-Array mit der Anzahl der gesetzten Bits des Array-Index
anlegen. Also z.B.:

/* Index =             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
..
uint8_t bitanz[256] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
..

Und dann zugreifen mit:

  z = bitanz[Zahl];

bei WINAVR natürlich mit der entsprechenden PROGMEM-Konstruktion.

Viele Grüße, Stefan
Gast #156778
Lesenswert?

Als Denkansatz - bei einem 8bit µC kann eine wiederholte 8bit Variante
sinnvoller sein.

int bitcount(unsigned long n)
{
    /* works for 32-bit numbers only    */
    /* fix last line for 64-bit numbers */

    unsigned long tmp;

    tmp = n - ((n >> 1) & 033333333333)
        - ((n >> 2) & 011111111111);
    return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}
Gast #156779
Lesenswert?

Das war die falsche Version, Divisionen sind bei µCs nicht so gut.
Besser:

int bitcount32 (unsigned long w)
{
   w = (0x55555555 & w) + (0x55555555 & (w >> 1));
   w = (0x33333333 & w) + (0x33333333 & (w >> 2));
   w = (0x0f0f0f0f & w) + (0x0f0f0f0f & (w >> 4));
   w = (0x00ff00ff & w) + (0x00ff00ff & (w >> 8));
   w = (0x0000ffff & w) + (0x0000ffff & (w >>16));
   return w;
}

oder

int bitcount8 (byte w)
{
    w = (byte)(0x55 & w) + (byte)(0x55 & (byte)(w >> 1));
    w = (byte)(0x33 & w) + (byte)(0x33 & (byte)(w >> 2));
    w = (byte)(0x0F & w) + (byte)(0x0F & (byte)(w >> 4));
    return w;
}
Gast #156780
Lesenswert?

Hi

ob das wohl schneller ist als?

UInt8 bitcount8(UInt8 c)
{
  const UInt8 lookup[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
  UInt8 result;

  result = lookup[c&0xF];
  result += lookup[(c>>4)&0xF];

  return result;
}

Für die Quersumme aus einer 32-Bitzahl muß man das ganze dann halt 4
mal aufrufen.

Matthias
Gast #156781
Lesenswert?

nö, nicht schneller und wahrscheinlich braucht es sogar mehr Code als
mit deiner kleinen Lookup Tabelle. Die gleiche Frage habe ich mir
übrigens auch gestellt.

Der Algo ansich ich nicht schlecht, aber nicht alles was auf zb. Intel
CPU's effizient ist ist auch zwangsläufig auch auf AVR's effizient.
Ich würde deine Methode vorziehen.

Gruß Hagen
Gast #156782
Lesenswert?

bitcount32 ist auf AVR sicher nicht ideal, aber bitcount8 ist
ausgesprochen kompakt (die ganzen casts sind nicht umsonst da drin ;-).
Die Lookup Table leidet ausserdem unter gewissen Portierungsproblemen.
Oder an RAM-Speicherplatz - je nachdem ob der Compiler sie auch ohne
brachiale Gewalt ins ROM packt ($$$) oder nicht (GNU).
Gast #156783
Lesenswert?

bitcount8 kann auf AVR's nicht so kompakt sein.

    w = (byte)(0x55 & w) + (byte)(0x55 & (byte)(w >> 1));
    w = (byte)(0x33 & w) + (byte)(0x33 & (byte)(w >> 2));
    w = (byte)(0x0F & w) + (byte)(0x0F & (byte)(w >> 4));
    return w;

6 * AND
3 * LSR
1 * SWAP
3 * ADD
4 * MOV
1 * RET

ca. 18 Worte. Wobei letzte Zeile vereinfacht werden kann

    w = w + (w >> 4) & 0x0F;


UInt8 bitcount8(UInt8 c)
{
  const UInt8 lookup[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
  UInt8 result;

  result = lookup[c&0xF];
  result += lookup[(c>>4)&0xF];

  return result;
}

8 * Worte Lookup
2 * AND
1 * SWAP
1 * ADD
1 * RET
1 * ZH laden
2 * LPM

ca. 16 Worte

so Pi*Daumen. Mein Gefühl sagt mir das die Lookup Methode auf dem AVR
kompakter und schneller ist. Muß man natürlich noch verifizieren.

Gruß Hagen
Gast #156786
Lesenswert?

Danke, danke, danke.

Den ersten Vorschlag von A.K. hab ich nicht ganz verstanden. Den
zweiten find ich genial.

Auch die Lookuptabelle (entweder für Nibbles oder ganze Bytes) ist eine
gute und einfache Möglichkeit.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren