Falls Du mit diesem Minimalschnippsel meinst, ich solle es so umbauen:
|
C-/C++-Quelltext
|
1
|
return ~0ull >> (64 - numBits);
|
Dann hast Du dabei leider nicht bedacht, dass damit das Scheitern nur auf numBits == 0 verschoben wird. Es gibt aber in jedem Fall einen Wert, für den die Funktion scheitert. Ich habe inzwischen gelernt, dass Overflow und Underflow für
unsigned-Typen wohl spezifiziert sind. Mein Problem mit obigem Code ist nicht der Underflow, sondern der Bitshift. Laut zum Beispiel
diesem Link maskiert der x86-Shift die Schubweite. Ein
(bla << 64) ist also identisch zu
(bla << 0), was dann erklären würde, warum obige Funktion für numBits==64 0 ergibt. Anscheinend hat der Visual Studio-Compiler die ganze Zeit zusätzlichen Code eingefügt, um sicherzustellen, dass der Bitshift für Schubweite>63 das erwartete Ergebnis bringt. Interessant.
Meine aktuelle Version nutzt nach ein paar klugen Hinweisen auf dem ZFX nun ein banales Conditional Move, wovon ich mir erhoffe, dass es die Pipeline nicht allzu heftig ausbremst.
|
C-/C++-Quelltext
|
1
|
return (numBits == 0) ? 0 : (~0ull >> (64 - pAnzahl));
|
Alternativ könnte man eine Bitschubserei ohne Bedingung konstruieren, die zumindest bis numBits < 128 noch stabil funktioniert.
|
C-/C++-Quelltext
|
1
|
return (uint64_t( (~numBits & 64) >> 6)) << (numBits & 63)) - uint64_t( 1);
|
Aber es kann gut sein, dass die zusätzlichen Datenabhängigkeiten wieder irgendwelche Pipeline Stalls auslösen... das ist dann Detailkampf, den ich mir nochmal anschaue, wenn das Gesamtprojekt dann soweit ist, dass realistische Datensätze zur Verfügung stehen.