Autor |
Beitrag |
Daniel L.
Beiträge: 140
Erhaltene Danke: 14
W7, W8
TurboD Prof, Delphi Community
|
Verfasst: Mi 20.06.18 19:26
Hi,
durch Fliesskommaberechnungen können ja ungenaue Ergebnisse entstehen.
Wie könnte man erreichen, dass ein nicht exaktes Ergebnis einer Fliesskommaberechung immer oberhalb (oder immer unterhalb) des exakten Wertes liegt - aber so genau wie möglich.
Gibts da was von Delphi?
Sonst müsste man wohl die letzte Nachkommazahl um 1 erhöhen (verringern), bzw. das letzte Mantissebit raufsetzen (runtersetzen) - aber das ist nicht so mein Gebiet
Daniel L.
Moderiert von Th69: Tippfehler in Titel korrigiert.
|
|
Gammatester
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: Mi 20.06.18 20:27
Daniel L. hat folgendes geschrieben : | Hi,
durch Fliesskommaberechnungen können ja ungenaue Ergebnisse entstehen.
Wie könnte man erreichen, dass ein nicht exaktes Ergebnis einer Fliesskommaberechung immer oberhalb (oder immer unterhalb) des exakten Wertes liegt - aber so genau wie möglich.
Gibts da was von Delphi? |
Für einzelne einfache Operation ist das möglich durch Benutzung von docwiki.embarcadero....em.Math.SetRoundMode also zB wie in docwiki.embarcadero....undMode_%28Delphi%29
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| OldRM := GetRoundMode;
SetRoundMode(rmDown); c := a + b;
SetRoundMode(rmUp); c := a + b;
SetRoundMode(OldRM); |
Achtung: Dadurch wird zB bei rmUp 1 + 1e-20 > 1! Und dann: Ich habe keine Ahnung, wie weit diese Monotonie-Eigenschaft für kompliziertere Ausdrück gilt. Außerdem ist dringend zu empfehlen, daß die Roundmode-Änderung möglichst lokal bleibt. Man kann global ändern, aber in der Regel hat man dann größere Fehler, oder wie bei Crocodile Dundee: Man kann damit leben, aber es schmeckt besch...
Daniel L. hat folgendes geschrieben : | Sonst müsste man wohl die letzte Nachkommazahl um 1 erhöhen (verringern), bzw. das letzte Mantissebit raufsetzen (runtersetzen) - aber das ist nicht so mein Gebiet |
In meiner AMath/DAMath-Bibliothek gibt es dafür die Funktionen predx/predd/preds bzw succx/succd/succs. In den Test/Logfiles findest Du auch alle Möglichkeit für +- 1 +- 1e-20 für extended/double/single und alle Modi.
Für diesen Beitrag haben gedankt: icho2099
|
|
Daniel L.
Beiträge: 140
Erhaltene Danke: 14
W7, W8
TurboD Prof, Delphi Community
|
Verfasst: Fr 22.06.18 00:00
...hat ne Weile gebraucht, bis ich kapiert habe, dass nach 'SetRoundMode'
bei einer Gleitkommaoperation das eigentliche Runden selber angestossen werden muss (z.B. mit 'RoundTo').
So muss ich die Stelle, an der gerundet wird, selbst entscheiden.
Danke, auch für Hinweis auf den Mathe-Bibliotheken
Gruß -
Daniel L.
|
|
Gammatester
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: Fr 22.06.18 09:36
Daniel L. hat folgendes geschrieben : | ...hat ne Weile gebraucht, bis ich kapiert habe, dass nach 'SetRoundMode'
bei einer Gleitkommaoperation das eigentliche Runden selber angestossen werden muss (z.B. mit 'RoundTo').
So muss ich die Stelle, an der gerundet wird, selbst entscheiden. |
Nein! Entgegen weit verbreiteten Gerüchten beeinflußt SetRoundMode jede Fließkommaoperation und nicht nur die Funktionen Round/Roundto. Da das Ergebnis von Fließkommaoperationen idR keine darstellbare FPZ ist, wird dann gemäß der eingestellten Rundungsart sofort gerundet.
Mit dem folgenden Programm
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
| {$apptype console} Uses math, sysutils; var a,b,c,d: double; ic: int64 absolute c; id: int64 absolute d; OldRM: TFPURoundingMode; Begin OldRM := GetRoundMode; SetRoundMode(rmNearest); a := 1; b := 1e-100; c := a+b; d := a-b; Writeln('n ',c:20:16, ' ', IntToHex(ic, 16), ' ', d:20:16, ' ', IntToHex(id, 16)); SetRoundMode(rmdown); a := 1; b := 1e-100; c := a+b; d := a-b; Writeln('d ',c:20:16, ' ', IntToHex(ic, 16), ' ', d:20:16, ' ', IntToHex(id, 16)); SetRoundMode(rmup); a := 1; b := 1e-100; c := a+b; d := a-b; Writeln('u ',c:20:16, ' ', IntToHex(ic, 16), ' ', d:20:16, ' ', IntToHex(id, 16)); SetRoundMode(OldRM); End. | kann man mit Delphi-eigenen Bordmitteln sehen
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| C:\TMP>D:\DMX\M6\DCC32 -b rnd2.dpr Borland Delphi Version 14.0 Copyright (c) 1983,2001 Borland Software Corporation rnd2.dpr(32) 33 lines, 0.02 seconds, 30632 bytes code, 3205 bytes data. C:\TMP>rnd2.exe n 1.0000000000000000 3FF0000000000000 1.0000000000000000 3FF0000000000000 d 1.0000000000000000 3FF0000000000000 0.9999999999999999 3FEFFFFFFFFFFFFF u 1.0000000000000002 3FF0000000000001 1.0000000000000000 3FF0000000000000 | D.h. es wird immer sofort gerundet. Bei rmNearest ist das Ergebnis 1 +- 1e-100 natürlich wieder, bei den anderen Modi sieht man die Unterschiede in der Dezimal- und Hex-Darstellung.
Vergleiche auch meine Posts in www.delphipraxis.net...h-obwohl-gleich.html und www.lazarusforum.de/...php?f=10&t=11620 (Indianer-Frank).
Für diesen Beitrag haben gedankt: Daniel L.
|
|
GuaAck
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: Sa 23.06.18 22:20
Hallo Daniel,
das ist ein interessantes Thema. Ich hatte bisher nur zwei Arten von Rechnungen:
a) Fließkomma reicht aus, letzte Unsicherheit wegen Rundung sind ohne Bedeutung.
b) Rundungsfehler sind nicht tragbar: Dann nutze ich Integer mit entsprechenden Skalierungen, so habe ich alles unter Kontrolle, kann z. B. den Rest einer Division für spätere Berücksichtung merken.
Auf welche Art von Problemen bezieht sich Deine Frage?
Gruß GuaAck
|
|
Daniel L.
Beiträge: 140
Erhaltene Danke: 14
W7, W8
TurboD Prof, Delphi Community
|
Verfasst: So 24.06.18 11:46
GuaAck hat folgendes geschrieben : | Auf welche Art von Problemen bezieht sich Deine Frage? |
Rundungsfehler haben in meinem Fall gravierende Folgen:
Sehr verkürzt gesagt:
In einer Funktion steht unter einer Quadratwurzel ein komplizierter Term mit vielen Variablen, welche ihrerseits vorher in mehreren Schritten berechnet wurden.
In bestimmten Situationen ist nun der mathematische Wert unter der Wurzel knapp über 0 oder sogar = 0,
wobei dann wegen Rundungsfehlern ein Wert knapp unter 0 entstehen kann.
Erster Ansatz:
Unter die Wurzel einen kleinen Wert Delta0 aufaddieren (z.B. 0.0001),
aber dieser Wert darf mal garnicht soo klein, um muss mal seehr klein seim (damit die Funktion ausreichend genau bleibt) -
Zweiter Ansatz:
Bei jeder einzelnen FliesskommaOp immer kleinstmöglich nach oben bzw. nach unten runden (daher dieser Thread),
wobei dann immer je nach Vorzeichen unterschieden werden muss, in welche Richtung gerundet werden muss.
Was ich von Gammatester gelernt habe, würde das auch wohl so gehen.
Dritte Ansatz:
Ich hab die Anfangsbedingungen meines Progs so eingeschränkt, daß ich mit einem konstanten Delta0 auskomme.
(Es geht um die Berechnung einer Ellipse aus 5 Punkten(x/y) -
Hatte ich für die 5 Punkte Anfagns x und y als double, so sind sie jetzt Ganzzahl und nach oben beschränkt) -
damit kann ich gut leben
In der besagten Funktion wird natürlich nach wie vor mit Fliesskommma gerechnet.
GuaAck hat folgendes geschrieben : | das ist ein interessantes Thema |
... ja, find ich auch, und ziemlich neu für mich...
Vielen Dank an beide,
Daniel L.
|
|
Gammatester
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: So 24.06.18 12:19
Ein weiterer Ansatz ist, teilweise mit erhöhter Genauigkeit zu rechnen. Das ist zB wichtig, wenn man quadratische Gleichungen lösen will, wo die Eigenschaften wesentlich von Vorzeichen der Diskriminante (d.h. dem Term unter der Quadratwurzel) abhängt, siehe W.Kahan On the Cost of Floating-Point Computation Without Extra-Precise Arithmetic, 2004, www.eecs.berkeley.edu/~wkahan/Qdrtcs.pdf. Das wird inline in meiner AMTools Squad-Funktion benutzt.
Als Prozeduren gibt es diese dbl2 (double-double) und ext2 (double-extended) Arithmetik in AMath/DAMath ( www.wolfgang-ehrhard...ctions.html#dblfloat ), benutzt zB in der Compound-Funktion, die damit den gesamten Argumentbereich mit einem maximalen Fehler von 0.5 ulps (RMS 0.21 ulps) abdeckt.
|
|
|