Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Int und Frac schlecht bei Nachkommavergleichen (https://www.delphipraxis.net/322-int-und-frac-schlecht-bei-nachkommavergleichen.html)

Phoenix 3. Jul 2002 10:05


Int und Frac schlecht bei Nachkommavergleichen
 
Hi,

mal keine Frage, sondern ein Hinweis für die Knowledge-Base hier :)

Problem:
Es kann unter Umständen wichtig sein, die Nachkommastelle einer Zahl auf einen Bestimmten Wert abzuprüfen.

Meine Erste Idee war folgendes:

Code:
function CheckFracValue(x, y : Real) : Boolean;
begin
   result := false;
   if ( frac(x) = y ) then
      result := true;
end;
Normalerweise müsste der Aufruf CheckFracValue(200204.02, 0.02) eigentlich true zurückliefern. Tut er aber nicht, da frac(200204.02) nicht 0.02 sondern 0.1999999999... zurückliefert.

Debugging im Assemblercode der native - Funktion von Delphi hat ergeben, das es durch die Gleitkommadarstellung bestimmte Nachkommawerte gibt, die nicht eineindeutig sind. So wird ,02 eben immer als ,0199... dargestellt. Das führt bei einem Vergleich, der auf Bitebene durchgeführt wird eben zu Unterschieden und damit zum falschen Ergebnis.

Die Funktion Frac(x: real) : real liefert deshalb zum Teil nicht unbedingt korrekte oder zumindest nicht unbedingt brauchbare Ergebnisse zurück. Gleiches tritt beim Int(x: real) : real - Aufruf auf, der nur den Vorkommaanteil einer Zahl zurückliefert:

Code:
   assert( Int(200204.02) + 0.02 <> 200204.02 );
liefert beispielsweise vollkommen falsch ein True zurück.

Lösung:
Der Vergleich darf nicht auf der Nachkommaebene, sondern vor dem Komma durchgeführt werden, und zwar nicht mit Gleitkommazahlen sondern anhand von Integerwerten.

Code:
function CheckFracValue(x, y:real; decimals : integer) : boolean;
begin
   result := false;
   if ( round( (Int(x) + y) * decimals) = round( x * decimals)) ) then
      result := true;
end;
Wobei Decimals bei 2 Nachkommastellen 100 ist, bei 3 eben 1000 bzw. bei einer eben 10.

Andere Vorschläge dieses Problem zu lösen sind immer willkommen :)

Bis denne,

Sebastian

jj-guitar 17. Okt 2002 10:44

Die Lösung ist schon OK.

Dass man real-Werte nicht auf Gleichheit prüfen darf, liegt an den Rundungsfehlern, die aufgrund der Speichergrenzen bei Rechenoperationen entstehen.

Hier aber noch meine bevorzugte Lösung:
Code:
function FracEqual(x,y: double; Tolerance : double = 0.000001): boolean
begin
  Result := (Abs(x-y) < Tolerance);
end;
CU, jj


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:10 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz