AGB  ·  Datenschutz  ·  Impressum  







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

Genauigkeit von String to Single Konvertierung

Ein Thema von Harry Stahl · begonnen am 31. Mär 2020 · letzter Beitrag vom 19. Apr 2020
Antwort Antwort
Andreas13

Registriert seit: 14. Okt 2006
Ort: Nürnberg
721 Beiträge
 
Delphi XE5 Professional
 
#1

AW: Genauigkeit von String to Single Konvertierung

  Alt 16. Apr 2020, 21:18
Ich habe absichtlich einige Zeit gewartet, um zu testen, ob die beide Jungs mit ihrer Behauptung recht hätten:
We agree with James Gosling about some things like ...
...
"95% of the folks out there are completely clueless about floating-point."
(James Gosling, 28 Feb. 1998)
(Maybe more than 95% ?: 1 March 1998, W. Kahan)
...
Das ist schon 22 Jahre her. Vielleicht wären heutzutage 99 % treffender...
Andreas
Anscheinend haben sie doch recht gehabt! Offenbar interessiert sich hier so gut wie niemand für die Genauigkeit von numerischen Berechnungen. Schade. Anderseits kann ich gut verstehen, wenn sich Integer-Programmierer nicht für Real-Zahlen interessieren. Jeder spezialisiert sich halt auf ein anderes Gebiet.
Nur für den Fall, daß sich hier zufällig auch mal ein Floating-Point-Interessierter verirrt, hier folgt die Auflösung des „Rätsels“ von # 23:
Die Lösung vo Redeamer:
Das Angebot der "Chaotic Bank Society":
Sie zahlen zunächst e - 1 $ auf Ihr Konto ein, wobei e = 2. 7182818… ist die Basis der natürlichen Logarithmen. Im ersten Jahr nehmen wir 1 $ von Ihrem Konto als Bearbeitungsgebühren. Das zweite Jahr ist besser für Sie: Wir multiplizieren Ihr Kapital mit 2, und wir nehmen 1 $ Bearbeitungsgebühren von Ihrem Konto. Das dritte Jahr ist sogar noch besser: Wir vervielfachen Ihr Kapital durch 3, und wir nehmen immer nur noch 1 $ an Bearbeitungsgebühren. Und so weiter: Im n-ten Jahr wird Ihr Kapital mit n multipliziert, und wir nehmen nur 1 Dollar Gebühren. Interessant, nicht wahr?
Wie hoch wäre Ihr Kapital nach 25 Jahren?
Exakte Lösung: 25!e-sum(i=1;25;25!/i!)=15511210043330985984000000 e - 26652630354867072870693626
Numerische Lösungen:
Currency läuft bei der Berechnung für das 18. Jahr über.
1,5511210043330986E25 Delphi Extended
1,55112100433309861E25 Delphi Double
1,55112100433655498E25 Delphi Real48
1,55112076204817268E25 Delphi Single
1.551121004333098598400000003993872967323020890367 14552103610609810902462093999452735830887464108224 0595856323132650458
… und weitere über 3500 Ziffern
ist leider keine „Erlösung“, denn Deine Formel ist falsch. Darüber hinaus hast Du gemogelt, weil Du nicht einmal Deine eigene Formel mit den verschiedenen Real-Typen berechnet hast, sondern nur Deine allerletzte Operation: 15511210043330985984000000 e - 26652630354867072870693626. Du hast dabei allerdings Deine „Zwischenergebnisse“ per Hand, mittels MatLab, Maple, Mathematica, MPA-Bibliotheken oder auf einem anderen Weg zuvor ausgerechnet und diese Zahlen als Input für die „Berechnung“ benutzt. Wenn man die Genauigkeit verschiedener Real-Typen vergleichen will, muß man schon die gesamte Berechnung damit durchführen, nicht nur einen willkürlichen Schritt. Hättest Du wenigstens Deine eigene (= falsche) Formel 25!e - sum(i=1;25;25!/i!) verwendet, dann wären folgende „Guthaben“ rausgekommen:
Guthaben Single : nach 25 Jahren = ---> Überlauf!
Guthaben Currency: nach 25 Jahren = ---> Überlauf!
Guthaben Real : nach 25 Jahren = ---> Überlauf!
Guthaben Double : nach 25 Jahren = -4.1341454764160250E+0050
Guthaben Extended: nach 25 Jahren = -4.1341454764160244E+0050
Ich denke, dieses Ergebnis brauchen wir nicht weiter zu kommentieren.
Apropos:
Die korrekte Formel für das Guthaben lautet: Guthaben nach n Jahren = n!*((e – 1) – (1/1! + 1/2! + 1/3! … + 1/n!)) .
Und das korrekte Guthaben nach 25 Jahren beträgt genau 0,03993873… $, also knapp 4 Cent.

Die Berechnung des Guthabens im vorliegenden Fall über die Formel mit den Fakultäten ist bereits ein grober Verstoß gegen das „kleine ABC“ der Gleitkomma–Arithmetik & jedweder numerischen Rechenregeln, weil es hier um die Differenzbildung zweier riesengroßer Zahlen handelt. Die Fakultät von 25, also 25! = 15511210043330985984000000 ist eine Zahl mit 26 Ziffern. Dabei verliert unser „Double“ durch arithmetische Rundung „hinten“ bereits 8 bis 9 Ziffern. Selbst bei Extended bußen wir immer noch 6 ..7 Ziffern ein. Wenn davon eine ähnlich große Zahl subtrahiert werden sollen, dann gehen uns genauso viele Nachkommastellen „hinten“ – also genau im Ergebnis – flöten. Kein Wunder, daß hierbei ein ziemlich „ungewöhnliches“ Resultat rauskommt, welches wir getrost in den Papierkorb werfen können. Daher ist dieser Weg über Fakultäten – selbst bei der Verwendung von Multipräzisionsarithmetik – nicht zu empfehlen.
Allerdings geht es bei der Beispiel-Aufgabe um die Demonstration des möglichen verheerenden Effektes winziger Rundungsfehler, die je nach verwendetem Floating-Point-Type Single, Double, Extended bei ca. 1E-8, 1E-15 und 1E-18 liegen, aber sich u. U. beachtlich aufschaukeln können.
Und vor allem, man sollte seine Ergebnisse und Formeln stets gewissenhaft überprüfen und ausgiebig testen, bevor man eine Vielzahl von falschen Ziffern raushaut… Numerische Mathematik bringt einen oft zur Verzweiflung, aber sie macht einen vor allem etwas bescheidener…
Viel einfacher und anschaulicher ist daher folgende For - Schleife:
Delphi-Quellcode:

VAR
  i : Integer;
  Guthaben_s: Single;
  Guthaben_c: Currency;
  Guthaben_r: Real48;
  Guthaben_d: Double;
  Guthaben_x: Extended;

CONST
  Br = 25;
  St = 20;

      Guthaben_s:= exp(1) - 1;
      Guthaben_c:= exp(1) - 1;
      Guthaben_r:= exp(1) - 1;
      Guthaben_d:= exp(1) - 1;
      Guthaben_x:= exp(1) - 1;

      WriteLn('StartGuthaben: e - 1 => Single : ', Guthaben_s:Br:St);
      WriteLn('StartGuthaben: e - 1 => Currency: ', Guthaben_c:Br:St);
      WriteLn('StartGuthaben: e - 1 => Real : ', Guthaben_r:Br:St);
      WriteLn('StartGuthaben: e - 1 => Double : ', Guthaben_d:Br:St);
      WriteLn('StartGuthaben: e - 1 => Extended: ', Guthaben_x:Br:St);
      WriteLn;
            
      For i := 1 To 25 Do
      Begin
        Guthaben_s := Guthaben_s*i - 1;
        // Guthaben_c := Guthaben_c*i - 1; // Überlauf nach 21 Jahren!
        Guthaben_r:= Guthaben_r*i - 1;
        Guthaben_d := Guthaben_d*i - 1;
        Guthaben_x := Guthaben_x*i - 1;
        // WriteLn('Guthaben Double : nach ' + (i).ToString + ' Jahren = ', Guthaben_d:Br:St);
        // WriteLn('Guthaben Extended: nach ' + (i).ToString + ' Jahren = ', Guthaben_x:Br:St);
      End;

      WriteLn;
      WriteLn('Guthaben Single : nach ' + (i-1).ToString + ' Jahren = ', Guthaben_s:Br:St);
      // WriteLn('Guthaben Currency: nach ' + (i-1).ToString + ' Jahren = ', Guthaben_c:Br:St);
      WriteLn('Guthaben Currency: nach ' + (i-1).ToString + ' Jahren = ','---> Überlauf nach 21 Jahren!');
      WriteLn('Guthaben Real : nach ' + (i-1).ToString + ' Jahren = ', Guthaben_r:Br:St);
      WriteLn('Guthaben Double : nach ' + (i-1).ToString + ' Jahren = ', Guthaben_d:Br:St);
      WriteLn('Guthaben Extended: nach ' + (i-1).ToString + ' Jahren = ', Guthaben_x:Br:St);
      WriteLn;
      WriteLn('Guthaben KORREKT : nach 25 Jahren = 0.0399387...');
Obige Berechnung liefert folgende Ergebnisse:

Guthaben Single : nach 25 Jahren = 5.68654735142289E+0017 (= 569 tausend Billionen!)
Guthaben Currency: nach 25 Jahren = ---> Überlauf nach 21 Jahren!
Guthaben Real : nach 25 Jahren = -1.30694632129600E+0013 (= -13 Billionen SCHULDEN!)
Guthaben Double : nach 25 Jahren = 1.20180724741045E+0009 (= 1,20 Milliarden!)
Guthaben Extended: nach 25 Jahren = 1.05291085275662E+0006 (= 1,05 Millionen!)

Guthaben KORREKT : nach 25 Jahren = 0.0399387...

Auch Excel rechnet uns arm:
Guthaben Excel Professional (2016): nach 25 Jahren = -2242373258,57016 (= -2,24 Milliarden SCHULDEN!)
Und je nach verwendetem Taschenrechner bekommen wir weitere recht „interessante“ Resultate.

Für die halbwegs korrekte Berechnung des Guthabens im 25-ten Jahr brauchen wir mindestens 30 wertvolle Ziffern, die Delphi von Haus nicht bietet. Interessant ist dabei, daß alle (korrekten) Zwischenergebnisse im Bereich 1,72 … 0 liegen: Es geht also um ganz „normale“ Alltags-Zahlen. Leider versagen hier alle „eingebauten“ Typen von Delphi. Excel – ein tolles Programm! – rechnet hier u .a. wegen eines winzigen Rundungsfehlers in der 15-ten Stelle bei der Zahl e) etwas falscher als Delphi‘s Double und bekommt für den Kontoinhaber anstelle des „erwarteten“ Milliarden-Guthabens einen beinahe doppelt so hohen Schuldenberg raus...
Dieses einfache Beispiel sollte uns zeigen, daß bei Gleitkomma-Berechnungen stets Vorsicht geboten ist. Selbst zuverlässige, fehlertolerante Algorithmen, Multipräzisions-Arithmetik und ausgiebige Tests sind keine Garantie vor gelegentlichen bösen Überraschungen.

Aber ich denke, es genügt: Schließlich interessiert es hier eh niemanden.
Gruß, Andreas
Grüße, Andreas
Wenn man seinem Nächsten einen steilen Berg hinaufhilft, kommt man selbst dem Gipfel näher. (John C. Cornelius)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Genauigkeit von String to Single Konvertierung

  Alt 16. Apr 2020, 22:03
und dennoch ist alles falsch, denn im Bankenwesen speichert keine Bank massenhaft Nachkommastellen.

4-6 Dezimalstellen und beim Ein-/Auszahlen nur die "normalen" 2 Dezimalstellen.
Wenn man hier also "normal" nach den jeweiligen Iterationen rundet, dann ist es fast egal, welchen Typ man hier verwendet.

Egal was ihr anstellt, es ist unmöglich 1.7182818… € oder was auch immer einzuzahlen.


