C: Bithack geht nicht

Gast #6442405
Lesenswert?
• ▲
▼
Es geht um eine Popcount-Routine mit Bit-Twiddling. Die funktioniert 
auch, aber nicht mehr, wenn man die Konstanten alle mit dem Suffix UL 
als unsigned long markiert, obwohl das gehen sollte. Gemäß integer 
promotion müßte das IMO ohnehin nach UL (32 bit) promoted werden.

Drei Versionen: die erste ohne Suffixe, die zweite soweit, daß es noch 
geht, die dritte geht nicht mehr. Also wenn man in der Zeile mit der 
Multiplikation nicht nur U, sondern UL hinzufügt (egal zu welcher der 
Konstanten), scheitert es mit verkehrtem Ergebnis.

Getestet mit GCC 9.3.0 64 Bit unter Linux, keine Warnungen erschienen.
1
/*
2
  compile with: gcc -O0 -std=c99 -Wall -Wextra
3
  see Bit Twiddling Hacks:
4
  https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
5
*/
6

7
#include <stdint.h>
8
#include <stdio.h>
9

10
/*works*/
11
uint32_t popcnt_1(uint32_t v) {
12
    v = v - ((v >> 1) & 0x55555555);
13
    v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
14
    return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
15
}
16

17
/*works*/
18
uint32_t popcnt_2(uint32_t v) {
19
    v = v - ((v >> 1) & 0x55555555UL);
20
    v = (v & 0x33333333UL) + ((v >> 2) & 0x33333333UL);
21
    return (((v + (v >> 4)) & 0xF0F0F0FU) * 0x1010101U) >> 24;
22
}
23

24
/*wrong result*/
25
uint32_t popcnt_3(uint32_t v) {
26
    v = v - ((v >> 1) & 0x55555555UL);
27
    v = (v & 0x33333333UL) + ((v >> 2) & 0x33333333UL);
28
    return (((v + (v >> 4)) & 0xF0F0F0FUL) * 0x1010101UL) >> 24;
29
}
30

31
int main (void) {
32
    printf("1: 0xFF00 ones: %u\n", popcnt_1(0xFF00UL)); /*result: 8*/
33
    printf("2: 0xFF00 ones: %u\n", popcnt_2(0xFF00UL)); /*result: 8*/
34
    printf("3: 0xFF00 ones: %u\n", popcnt_3(0xFF00UL)); /*result: 2056*/
35
    return 0;
36
}
Gast #6442428
Lesenswert?
• ▲
▼
Egon D. schrieb:
> Nop schrieb:
>
>> UL (32 bit)
>
> ???
>
>> Getestet mit GCC 9.3.0 64 Bit unter Linux,
> .........................^^^^^^

Ah, danke. Mir war nicht klar, daß "long" 64 Bit hat unter einem 
64-Bit-Linux. Der Code kam von einem Windowssystem mit 64 Bit, wo "long" 
nur 32 Bit hat.
Gast #6442563
Lesenswert?
• ▲
▼
Rolf M. schrieb:
> Wenn's ein uint32_t sein soll, nimmt man am besten
> UINT32_C(0x55555555).
> Dann macht der Präprozessor den auf dem System passenden Suffix draus.

Auch ein interessanter Tip, gerade für solche Bithacks.

Wobei einfach nur U als Suffix ja im Normalfall ohnehin passend promoten 
sollte, selbst wenn man auf einem System mit int als 16 Bit ist, da 
müßte das dann nach unsigned long gehen. Also, auch ohne daß es wie hier 
zusammen mit einem uint32_t in einem Ausdruck steht.
Gast #6442804
Lesenswert?
• ▲
▼
Nop schrieb:
> Rolf M. schrieb:
>> Wenn's ein uint32_t sein soll, nimmt man am besten
>> UINT32_C(0x55555555).
>> Dann macht der Präprozessor den auf dem System passenden Suffix draus.
>
> Auch ein interessanter Tip, gerade für solche Bithacks.

Allgemein sind die Makros in <stdint.h> einen Blick wert. Manche sind 
zwar richtig hässlich, aber wenn mans brauch ist man trotzdem froh.
#6442988
Lesenswert?
• ▲
▼
Nop schrieb:
> Das mache ich schon, wenn eine entsprechende Build-Option mitgegeben
> wurde. Das Bitgeschubse ist der Fallback, wenn nicht.

Semi-OT: Wie machst Du die Auswahl, welche Lösung verwendet wird? Bei 
mir sieht das oft unschön aus:
1
    static_inline uint8_t ctz(unsigned int x)
2
    {
3
        #if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
4
            return __builtin_ctz(x);
5
        #else
6
            /* Bitwise binary search */
7
            /* Quelle https://graphics.stanford.edu/~seander/bithacks.html */
8
            unsigned int c = 32; /* c will be the number of zero bits on the right */
9
            x &= -signed(x);
10
            if (x) c--;
11
            if (x & 0x0000FFFF) c -= 16;
12
            if (x & 0x00FF00FF) c -= 8;
13
            if (x & 0x0F0F0F0F) c -= 4;
14
            if (x & 0x33333333) c -= 2;
15
            if (x & 0x55555555) c -= 1;
16
            return c;
17
        #endif
18
    }
Gast #6443226
Lesenswert?
• ▲
▼
Walter T. schrieb:

> Semi-OT: Wie machst Du die Auswahl, welche Lösung verwendet wird?

Ich verwende die portable Lösung als Default, wenn kein entsprechendes 
define über die Buildchain gesetzt ist. Die Auto-Detection wie in Deinem 
Beispiel nehme ich nicht, weil ich das manuell kontrollieren will.

Das brauche ich z.B., wenn ich im Mockup auf dem Host teste, der popcnt 
unterstützt, aber die spätere embedded-Anwendung auf einer Plattform 
laufen soll, die das nicht tut. Zugleich bekomme ich eine Vorstellung, 
wieviel Geschwindigkeitsgewinn in der konkreten Anwendung denn über 
natives popcnt drin wäre.

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.