Routinen zum Vergleich von Gleitkommawerten

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Routinen für Gleitkommawerte


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)

Siehe auch