Stellen Sie sich vor, ich habe zwei vorzeichenlose Bytes b
und x
. Ich muss bsub
als b - x
und badd
als berechnen b + x
. Ich möchte jedoch nicht, dass während dieser Vorgänge ein Unterlauf / Überlauf auftritt. Zum Beispiel (Pseudocode):
b = 3; x = 5;
bsub = b - x; // bsub must be 0, not 254
und
b = 250; x = 10;
badd = b + x; // badd must be 255, not 4
Der offensichtliche Weg, dies zu tun, umfasst das Verzweigen:
bsub = b - min(b, x);
badd = b + min(255 - b, x);
Ich frage mich nur, ob es bessere Möglichkeiten gibt, dies zu tun, z. B. durch einige hackige Manipulationen?
y ^ ((x ^ y) & -(x < y))
fürint
Typen wirdmin(x, y)
ohne Verzweigung ausgewertet . Dies könnte Teil einer möglichen Lösung sein, basierend auf dem, was Sie bisher haben._mm_adds_epi8
Intrinsic eine Sättigungsaddition von 16 Bytes in einem einzelnen Befehl durch.Antworten:
Der Artikel Branchfree Saturating Arithmetic bietet Strategien dafür:
Ihre Additionslösung ist wie folgt:
geändert für uint8_t:
und ihre Subtraktionslösung ist:
geändert für uint8_t:
quelle
template<class T>struct sat{T t;};
mit überladenen Operatoren sein, die gesättigt sind? Richtige Verwendung von Namespaces. Meistens Zucker.Eine einfache Methode besteht darin, einen Überlauf zu erkennen und den Wert wie folgt entsprechend zurückzusetzen
GCC kann die Überlaufprüfung beim Kompilieren mit -O2 in eine bedingte Zuordnung optimieren.
Ich habe gemessen, wie viel Optimierung im Vergleich zu anderen Lösungen. Bei mehr als 1000000000 Operationen auf meinem PC waren diese Lösung und die von @ShafikYaghmour durchschnittlich 4,2 Sekunden und die von @chux durchschnittlich 4,8 Sekunden. Diese Lösung ist auch besser lesbar.
quelle
Zur Subtraktion:
Zusatz:
Evolution
Dank an @R_Kapp
Dank an @NathanOliver
Diese Übung zeigt den Wert der einfachen Codierung.
quelle
sum
vielleicht(a + b) | -(a <= (255 - b))
?sum = ((a + b) | (!!((a + b) & ~0xFF) * 0xFF)) & 0xFF
, vorausgesetztsizeof(int) > sizeof(unsigned char)
, aber das sieht so komplex aus, dass ich nicht weiß, ob Sie damit etwas gewinnen würden (außer Kopfschmerzen).(a+b+1)*(a <= (255-b)) - 1
.sub
so einfach war wie das Limit0
. Andere Grenzwerte stellen jedoch Komplikationen dar und folgen dem Kommentar von user2079303 .Wenn Sie eine aktuelle genug Version von gcc oder Klirren (vielleicht auch einige andere) verwenden könnten Sie Einbauten erkennen Überlauf.
quelle
Zur Ergänzung:
Zur Subtraktion:
Keine Vergleichsoperatoren oder Multiplikationen erforderlich.
quelle
Wenn Sie bereit sind, Assembly oder Intrinsics zu verwenden, habe ich meiner Meinung nach eine optimale Lösung.
Zur Subtraktion:
Wir können die verwenden
sbb
Anweisung verwendenIn MSVC können wir die intrinsische Funktion _subborrow_u64 verwenden (auch in anderen Bitgrößen verfügbar).
So wird es verwendet:
So können wir es auf Ihre Situation anwenden
Zur Ergänzung:
Wir können die
adcx
Anweisung verwendenIn MSVC können wir die intrinsische Funktion _addcarry_u64 verwenden (auch in anderen Bitgrößen verfügbar).
So wird es verwendet:
So können wir es auf Ihre Situation anwenden
Ich mag dieses nicht so sehr wie das Subtraktions-, aber ich denke, es ist ziemlich geschickt.
Wenn das Hinzufügen überläuft ,
carry_flag = 1
. Wenn Sie nichtscarry_flag
sagen, erhalten Sie 0,!carry_flag * result = 0
wenn also ein Überlauf vorliegt. Und da0 - 1
der vorzeichenlose Integralwert auf sein Maximum gesetzt wird, gibt die Funktion das Ergebnis der Addition zurück, wenn kein Übertrag vorliegt, und das Maximum des gewählten Integralwerts zurück, wenn ein Übertrag vorhanden ist.quelle
was ist damit:
quelle
Alles kann in vorzeichenloser Bytearithmetik erfolgen
quelle
Wenn Sie dies mit zwei Bytes tun möchten, verwenden Sie den einfachsten Code, der möglich ist.
Wenn Sie dies mit 20 Milliarden Bytes tun möchten, überprüfen Sie, welche Vektoranweisungen auf Ihrem Prozessor verfügbar sind und ob sie verwendet werden können. Möglicherweise kann Ihr Prozessor 32 dieser Vorgänge mit einer einzigen Anweisung ausführen.
quelle
Sie können auch die sichere numerische Bibliothek von Boost Library Incubator verwenden . Es bietet Drop-In-Ersatz für int, long usw., die garantieren, dass Sie niemals einen unerkannten Überlauf, Unterlauf usw. erhalten.
quelle
Wenn Sie diese Methoden häufig aufrufen, ist der schnellste Weg nicht die Bitmanipulation, sondern wahrscheinlich eine Nachschlagetabelle. Definieren Sie für jede Operation ein Array mit der Länge 511. Beispiel für Minus (Subtraktion)
Das Array ist statisch und wird nur einmal initialisiert. Jetzt kann Ihre Subtraktion als Inline-Methode oder mithilfe des Pre-Compilers definiert werden:
Wie es funktioniert? Nun, Sie möchten alle möglichen Subtraktionen für vorzeichenlose Zeichen vorberechnen. Die Ergebnisse variieren von -255 bis +255, insgesamt 511 verschiedene Ergebnisse. Wir definieren ein Array aller möglichen Ergebnisse, aber da wir in C nicht über negative Indizes darauf zugreifen können, verwenden wir +255 (in [A-B + 255]). Sie können diese Aktion entfernen, indem Sie einen Zeiger auf die Mitte des Arrays definieren.
benutze es wie:
Beachten Sie, dass die Ausführung extrem schnell ist. Nur eine Subtraktion und eine Zeiger-Deferenz, um das Ergebnis zu erhalten. Keine Verzweigung. Die statischen Arrays sind sehr kurz, sodass sie vollständig in den CPU-Cache geladen werden, um die Berechnung weiter zu beschleunigen
Das Gleiche würde für die Addition funktionieren, jedoch mit einer etwas anderen Tabelle (die ersten 256 Elemente sind die Indizes und die letzten 255 Elemente sind gleich 255, um den Cutoff über 255 hinaus zu emulieren.
Wenn Sie auf einer Bitoperation bestehen, sind die Antworten, die (a> b) verwenden, falsch. Dies kann weiterhin als Verzweigung implementiert werden. Verwenden Sie die Vorzeichen-Bit-Technik
Jetzt können Sie es zur Berechnung der Subtraktion und Addition verwenden.
Wenn Sie die Funktionen max (), min () ohne Verzweigung emulieren möchten, verwenden Sie:
Meine obigen Beispiele verwenden 32-Bit-Ganzzahlen. Sie können es in 64 ändern, obwohl ich glaube, dass 32-Bit-Berechnungen etwas schneller ablaufen. Wie du willst
quelle
(x > y)
ist verzweigt.