Diese Berechnung als Beispiel für grundsätzlichen Rundungsprobleme OK, aber mit dem Beispiel an einem Konto ist absolut abwägig.
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (17. Apr 2020 um 02:05 Uhr)
  Mit Zitat antworten Zitat
Jost Riedel

Registriert seit: 25. Mär 2020
5 Beiträge
 
#3

AW: Genauigkeit von String to Single Konvertierung

  Alt 17. Apr 2020, 23:21
Natürlich nicht. Geld, egal welche Währung, sind Integer. Die Unterteilung in Euro und Cent ist Augenwischerei - Euro ist nur eine Bezeichnung für einhundert Cent. Genauso, wie hundert ein anderes Wort für einhundert ist.

Addieren und subtrahieren kann man Geldbeträge mit Integers und einem impliziten Dezimalpunkt (Sorry, Komma). Und wenn man noch Multiplizieren und Dividieren will wie die Banken, dann braucht man 4 implizite Nachkommastellen - so wie z.B. beim Delphi Typ Currency.

Die BCD Darstellung macht nichts wirklich genauer - nur anders. Und BCD stellt nur die Mantisse anders dar. Eigentlich gar nicht erforderlich. Der Exponent und implizierte Basis geben den Takt an - die Mantisse ist nur ein Integer. Aber wenn die implizierte Basis 2 ist, dann ist ein Teilen oder Multiplizieren mit dieser Basis (und das ist ist eine sehr häufige Operation) bei einer binär codierten Mantisse ein simples Shiften. Oder bei einer Basis 10 und einer BCD codierten Mantisse ebenfalls.

Fließkommazahlen haben immer eine recht konstante relative Genauigkeit. Und finanzielle Berechnungen eine absolute (auf einen Pfennig, oder Cent, genaue), und die kann bei kleinen Beträgen relativ schlecht sein.

32 bit floating point braucht man im Prinzip nur für eine Sache: Um die selben spaßig schlechten Ergebnisse zu erhalten wie uralte FORTRAN Programme.
  Mit Zitat antworten Zitat
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#4

AW: Genauigkeit von String to Single Konvertierung

  Alt 18. Apr 2020, 18:24
Andreas13 hat es doch schön ausgeführt, wenn man sich blind auf ein Programm verläßt kann man verlassen sein.
Nachdem ich vor ein paar Jahren "unerklärliche Rundungsfehler" hatte, war klar, das Single oder Double selbst für dreistellige Beträge mit relativ einfachen Prozentbrüchen nicht geeignet ist. Aber es gibt ja Currency.
Bleib die Frage warum diese Krüppeltypen überhaupt existieren, wenn die Genauigkeit auf dem Stand eines Rechenschiebers stehen geblieben ist.

Gruß
K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Genauigkeit von String to Single Konvertierung

  Alt 18. Apr 2020, 23:46
Wenn dir was Besseres einfällt, dann darfst es gern patentieren und reich werden.

Es sind halt Typen, die vielen Ansprüchen gerecht werden müssen
und aufgrund der Größe geht es nicht ohne Kompromisse.
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
Redeemer

Registriert seit: 19. Jan 2009
Ort: Kirchlinteln (LK Verden)
1.116 Beiträge
 
Delphi 2009 Professional
 
#6

AW: Genauigkeit von String to Single Konvertierung

  Alt 19. Apr 2020, 10:15
Dezimale Gleitkommazahlen. Im Prinzip die beiden Komponenten einer in Wissenschaftlicher Notation aufgeschriebener Zahl hintereinander als Integers gespeichert (Exponent ggf. mit Bias statt vorzeichenbehaftetem Integer), nur wäre die Mantisse wohl sinnvollerweise nicht normiert. Der Exponent ist zur Basis 10 zu verstehen und kann deshalb um 2 bis 3 Bits (=lb(10)) gekürzt werden.

Darüber hinaus hast Du gemogelt, weil Du nicht einmal Deine eigene Formel mit den verschiedenen Real-Typen berechnet hast, sondern nur Deine allerletzte Operation: 15511210043330985984000000 e - 26652630354867072870693626.
Nein.
Janni
2005 PE, 2009 PA, XE2 PA
  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 09:34 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz