2016-06-28 9 views
6

Ich arbeite gerade an einem Projekt, in dem ich Bit-Sets brauche. Ich verwende ein Array von uint64_t für das Bitset.C - BitArray - Setze ein einzelnes Bit von uint64_t

Mein aktuelles Problem ist, dass, wenn ich festlegen möchten oder etwas überprüfen Ich brauche eine Operation wie dies zu tun:

uint64_t index = 42; 
bArr[index/64] |= (((uint64_t)1)<<(index%64)); 

ich die Abteilung neu schreiben kann und Modulo mit einigen cleveren und und Bitshift Operationen als auch, aber ich bin besorgt über die Besetzung von 1. Ich brauche diese Besetzung, da sonst die 1 als 32-Bit-Einheit gesehen wird. Wie in diesem Beispiel gesehen - erhalten Sie falsche Ausgabe ohne Guss:

uint64_t bArr[4];       // 256 bits 
bArr[0] = bArr[1] = bArr[2] = bArr[3] = 0; // Set to 0 

uint64_t i = 255; 
bArr[i/64] = (bArr[i/64] | (((uint64_t)1)<<(i%64))); 

uint32_t i2; 
for (i2 = 0; i2 < 256; i2++) { 
    if ((bArr[i2/64] & (((uint64_t)1)<<(i2%64))) != 0) { 
    printf("bArray[%" PRIu32 "] = 1\n", i2); 
    } 
} 

Kann ich um diese Besetzung in einem cleveren Weg? Ich dachte, dass die Leistung wahrscheinlich von einer Besetzung bei alle lesen/schreiben ...

+0

Do * not * die Division und modulo neu schreiben, um "clever" zu sein; Der Compiler ist sicherlich clever genug, um diese Optimierungen bereits für Sie durchzuführen. Denken Sie auch daran, 'CHAR_BIT * sizeof bArr [0]' anstelle von '64' zu verwenden, um magische Zahlen zu vermeiden. – unwind

+0

@unwind Danke für den Tipp. Ich werde es mit meinem Code testen. Dies ist jedoch wahrscheinlich der Fall. – Matthias

+0

Wenn Sie nach Geschwindigkeit suchen, stellen Sie eine Tabelle "const uint64_t" mit den 64 verschiedenen ULL-Konstanten (1 an allen möglichen Stellen vorverschoben) zur Verfügung und indizieren Sie diese. – tofro

Antwort

4

Der Ergebnistyp von << Operator ist der Typ des linken Operanden (nach integer aktionen), warum Sie den richtigen Typ verwenden müssen: 1 ist vom Typ int aber Sie müssen Typ uint64_t.

können Sie entweder:

(uint64_t) 1 

oder

UINT64_C(1) /* you have to include <stdint.h> */ 

oder

1ULL 

(für die letzten unter der Annahme unsigned long long 64-Bit in Ihrem System, das sehr wahrscheinlich ist,).

Aber sie sind alle gleichwertig: Alle diese Ausdrücke sind Ganzzahlkonstante Ausdrücke und ihr Wert wird zur Kompilierzeit und nicht zur Laufzeit berechnet.

+0

Gut zu wissen, dass dies zur Kompilierzeit geschieht und die Leistung nicht beeinträchtigt (nur den Code hätig) ... – Matthias

+1

Detail 'unsigned long long' ist mindestens 64-bit, also' bArr [index/64] | = 1ULL << (Index% 64); 'wird sicherlich funktionieren. Angenommen, 'unsigned long long' ist 64-bit wird nicht benötigt. – chux

+0

@chux Wird es brechen, wenn 'unsigned long long' größer als 64-bit ist? Ich bin mir nicht ganz sicher, was passiert. Könnten Sie versuchen zu erklären, was ohne Besetzung passiert? Ich sah die falsche Ausgabe und ging davon aus, dass es etwas mit der Breite zu tun hatte (und somit die Besetzung erfolgreich ausprobiert hat). Ich würde gerne wissen, warum der Code brach ... – Matthias

1

Wenn ich Sie richtig verstehe, wollen Sie ein Literal 1, das mindestens 64 Bit lang ist. Sie können get this ohne irgendwelche Umwandlungen schreiben, indem Sie 1ull statt nur 1 schreiben. Dies erzeugt ein unsigned long long Literal mit dem Wert 1. Es ist jedoch nicht garantiert, dass der Typ nicht länger als 64 Bits ist. Wenn Sie also davon ausgehen, dass es genau 64 Bits ist, benötigen Sie vielleicht eine Umwandlung.

3

Ein Cast an und für sich wirkt sich nicht auf die Leistung aus. Es ist ein Kompilierzeit-Konstrukt, das dem Compiler den Typ eines Ausdrucks mitteilt.

In diesem Fall handelt es sich um Integer-Casts und -Ausdrücke, daher sollte es keine Auswirkungen auf die Leistung geben.

+0

Oh, du wieder :) So wie du dich wahrscheinlich erinnern wirst, ist das ziemlich dein Code (nur mit 'uint64_t'). Wissen Sie auch, welche Größe wahrscheinlich am besten für das Array 'uint64_t',' uint32_t', 'uint16_t' oder sogar' uint8_t' zu verwenden ist? Ich habe L1/L2 Cachegröße in meinem Kopf, aber weiß nicht viel darüber ... – Matthias

+0

* eine Besetzung [...] ist ein Kompilierzeit Konstrukt, das dem Compiler den Typ eines Ausdrucks sagt * es ist nicht richtig zu sagen, eine Besetzung ist ein Kompilierzeit Konstrukt, wenn es in den meisten Fällen nicht ist. – ouah

3

C bieten Makros dafür, die die Integer-Konstante auf den Typ int_leastN_t erweitern.

INTN_C(value) 
UINTN_C(value) 

Beispiel

#include <stdint.h>. 
bArr[index/64] |= UINT64_C(1) << (index%64); 

In allgemeinen es ist besser, Gießen zu vermeiden. Manchmal Casting unerwartet machen den Ausdruck schmäler als erhofft.


Nutzen von UINT64_C: uint_least_64_t/int_least_64_t Typen muss vorhanden sein (C99). int64_t/uint64_t sind optional.

+0

Schönes Makro, danke für den Tipp :) In diesem Fall sollte Casting in Ordnung sein, da ich immer an 'uin64_t' arbeite, oder? – Matthias

+0

Gibt es einen Vorteil bei der Wahl von 'INT64_C' gegenüber einem Cast zu' (int64_t) '? – a3f

+1

@ a3f Antwort bearbeitet, um einen Nutzen zu adressieren. Wenn N> = 'unsigned/int' width ist und' (u) intN_t' existieren, verhalten sich 'INTN_C()' und casting '(intN_t)' sicher ähnlich. – chux

Verwandte Themen