AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Trunc Fehler ???

Ein Thema von Kraisel · begonnen am 24. Okt 2015 · letzter Beitrag vom 25. Okt 2015
Antwort Antwort
Benutzerbild von Kraisel
Kraisel

Registriert seit: 19. Mär 2012
Ort: Bochum-Linden
64 Beiträge
 
Delphi 12 Athens
 
#1

Trunc Fehler ???

  Alt 24. Okt 2015, 14:13
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.
Peter Kaisler
Das einzig Komplizierte ist zu begreifen wie einfach es ist.
  Mit Zitat antworten Zitat
Mikkey

Registriert seit: 5. Aug 2013
265 Beiträge
 
#2

AW: Trunc Fehler ???

  Alt 24. Okt 2015, 17:01
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 "==").
  Mit Zitat antworten Zitat
SMO

Registriert seit: 20. Jul 2005
178 Beiträge
 
Delphi XE6 Professional
 
#3

AW: Trunc Fehler ???

  Alt 24. Okt 2015, 17:50
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

Geändert von SMO (24. Okt 2015 um 18:02 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Kraisel
Kraisel

Registriert seit: 19. Mär 2012
Ort: Bochum-Linden
64 Beiträge
 
Delphi 12 Athens
 
#4

AW: Trunc Fehler ???

  Alt 24. Okt 2015, 18:55
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.
Peter Kaisler
Das einzig Komplizierte ist zu begreifen wie einfach es ist.

Geändert von Kraisel (24. Okt 2015 um 22:43 Uhr) Grund: Trunc anstatt Frac eingefügt
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.063 Beiträge
 
Delphi 12 Athens
 
#5

AW: Trunc Fehler ???

  Alt 25. Okt 2015, 09:38
Ein 1/10 ist in einem Binärsystem nunmal nicht unbedingt ganzzahlig und somit geht es einfach nicht.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:38 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