Lassen Sie x
, y
werden zwei Gleitkommazahlen. Wie kann man den Mittelwert richtig berechnen?
Der naive Weg (x+y)/2
kann zu Überläufen führen, wenn x
und y
sind zu groß. Ich denke 0.5 * x + 0.5 * y
vielleicht besser, aber es geht um zwei Multiplikationen (was vielleicht ineffizient ist), und ich bin nicht sicher, ob es gut genug ist. Gibt es einen besseren Weg?
Eine andere Idee, mit der ich gespielt habe, ist (y/2)(1 + x/y)
wenn x<=y
. Aber auch hier bin ich mir nicht sicher, wie ich das analysieren und beweisen soll, dass es meinen Anforderungen entspricht.
Außerdem brauche ich eine Garantie, dass der berechnete Mittelwert >= min(x,y)
und ist <= max(x,y)
. Wie in der Antwort von Don Hatch ausgeführt , lautet eine bessere Möglichkeit, diese Frage zu stellen: Was ist eine Implementierung des Mittelwerts aus zwei Zahlen, die immer das genaueste Ergebnis liefert? Das heißt, wenn x
und y
sind Gleitkommazahlen, wie wird die Gleitkommazahl berechnet, die am nächsten ist (x+y)/2
? In diesem Fall ist der berechnete Mittelwert automatisch >= min(x,y)
und <= max(x,y)
. Siehe Don Hatchs Antwort für Details.
Hinweis: Meine Priorität ist robuste Genauigkeit. Effizienz ist entbehrlich. Wenn es jedoch viele robuste und genaue Algorithmen gibt, würde ich die effizienteste auswählen.
quelle
Antworten:
Ich denke, Highams Genauigkeit und Stabilität numerischer Algorithmen befassen sich damit, wie man diese Art von Problemen analysieren kann. Siehe Kapitel 2, insbesondere Übung 2.8.
In dieser Antwort möchte ich auf etwas hinweisen, das in Highams Buch nicht wirklich angesprochen wird (es scheint im Übrigen nicht sehr bekannt zu sein). Wenn Sie interessiert sind zu beweisen Eigenschaften von einfachen numerischen Algorithmen wie diese, können Sie die Macht der modernen SMT - Solver (verwenden Satisfiability Modulo Theories ), wie z3 , ein Paket wie die Verwendung von SBV in Haskell. Das ist etwas einfacher als mit Bleistift und Papier.
Angenommen, mir wird , und ich würde gerne wissen, ob0 ≤ x ≤ y x ≤ z ≤ y erfüllt. Der folgende Haskell-Codez= ( x + y) / 2 x ≤ z≤ y
werde mich das automatisch machen lassen . Hierx ≤ f u n ( x , y) ≤ y x , y 0 ≤ x ≤ y
test1 fun
ist der Satz, dass für alle finiten floats x , y mit 0 ≤ x ≤ y .Es läuft über. Angenommen, ich nehme jetzt Ihre andere Formel:z= x /2+y/2
Funktioniert nicht (aufgrund eines allmählichen Unterlaufs: , was möglicherweise nicht intuitiv ist, da alle Arithmetik zur Basis 2 gehört).( x / 2 ) × 2 ≤ x
Versuchen Sie nun :z= x + ( y- x ) / 2
Funktioniert! Dies
Q.E.D.
ist ein Beweis dafür, dass dietest1
Eigenschaft für alle oben definierten Floats gilt.Was ist mit dem gleichen, aber auf (anstelle von 0 ≤ x ≤ y )?x ≤ y 0 ≤ x ≤ y
Okay, wenn überläuft, wie wäre es dann mit z =y- x ?z= x + ( y/ 2-x / 2)
So scheint es, dass unter den Formeln, die ich hier ausprobiert habe, zu funktionieren scheint (auch mit einem Beweis). Der SMT-Solver-Ansatz scheint mir eine viel schnellere Möglichkeit zu sein, Verdacht auf einfache Fließkommaformeln zu äußern, als die Fließkomma-Fehleranalyse mit Bleistift und Papier durchzuführen.x + ( y/ 2-x / 2)
Schließlich steht das Ziel der Genauigkeit und Stabilität häufig im Widerspruch zum Ziel der Leistung. Was die Leistung angeht, sehe ich nicht wirklich, wie Sie es besser machen können als , zumal der Compiler immer noch die Mühe macht, dies in Maschinenanweisungen für Sie zu übersetzen.( x + y) / 2
PS Dies ist alles mit IEEE754-Gleitkomma-Arithmetik mit einfacher Genauigkeit. Ich habex ≤ x + ( y/ 2-x / 2)≤y
SFloat
SDouble
PPS( x + y) / 2
-ffast-math
PPPS Ich wurde ein wenig mitgerissen, als ich mir nur einfache algebraische Ausdrücke ohne Bedingungen ansah . Die Formel von Don Hatch ist streng besser.
quelle
>>> x = -1.; y = 1.+2.**-52; print `2**-53`, `(x+y)/2.`, `x+(y/2.-x/2.)`
Beachten Sie zunächst, dass eine Methode, die in allen Fällen die genaueste Antwort liefert, Ihre erforderliche Bedingung erfüllt. (Beachten Sie, dass ich sage , eine genaueste Antwort eher als die genaueste Antwort, da es kann zwei Gewinner sein.) Beweis: Wenn, im Gegenteil, Sie haben eine genaue as mögliche Antwort , die nicht nicht die erforderliche Bedingung erfüllen, dass bedeutet entweder
answer<min(x,y)<=max(x,y)
(in welchem Fallmin(x,y)
ist eine bessere Antwort, ein Widerspruch) odermin(x,y)<=max(x,y)<answer
(in welchem Fallmax(x,y)
ist eine bessere Antwort, ein Widerspruch).Ich denke, das bedeutet, dass Ihre Frage darauf hinausläuft, eine möglichst genaue Antwort zu finden. Unter der Annahme von IEEE754-Arithmetik schlage ich Folgendes vor:
Mein Argument, dass dies die genaueste Antwort liefert, ist eine etwas langwierige Fallanalyse. Hier geht:
Fall
max(abs(x),abs(y)) >= 1.
:x/2.+y/2.
manipuliert die berechnete Antwort die gleichen Mantissen und gibt daher genau die gleiche Antwort wie die Berechnung von(x+y)/2
würde ergeben, wenn wir erweiterte Exponenten würden, um einen Überlauf zu verhindern. Diese Antwort kann vom Rundungsmodus abhängen, aber in jedem Fall garantiert IEEE754, dass es sich um eine bestmögliche Antwort handelt (aus der Tatsache, dass die berechnetex+y
Näherung an das mathematische x + y garantiert ist und die Division durch 2 genau ist) Fall).Der Unterfall x ist denormalisiert (und so
abs(y)>=1
):answer = x/2. + y/2. = y/2. since abs(x/2.) is so tiny compared to abs(y/2.) = the exact mathematical value of y/2 = a best possible answer.
Der Unterfall y ist denormalisiert (und so
abs(x)>=1
): analog.max(abs(x),abs(y)) < 1.
:x+y
ist entweder nicht-denormalisiert oder denormalisiert-und- "gerade": Obwohl der berechnetex+y
Wert möglicherweise nicht genau ist, wird durch IEEE754 eine bestmögliche Annäherung an das mathematische x + y garantiert. In diesem Fall ist die nachfolgende Division durch 2 im Ausdruck(x+y)/2.
genau, sodass die berechnete Antwort(x+y)/2.
eine bestmögliche Annäherung an das mathematische (x + y) / 2 darstellt.x+y
wird denormiert und „ungerade“: In diesem Fall genau ein von x, y muss auch denormalisierte-und- „odd“ sein , welches die andere von X bedeutet, y mit dem entgegengesetzten Vorzeichen denormalisiert ist, und so die berechnetex+y
IST genau das mathematische x + y, und so(x+y)/2.
wird durch IEEE754 garantiert , dass das berechnete eine bestmögliche Annäherung an das mathematische (x + y) / 2 ist.quelle
Für binäre Gleitkommaformate nach IEEE-754, am Beispiel von
binary64
(doppelte Genauigkeits-) Berechnung , hat S. Boldo formal bewiesen, dass der unten gezeigte einfache Algorithmus den korrekt gerundeten Durchschnitt liefert.Sylvie Boldo, "Formale Überprüfung von Programmen, die den Gleitkomma-Durchschnitt berechnen." In International Conference on Formal Engineering Methods , S. 17-32. Springer, Cham, 2015. ( Entwurf online )
binary64
Dies ergibt den folgenden beispielhaften
ISO-C99
Code:In den jüngsten Nacharbeiten haben S. Boldo und Mitautoren gezeigt, wie die bestmöglichen Ergebnisse für die IEEE-754-Dezimal-Gleitkommaformate erzielt werden können, indem FMA-Operationen (Fused Multiply Add) und eine bekannte Präzisionsmethode verwendet werden. Baustein verdoppeln (TwoSum):
Sylvie Boldo, Florian Faissole und Vincent Tourneur, "Ein formell erprobter Algorithmus zur Berechnung des korrekten Durchschnitts von Gleitkommazahlen." Im 25. IEEE-Symposium für Computerarithmetik (ARITH 25) , Juni 2018, S. 69-75. ( Entwurf online )
quelle
Obwohl dies in Bezug auf die Leistung möglicherweise nicht besonders effizient ist, gibt es eine sehr einfache Möglichkeit, um (1) sicherzustellen, dass keine der Zahlen größer als entweder
x
odery
(keine Überläufe) ist, und (2) den Gleitkommawert so genau wie möglich zu halten möglich (und (3) , als zusätzlicher Bonus, obwohl Subtraktion verwendet wird, werden niemals Werte als negative Zahlen gespeichert.In der Tat, wenn Sie wirklich Genauigkeit anstreben möchten, müssen Sie die Teilung nicht einmal an Ort und Stelle durchführen. Geben Sie einfach die Werte von
min(x, y)
und zurück,difference
die Sie zur Vereinfachung oder späteren Bearbeitung verwenden können.quelle
2,4,9
, es ist nicht dasselbe wie das Mittel von3,9
.x
undy
sind Gleitkomma, Ihre Berechnung erzeugt ein Gleitkomma am nächsten zu(x+y)/2
?In höhere Genauigkeit konvertieren, dort die Werte addieren und zurückkonvertieren.
Bei der höheren Genauigkeit sollte es keinen Überlauf geben, und wenn sich beide im gültigen Gleitkommabereich befinden, sollte die berechnete Zahl auch innerhalb liegen.
Und es sollte dazwischen liegen, im schlimmsten Fall nur die Hälfte der größeren Zahl, wenn die Präzision nicht ausreicht.
quelle
Theoretisch,
x/2
kann durch Subtrahieren von 1 von der Mantisse berechnet werden.Die tatsächliche Implementierung solcher bitweisen Operationen ist jedoch nicht unbedingt einfach, insbesondere wenn Sie das Format Ihrer Gleitkommazahlen nicht kennen.
Wenn Sie dies tun können, wird die gesamte Operation auf 3 Additionen / Subtraktionen reduziert, was eine signifikante Verbesserung darstellen sollte.
quelle
Ich habe nach dem Vorbild von @Roland Heath gedacht, kann mich aber noch nicht dazu äußern.
x/2
kann durch Subtrahieren von 1 vom Exponenten berechnet werden (nicht von der Mantisse, Subtrahieren von 1 von der Mantisse ist Subtrahieren2^(value_of_exponent-length_of_mantissa)
vom Gesamtwert ).Nehmen wir ohne Einschränkung des allgemeinen Falls an
x < y
. (Wennx > y
, benennen Sie die Variablen neu. Wennx = y
,(x+y) / 2
ist das trivial.)(x+y) / 2
inx/2 + y/2
, was durch zwei ganzzahlige Subtraktionen (durch eine vom Exponenten) durchgeführt werden kannx
wird auf machenx/2
kleiner als darstellbar (vorausgesetzt, die Mantisse wird mit einer impliziten führenden 1 dargestellt).x
, verschieben Siex
die Mantisse um eins nach rechts (und addieren Sie gegebenenfalls die implizite führende 1).x
nach rechts entsprechend dem Exponenten vony
.x
wurde vollständig verschoben. Wenn beide Exponenten minimal wären, würden die führenden überlaufen, was in Ordnung ist, da dieser Überlauf wieder zu einem impliziten führenden werden soll.quelle