Wie kann ich sicher Durchschnitt zweier unsigned ints in C++?
Unter Verwendung der integer-Mathematik allein, ich würde gerne "sicher" Durchschnitt zweier unsigned ints in C++.
Was ich meine "sicher" ist die Vermeidung von overflows (und alles andere, was gedacht werden kann).
Zum Beispiel, im Durchschnitt 200 und 5000 leicht:
unsigned int a = 200;
unsigned int b = 5000;
unsigned int average = (a + b) / 2; //Equals: 2600 as intended
Aber im Falle von 4294967295 und 5000, dann:
unsigned int a = 4294967295;
unsigned int b = 5000;
unsigned int average = (a + b) / 2; //Equals: 2499 instead of 2147486147
die beste, Die ich mir ausgedacht habe ist:
unsigned int a = 4294967295;
unsigned int b = 5000;
unsigned int average = (a / 2) + (b / 2); //Equals: 2147486147 as expected
Gibt es bessere Möglichkeiten?
- Kann nicht, die du wirkst, die Summe zu
long long
? - Die Dritte option wird die falsche Antwort geben, wenn beide a und b sind ungerade, da es Runde, die sich beide Hälften).
- US-patent mit der Nummer 6,007,232. Die Berechnung der Mittelwert von zwei integer-zahlen, die gerundet in Richtung null in einer einzigen Instruktion-Zyklus: google.com/patents?id=eAIYAAAAEBAJ&dq=6007232 im wesentlichen verwendet
return (a >> 1) + (b >> 1) + (a & b & 0x1);
- ...wow. Ich bin speichern, der link für das nächste mal, wenn jemand beschwert sich über software-Patente.
- es ist interessant, wie viele Antworten enthalten diese patentierte Lösung. Ich bin sicher, dass die meisten/alle von Ihnen entwickelten es selbständig, vielleicht sogar auf der Stelle für Ihre Antwort. Das würde scheinen, zu zeigen das patent nicht erfüllen die Norm der nicht-Offensichtlichkeit.
- dies ist ein hardware-patent (beachten Sie, dass das Ergebnis produziert wird in einem Taktzyklus)
- Ich bin mir nicht sicher, das ist eine echte Auszeichnung. Der code @ArunSaha schrieb wird die CPU geworden, die Schaltung im patent beschriebene. Es kann sogar Arbeit in einem instruction-Zyklus auf einem modernen x86, aber ich bin mir nicht sicher. Unabhängig davon, daß C++ - code könnte trivial geändert in den VHDL-code, und dann ist es hardware...
- York: sagen Sie, ops Antwort nicht funktioniert? er weiß. Wenn Ihr reden ArunSaha Kommentar oder sellibitze beantworten, dann haben Sie vergessen, die
+ (a & b & 0x1)
Teil.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ihre Letzte Ansatz scheint vielversprechend. Sie können verbessern, indem manuell in den niedrigsten bits von a und b:
Dieser gibt die richtigen Ergebnisse in den Fall, dass beide a und b sind ungerade.
BEARBEITEN
Hier ist eine Verwandte Artikel: http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html
high - low
unterzeichnet werden, so kann dies leicht overlow in der gleichen Weise wie das ursprüngliche problem. Sie können dies vermeiden, nur durch die Berücksichtigung dieser Unterschied nicht, so müssen Sie wissen, welches größer ist.int
ist die gleiche wie die Größe des Zeigers, so dass Sie benötigen eine spezielle Maschine für diese Art von überlauf, mit riesigen Adressraum und kleine ints.int
werden immer noch 32 bit. Bitte Lesen Sie den Artikel sorgfältig durch, bevor Sie starke Bemerkungen über Sie.Ihre Methode ist nicht korrekt, wenn beide zahlen sind ungerade zB 5 und 7, der Durchschnitt ist 6, aber Ihre Methode #3 gibt 5.
Versuchen Sie dies:
mit mathematischen Operatoren nur:
(a >> (1 + b) >> (1 + a)) & b & 1
. (Dein zweites Beispiel ist richtig, jedoch).Wenn Sie don T Geist ein wenig x86 inline Assembler (GNU C-syntax), können Sie die Vorteile der supercat ' s Vorschlag, drehen-mit-carry nach add zu setzen die hohen 32-bits des vollständigen 33-bit-Ergebnis in ein register.
Natürlich, Sie in der Regel sollte dagegen, mit inline-asm, weil es Niederlagen, einige Optimierungen (https://gcc.gnu.org/wiki/DontUseInlineAsm). Aber hier gehen wir sowieso:
Den die
%
- Modifikator, um dem compiler die Argumente sind im kommutativen nicht wirklich helfen, bessere asm in der Fall ich habe versucht, den Aufruf der Funktion mit y als eine Konstante oder pointer-deref (memory-operand). Vermutlich mit einem passenden constraint für ein output-operand Niederlagen, da kann man es nicht verwenden, die Lesen und schreiben von Operanden.Wie Sie sehen können auf der Godbolt compiler explorer, diese korrekt kompiliert, und hat so eine version, wo wir ändern Sie den Operanden zu
unsigned long
mit den gleichen inline-asm. clang3.9 macht ein Chaos, obwohl, und entscheidet sich für die"m"
option für die"rme"
Einschränkung, so speichert es im Speicher und verwendet ein Speicher-operand.RCR-by-one ist nicht zu langsam, aber es ist immer noch 3 uops auf Skylake, mit 2-Takt-Latenz. Es ist toll, auf AMD-CPUs, wo RCR verfügt über Einzel-Zyklus-Latenz. (Quelle: Agner Fog-Anweisung Tabellen, siehe auch die x86 - Tags, wiki für x86-performance-links). Es ist immer noch besser als @sellibitze version, aber schlimmer als @Sheldon ' s um-abhängige version. (Siehe code auf Godbolt)
Aber denken Sie daran, dass inline-asm Niederlagen Optimierungen wie constant-propagation, also alle reinen C++ - version wird besser sein in diesem Fall.
x
undy
abgeholt werden.cdecl
(Standard für C-und nicht-Mitglied C++ - Funktionen), die Sie vielleicht nachschlagen möchten, wenn Sie mehr Informationen wünschen.add %eax, %eax; rcr %eax
gültig).x = foo();
bevor die asm-Anweisung, kompilieren für 32-bit und Optimierung mit -O3, und Sie sollten es mit derx
bereits in EAX als[y]
/[res]
Operanden.Und die richtige Antwort ist...
Was Sie haben, ist in Ordnung, mit der Kleinigkeit, dass es wird behaupten, dass der Durchschnitt von 3 und 3 ist 2. Ich vermute, dass Sie das nicht wollen; glücklicherweise gibt es eine einfache Lösung:
Diese nur Beulen, die Durchschnittliche back-up im Falle, dass beide Bereiche wurden abgeschnitten.
Wenn der code für ein integriertes Mikro, und wenn die Geschwindigkeit ist entscheidend, assembly Sprache kann nützlich sein. Auf viele mikrocontroller, der das Ergebnis der Beurteilung würde natürlich gehen Sie in das carry-flag, und Anweisungen existieren, um verschieben Sie Sie zurück in ein register. Auf einem ARM, der Durchschnittliche Betrieb (source und dest. in Register) kann man in zwei Anweisungen; jede C-Sprache entspricht, würde wahrscheinlich Rendite, die mindestens 5, und wohl ein gutes Stück mehr als das.
Übrigens, auf Maschinen mit kürzeren word Größen, die Unterschiede werden noch deutlicher. Auf einem 8-bit-PIC-18-Serie, die durchschnittlich zwei 32-bit-zahlen dauern würde, zwölf Anweisungen. Dabei die Schichten, Beurteilung und Korrektur in Anspruch nehmen würden, 5 Anweisungen für jede Schicht, acht für das hinzufügen und acht für die Korrektur, so 26 (nicht ganz 2,5 x Unterschied, aber wahrscheinlich mehr Bedeutung in absoluten zahlen).
(((a&b << 1) + (a^b)) >> 1)
ist auch ein schöner Weg.Courtesy: http://www.ragestorm.net/blogs/?p=29
(a&b)+((a^b)>>1)
.erwartet avg == 5.0 für diesen test