Routinen zum Vergleich von Gleitkommawerten
Nach oben zu Routinen für Gleitkommawerte
Inhaltsverzeichnis
Beim Vergleich von Gleitkommazahlen gibt es einige nicht offensichtliche Probleme.
Probleme beim Vergleich von Gleitkommazahlen
Ein Problem beim Vergleich von Gleitkommazahlen ist, dass die angezeigten Werte nicht genau der Repräsentation der Zahlen im Arbeitsspeicher entsprechen. Beispielsweise werden die Zahlen X := 99.9999964
und Y = 100.000003
beide als 100 angezeigt, aber sie sind nicht gleich:
uses
System.SysUtils;
var X, Y: Single;
begin
X := 99.9999964;
Y := 100.000003;
Writeln('X =', FloatToStr(X));
Writeln('Y =', FloatToStr(Y)); ReadLn;
end.
Programmausgabe:
X = 100 Y = 100
Wenn Sie jedoch X mit Y vergleichen, sind die Werte nicht gleich. Ein gegenteiliges Beispiel:
var X:Single;
begin
X:=0.1;
Writeln('X =', FloatToStr(X));
end.
Programmausgabe:
X = 0.100000001490116
Ein anderes Problem hängt mit der endlichen Darstellung von Gleitkommazahlen zusammen. Als Beispiel wird eine Gleitkommazahl mit sich selbst verglichen:
var X:Single;
begin
X:=0.1;
if X=0.1 then
Writeln('Equal')
else
Writeln('Not equal');
ReadLn;
end.
Programmausgabe:
Not equal
Warum wird dieses Ergebnis ausgegeben? Die genaue Zahl 0,1 wird binär als sich unendlich wiederholender Bruch dargestellt 0,0(0011). Die einfache (Single) Genauigkeit X speichert nur 23 Bit für die Mantisse. Delphi führt alle Gleitkommaoperationen mit der Genauigkeit Extended durch. Wenn Delphi die einfache Genauigkeit X in die Extended-Darstellung mit 63 Bit für die Mantisse konvertiert, initialisiert das Programm einfach alle zusätzlichen Bits mit Nullen (nicht mit den tatsächlichen Bits von 0,0(0011)). Weitere Informationen finden Sie unter Auswirkungen der endlichen Genauigkeit.
Im Folgenden wird 0,1 mit einfacher Genauigkeit mit 0,1 mit doppelter Genauigkeit verglichen:
var
X:Single;
Y:Double;
begin
X:=0.1;
Y:=0.1;
if X=Y then
Writeln('Equal')
else
Writeln('Not equal');
ReadLn;
end.
Programmausgabe:
Not equal
Auch hier speichern die einfachen und doppelten Repräsentationen von 0,1 eine unterschiedliche Anzahl von Mantissenbits.
Vergleichsroutinen
Die Lösung, die einen korrekten Vergleich von Gleitkommazahlen ermöglicht, besteht in der Verwendung von kleinen Epsilon-Bereichen. Wenn die Differenz zweier Gleitkommazahlen innerhalb des Epsilon-Bereichs liegt, werden sie als gleich angesehen.
Die Unit System.Math enthält die Funktionen CompareValue, SameValue und IsZero, die den Vergleich von Gleitkommazahlen behandeln. Diese Funktionen haben Deklarationen, wie die folgenden:
function CompareValue(const A, B: Extended; Epsilon: Extended): TValueRelationship;
function SameValue(const A, B: Extended; Epsilon: Extended): Boolean;
function IsZero(const A: Extended; Epsilon: Extended): Boolean;
Hier sind A
und B
zu vergleichende Gleitkommazahlen und Epsilon
ist der kleine Bereich, um den A
und B
abweichen dürfen und trotzdem als gleiche Werte betrachtet werden.
Wenn Epsilon = 0
ist, dann wird implizit ein angemessener Standardwert verwendet. Beispielweise verwendet die Extended-Version von SameValue den Standardwert:
Epsilon = Max(Min(Abs(A), Abs(B)) * 1E-16, 1E-16)