Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Trunc Fehler ??? (https://www.delphipraxis.net/187057-trunc-fehler.html)

Kraisel 24. Okt 2015 13:13

Trunc Fehler ???
 
Hallo,

Handelt es sich bei Trunc um einen Fehler, oder gibt es eine gute Erklärung:

Delphi-Quellcode:
procedure Test;
var a, b, e1, e2, e3, e4: Double; // mit Single immer OK ???
begin
  a := 0.1;

  b := 1 / a;
  e1 := Trunc (b);     // e1 = 10 OK

  e2 := Trunc (1 / a); // e2 = 9 falsch

  e3 := Trunc (RoundTo(1 / a, -14)); // e3 = 10 OK
  e4 := Trunc (RoundTo(1 / a, -15)); // e4 = 9 falsch

  Assert (e1 = 10);
  Assert (e2 = 10); // Abbruch durch Assertion
  Assert (e3 = 10);
  Assert (e4 = 10); // Abbruch durch Assertion
end;
Natürlich ist mir klar, dass es Rundungsfehler gibt und Trunc diese dann ungünstig abschneiden kann. So ist z.B. (5 - 2 x Resolution) ja 4 und nicht 5. In Test erkennt man, dass der Rundungsfehler zwischen der 14 und 15 Stelle hinter dem Komma entsteht. Soweit ja OK.

Aber wieso ist e1 richtig und e2 falsch, da hier doch lediglich eine lokale Kopie eines berechneten Wertes anstatt der berechnete Wert selber genutzt wird? Und warum funktioniert das alles bei Single einwandfrei?

Vielen Dank.

Mikkey 24. Okt 2015 16:01

AW: Trunc Fehler ???
 
Vermutung: Bei der Auswertung des Ausdrucks bei e2 wird nicht mit double gerechnet sondern mit Extended.

Generell ist es nicht anzuraten, Fließkommawerte mit = zu vergleichen, das gilt für sämtliche Sprachen, nicht nur für Delphi. Nur der Vergleich mit 0 oder 1 funktioniert im Allgemeinen (die müssen in einem Fließkomma-System exakt dargestellt werden).

Bei C# wird bei einem ==-Operator auf einen Fließkommawert eine Warnung ausgegeben (edit: bei C# heißt der Operator natürlich "==").

SMO 24. Okt 2015 16:50

AW: Trunc Fehler ???
 
Fließkommazahlen haben leider eine begrenzte Genauigkeit.

Der Wert 0,1 kann nicht exakt gespeichert werden. Als Double ist es ungefähr 0,100000000000000006, als Single ca. 0,100000001490116119.

Unter 32 Bit benutzt Delphi die x87 FPU für Berechnungen von Fließkommazahlen. Die arbeitet intern mit "Extended"-Werten (80 Bit; Double ist 64 Bit und Single nur 32 Bit).

Der Befehl "b := 1 / a;" macht also folgendes:
  1. Konstante 1.0 laden (ist exakt)
  2. Wert aus Variable "a" laden und von Double nach Extended kovertieren
  3. Rechnung durchführen (1.0 / Extended(a))
  4. Das Ergebnis ist ungefähr 9,9999999999999994 und liegt natürlich als Extended vor.
  5. Das Ergebnis passt nicht in "b", muss also nach Double konvertiert werden. Dabei wird gerundet! Und deshalb steht im Double "b" dann 10 (ein Wert, der exakt speicherbar ist).

Damit sollte klar sein, was anders läuft, wenn du "Trunc(1 / a)" aufrufst: der Wert bleibt Extended und wird nicht nach Double gerundet. Und Trunc(9,9999999999999994) ist nun mal 9, nicht 10.

Unter Delphi 64 Bit können die Ergebnisse übrigens wieder leicht anders aussehen, da hier SSE-Befehle zur Berechnungen von Fließkommazahlen verwendet werden. Extended gibt es da nicht mehr, nur noch Double und Single. Delphi benutzt dort für Zwischenwerte standardmäßig Double, selbst wenn man nur mit Single rechnet. Dieses Verhalten kann man mit der Direktive {$EXCESSPRECISION OFF} abschalten. Was natürlich auch wieder leicht andere Ergebnisse verursachen kann.

Wie Mikkey sagte, sollte man Fließkommazahlen nicht direkt per "=" vergleichen. In der "Math" Unit gibt es dafür "SameValue" und "CompareValue".

Bei mir in XE6 liefert dein Test diese Ergebnisse für e1, e2, e3, e4:
Code:
32 Bit, Single:  10; 9; 9; 9
32 Bit, Double:  10; 9; 10; 9
32 Bit, Extended: 10; 10; 10; 10

64 Bit, Single:  10; 9; 9; 9
64 Bit, Single*: 10; 10; 10; 10  *{$EXCESSPRECISION OFF}
64 Bit, Double:  10; 10; 10; 10

Kraisel 24. Okt 2015 17:55

AW: Trunc Fehler ???
 
Danke,

ja ... ich hatte übersehen, dass Trunc intern immer mit Extended arbeitet. Damit ist nun alles klar.

Natürlich ist auch klar, dass man Fließkommazahlen nicht mit " = ", sondern mit SameValue vergleichen sollte. Meine Assert - Methoden dienten nur dem Schnelltest, vor allem, da die Werte in meinem Beispiel ja immer ganzzahlig sind, und somit exakt in allen Formaten intern abgebildet werden können. Dann gehts schon.

himitsu 25. Okt 2015 08:38

AW: Trunc Fehler ???
 
Ein 1/10 ist in einem Binärsystem nunmal nicht unbedingt ganzzahlig und somit geht es einfach nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:03 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