Berechnung bei double Fehler im letzten Digit.
-
- Beiträge: 34
- Registriert: Fr 8. Jul 2011, 10:45
- OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
- CPU-Target: amd_64 und i386
- Kontaktdaten:
Berechnung bei double Fehler im letzten Digit.
Hallo Forum,
ich habe folgendes Problem:
In einem Lazarusprogramm wird ein double mehrfach multipliziert und dividiert
Das Ergebnis gebe ich als Liste aus.
Mach ich nun das gleiche in Java (ich brauche exakt das gleiche Ergebnis auf meinem Android Gerät)
unterscheidet sich das Ergebnis auf der letzten Nachkommastelle.
Lazarus:
37,5789473684211 * 97,0 = 3645,15789473684
in Java ergibt 3645,15789473684 66
In Taschenrechner 3645,15789473684 67
Hier sieht es aus als würden die überzähligen Stellen (>15 digits) abgeschnitten.
Lazarus:
33,1377990430622 * 107,0 = 3545,74449760766
in Java = 3545,74449760765 6
in Taschenrechenr = 3545,74449760765 54
Hier sieht es aus als würde die letzte Stelle aufgerundet.
Weiss jemand wie in Freepascal die Berechnung stattfindet, so das ich das Verhalten nachprogrammieren kann?
So ganz stimmig scheint es ja nicht zu sein, oder hab ich was übersehen?
Grüße
Rob
ich habe folgendes Problem:
In einem Lazarusprogramm wird ein double mehrfach multipliziert und dividiert
Das Ergebnis gebe ich als Liste aus.
Mach ich nun das gleiche in Java (ich brauche exakt das gleiche Ergebnis auf meinem Android Gerät)
unterscheidet sich das Ergebnis auf der letzten Nachkommastelle.
Lazarus:
37,5789473684211 * 97,0 = 3645,15789473684
in Java ergibt 3645,15789473684 66
In Taschenrechner 3645,15789473684 67
Hier sieht es aus als würden die überzähligen Stellen (>15 digits) abgeschnitten.
Lazarus:
33,1377990430622 * 107,0 = 3545,74449760766
in Java = 3545,74449760765 6
in Taschenrechenr = 3545,74449760765 54
Hier sieht es aus als würde die letzte Stelle aufgerundet.
Weiss jemand wie in Freepascal die Berechnung stattfindet, so das ich das Verhalten nachprogrammieren kann?
So ganz stimmig scheint es ja nicht zu sein, oder hab ich was übersehen?
Grüße
Rob
-
- Lazarusforum e. V.
- Beiträge: 3178
- Registriert: Di 22. Jul 2008, 19:27
- OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
- CPU-Target: 32bit x86 armhf
- Wohnort: Köln
- Kontaktdaten:
Re: Berechnung bei double Fehler im letzten Digit.
Dann nimmt man Ganzzahlarithmetik; bei Fließkommazahlen hast du immer Rundungsfehler. Am besten sagst du uns auch noch, wie du zu der Ausgabe kommst; die kann das Ergebnis nämlich auch noch verfälschen.Rob hat geschrieben:Mach ich nun das gleiche in Java (ich brauche exakt das gleiche Ergebnis auf meinem Android Gerät)
Soweit ich weiß, verlässt sich Free Pascal auf die Gleitkommaeineinheit deines Prozessors. In Java ist das möglicherweise in der JVM versteckt. Wenn du das selbst nachpgrammieren willst, kannst du dir erste Infos unter https://de.wikipedia.org/wiki/Flie%C3%9 ... mmazahl.29 anlesen. Einfacher dürfte es sein dein Programm anständig zu entwerfenRob hat geschrieben:Weiss jemand wie in Freepascal die Berechnung stattfindet, so das ich das Verhalten nachprogrammieren kann?

MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
-
- Beiträge: 34
- Registriert: Fr 8. Jul 2011, 10:45
- OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
- CPU-Target: amd_64 und i386
- Kontaktdaten:
Re: Berechnung bei double Fehler im letzten Digit.
s := FloatToStr(x);Socke hat geschrieben:Dann nimmt man Ganzzahlarithmetik; bei Fließkommazahlen hast du immer Rundungsfehler. Am besten sagst du uns auch noch, wie du zu der Ausgabe kommst; die kann das Ergebnis nämlich auch noch verfälschen.
Gibt es eine Möglichkeit in Lazarus den genauen Inhalt (alle Digits) eines Doubles auszugeben, ohne das das Ergebnis durch die Stringconvertierung verändert wird?
Die scheint aber dann ganz schön Banane zu sein.Socke hat geschrieben:Soweit ich weiß, verlässt sich Free Pascal auf die Gleitkommaeineinheit deines Prozessors.
Ich würde erwarten, das er entweder rundet oder abschneidet, hier hab ich den Eindruck das manchmal gerundet wird und manchmal abgeschnitten.
Leider nein. Das Lazarusprogramm ist gegeben. Ich muss nur auf meinem Android Gerät das gleiche Ergebnis hinbekommen.Socke hat geschrieben:Einfacher dürfte es sein dein Programm anständig zu entwerfen
Das Lazarusprogramm ist nicht als Quellcode verfügbar bzw. kann nicht verändert werden.
Ich habe mit Lazarus nur ein paar Rechnungen nachgestellt um sie mit meinem Android Programm zu vergleichen.
Lazarus auf Android geht ja nicht, oder?
Grüße
Rob
-
- Lazarusforum e. V.
- Beiträge: 3178
- Registriert: Di 22. Jul 2008, 19:27
- OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
- CPU-Target: 32bit x86 armhf
- Wohnort: Köln
- Kontaktdaten:
Re: Berechnung bei double Fehler im letzten Digit.
Die BinärdarstellungRob hat geschrieben:s := FloatToStr(x);
Gibt es eine Möglichkeit in Lazarus den genauen Inhalt (alle Digits) eines Doubles auszugeben, ohne das das Ergebnis durch die Stringconvertierung verändert wird?

Code: Alles auswählen
var d: Double; p: ^QWord; s: String;
begin
p := PQword(@d);
s := binstr(p^, 64);
end;
Abschneiden ist eine Form des Runden. Ich habe gerade gesehen, dass du das Verhalten durchaus steuern kannst: http://www.freepascal.org/docs-html/rtl ... dmode.htmlRob hat geschrieben:Die scheint aber dann ganz schön Banane zu sein.Socke hat geschrieben:Soweit ich weiß, verlässt sich Free Pascal auf die Gleitkommaeineinheit deines Prozessors.
Ich würde erwarten, das er entweder rundet oder abschneidet, hier hab ich den Eindruck das manchmal gerundet wird und manchmal abgeschnitten.
Dann verweise ich nochmal nach oben: bei einer Abweichung auf der letzten Nachkommastelle sind die Zahlen unter Einbeziehung der Fließkommazahlen innewohnenden Eigenschaften identisch.Rob hat geschrieben:Ich habe mit Lazarus nur ein paar Rechnungen nachgestellt um sie mit meinem Android Programm zu vergleichen.
Lazarus auf Android geht ja nicht, oder?
Wie schon angedeutet, kann man dir vielleicht helfen dein übergeordnetes Ziel zu erreichen. Dazu müsstest du etwas genauer erklären, wozu du die exakte Gleichheit der Ergebnisse benötigst.
Lazarus auf Android geht, will aber keiner

MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
-
- Beiträge: 3444
- Registriert: Mo 11. Sep 2006, 10:24
- OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
- CPU-Target: X32 / X64 / ARMv5
- Wohnort: Krefeld
Re: Berechnung bei double Fehler im letzten Digit.
Das ist kein "Fehler", sondern genau das ist die Idee, die hinter Floating-Point steckt: "Rechne so genau wie möglich mit vorgegebenem Verbrauch an Speicher und Rechenzeit" !!!Rob hat geschrieben:So ganz stimmig scheint es ja nicht zu sein, oder hab ich was übersehen?
Mathematisch gesehen gibt es bei jeder Berechnung ein definierbare Genauigkeit von +/- einem von der Hardware und Software Infrastruktur abhängigen Wert.
Werden mehrere Berechnungen hintereinander ausgeführt, wird das Ergebnis immer ungenauer. Gute Programmierer achten darauf. Es gibt jede Menge Anleitungen in informatik-Büchern und Zeitungen, wie man Ketten-Berechnungen so programmiert, dass man das Ergebnis mit einer eine möglichst gute Genauigkeit bekommt. (z-B. "Pivot-Suche" und im Extremfall iterative Methoden statt "geradeaus" zur Auflösung lineare Gleichungssysteme. )
Gleitpunkt Zahlen auf "Gleichheit" zu untersuchen ist von vorne herein Unsinn. Wer diese Aufgabenstellung erfindet hat offensichtlich keine Ahnung.
-Michael
Zuletzt geändert von mschnell am Mo 6. Mai 2013, 10:33, insgesamt 4-mal geändert.
-
- Beiträge: 3444
- Registriert: Mo 11. Sep 2006, 10:24
- OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
- CPU-Target: X32 / X64 / ARMv5
- Wohnort: Krefeld
Re: Berechnung bei double Fehler im letzten Digit.
Das geht quasi nicht. Die Aufgabenstellung ist absoluter Blödsinn: Du kannst das orginale mit Lazarsus erstellte Programm (zumindest den Rechen-Teill ohne User - Interface )auf dem Android Gerät auf zwei verschiedene Arten ans laufen bringen: mit Davlin/JVM und "nativ". Das exakte Gleitpunkt-Ergebnis wird trotzdem nicht dasselbe sein wie auf dem PC, weil die ARM Hardware im Detail anders arbeitetRob hat geschrieben:[ Das Lazarusprogramm ist gegeben. Ich muss nur auf meinem Android Gerät das gleiche Ergebnis hinbekommen.
Einzige Mlöglichkeit: Du programmiers die Algorithmen des PC Gleitpunktprozessors in Software nach. Viel Spaß

-Michael
-
- Beiträge: 2013
- Registriert: Do 16. Okt 2008, 10:22
- OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
- CPU-Target: x86,x64,ARM
Re: Berechnung bei double Fehler im letzten Digit.
Die MSEgui Funktion doubletostring() bringt im mode fsm_default die Werte 3645.15789473685 und 3545.74449760766 , für fsm_fix und precision = 13Rob hat geschrieben: Ich würde erwarten, das er entweder rundet oder abschneidet, hier hab ich den Eindruck das manchmal gerundet wird und manchmal abgeschnitten.
3645.1578947368466 und 3545.7444976076560.
http://gitorious.org/mseide-msegui/msei ... ttostr.pas
Martin
-
- Beiträge: 134
- Registriert: So 30. Nov 2008, 21:53
Re: Berechnung bei double Fehler im letzten Digit.
Die Diskussion ist müßig, solange nicht geklärt ist, woher die Ausgangsdaten mit Nachkommastellen stammen (97 und 107 werden korrekt dargestellt). Nehmen wird einmal an, daß die Zahlen wie angegeben dezimal korrekt sind. Dann ergeben sich folgende Rechnungen:
Der Ausganswert ist 37.5789473684211 als 4-Word Double = ($BCA8,$F286,$CA1A,$4042) = 37.57894736842109750796225853264331817626953125.
Das Produkt der exakten Werte ist 37.5789473684211*97 = 3645.1578947368467,
als Double ($35EF,$D794,$7A50,$40AC) = 3645.15789473684662880259566009044647216796875
Und das Produkt double(37.5789473684211)*97 = 3645.15789473684645827233907766640186309814453125,
als Double = ($35EF,$D794,$7A50,$40AC) = 3645.15789473684662880259566009044647216796875
In diesen Fall wäre also das Java-Ergebis 'korrekt', FPC und Taschenrechner 'falsch' !
Wenn aber zB die 37.5789473684211 'eigentlich' 37.57894736842108 sind, wäre das Freepascal/Lazuarus- Ergebnis völlig korrekt, Java und Taschenrechner falsch, denn:
37.57894736842108*97 = 3645.15789473684476,
als Double ($35EB,$D794,$7A50,$40AC) = 3645.15789473684480981319211423397064208984375
Der Taschenrechner war also beides mal 'falsch', wahrscheinlich kann man aber an den gerundeten Ausgangsdaten schrauben, bis er auch mal 'richtig' rechnet.
Gruß Frank
Der Ausganswert ist 37.5789473684211 als 4-Word Double = ($BCA8,$F286,$CA1A,$4042) = 37.57894736842109750796225853264331817626953125.
Das Produkt der exakten Werte ist 37.5789473684211*97 = 3645.1578947368467,
als Double ($35EF,$D794,$7A50,$40AC) = 3645.15789473684662880259566009044647216796875
Und das Produkt double(37.5789473684211)*97 = 3645.15789473684645827233907766640186309814453125,
als Double = ($35EF,$D794,$7A50,$40AC) = 3645.15789473684662880259566009044647216796875
In diesen Fall wäre also das Java-Ergebis 'korrekt', FPC und Taschenrechner 'falsch' !
Wenn aber zB die 37.5789473684211 'eigentlich' 37.57894736842108 sind, wäre das Freepascal/Lazuarus- Ergebnis völlig korrekt, Java und Taschenrechner falsch, denn:
37.57894736842108*97 = 3645.15789473684476,
als Double ($35EB,$D794,$7A50,$40AC) = 3645.15789473684480981319211423397064208984375
Der Taschenrechner war also beides mal 'falsch', wahrscheinlich kann man aber an den gerundeten Ausgangsdaten schrauben, bis er auch mal 'richtig' rechnet.
Gruß Frank
-
- Beiträge: 34
- Registriert: Fr 8. Jul 2011, 10:45
- OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
- CPU-Target: amd_64 und i386
- Kontaktdaten:
Re: Berechnung bei double Fehler im letzten Digit.
Hallo,
Dividend und Multiplikant sind jeweils Integer.
i: integer;
d: double;
d := 50.0;
i:= 230; //<- ändert sich bei jeder Berechnung im Bereich von 0 - 999
d := d * i; oder d := d / i;
Das Ganze n mal.
Vielleicht komme ich damit weiter.
Ich weiß das es keine gute Idee war hier Double zu verwenden, ich versuche mich gerade in Schadensbegrenzung.
Den Lazarus/Freepascal code kann ich erstmal nicht ändern. (In einem nächsten Release, wenn ich dem Entwickler gut zurede bestimmt).
Grüße
Rob
beginnend von einer Ganzzahl (30.0) werden Divisionen und Multiplikationen durchgeführt.indianer-frank hat geschrieben:Die Diskussion ist müßig, solange nicht geklärt ist, woher die Ausgangsdaten mit Nachkommastellen stammen
Dividend und Multiplikant sind jeweils Integer.
i: integer;
d: double;
d := 50.0;
i:= 230; //<- ändert sich bei jeder Berechnung im Bereich von 0 - 999
d := d * i; oder d := d / i;
Das Ganze n mal.
Wie hast du die Umrechnung von 4Byte Double in die Gleitpunktzahl durchgeführt?indianer-frank hat geschrieben: Dann ergeben sich folgende Rechnungen:
Der Ausganswert ist 37.5789473684211 als 4-Word Double = ($BCA8,$F286,$CA1A,$4042) = 37.57894736842109750796225853264331817626953125.
Vielleicht komme ich damit weiter.
Ich weiß das es keine gute Idee war hier Double zu verwenden, ich versuche mich gerade in Schadensbegrenzung.
Ob richtig oder falsch ist mir erstmal egal. Worum es geht ist, das Ergebnis des Lazarus/Freepascal Codes ru reproduzieren, auch wenn es mathematisch falsch ist.indianer-frank hat geschrieben: In diesen Fall wäre also das Java-Ergebis 'korrekt', FPC und Taschenrechner 'falsch' !
Den Lazarus/Freepascal code kann ich erstmal nicht ändern. (In einem nächsten Release, wenn ich dem Entwickler gut zurede bestimmt).
Genau das versuche ich nun ...indianer-frank hat geschrieben: wahrscheinlich kann man aber an den gerundeten Ausgangsdaten schrauben, bis er auch mal 'richtig' rechnet.
Grüße
Rob
-
- Beiträge: 733
- Registriert: Do 27. Sep 2012, 00:07
- OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
- CPU-Target: x86_64-win64
- Wohnort: Hamburg
Re: Berechnung bei double Fehler im letzten Digit.
Für Konvertierung zu String kannst du die Funktion Format nehmen. Mit Format kann man auch nachkomma Stellen bestimmen. Dann hast du gleiche Ergebnisse wie Taschenrechner:
Format('%.16f', [(37.5789473684211 * 97.0)]) ist gleich 3645.1578947368467000
Format('%.16f', [(33.1377990430622 * 107.0)]) ist gleich 3545.7444976076554000
Wenn man die letzten Stellen bei Java-Ergebnisse schaut kommt es mir vor als ob Java falsch aufrundet.
Nachtrag:
Was merkwürdig ist dass bei mein System Freepascal und Windows-Taschenrechner gleiches Ergebnis liefern und ein Java-Programm und ein Javascript-Program (in Firefox, IE8, Chrome, Opera) anderes Ergebnis und zwar Java-Ergebnis von oben liefern.
Nachtrag 2:
Jetzt habe ich mit C-Programm gestestet, kompiliert mit gcc 4.7.2. Erste Ergebnis ist wie in Java 2.Ergebnis anders:
3645.1578947368466000
3545.7444976076558000
Nachtrag 3:
Jetzt habe ich selber (Hirn
) nachgerechnet Freepascal und Windows-Taschenrechner rechnen richtig alle anderen Falsch oder die C/C++ Biblotheken haben merkwürdige Rundungsregel.
Format('%.16f', [(37.5789473684211 * 97.0)]) ist gleich 3645.1578947368467000
Format('%.16f', [(33.1377990430622 * 107.0)]) ist gleich 3545.7444976076554000
Wenn man die letzten Stellen bei Java-Ergebnisse schaut kommt es mir vor als ob Java falsch aufrundet.
Nachtrag:
Was merkwürdig ist dass bei mein System Freepascal und Windows-Taschenrechner gleiches Ergebnis liefern und ein Java-Programm und ein Javascript-Program (in Firefox, IE8, Chrome, Opera) anderes Ergebnis und zwar Java-Ergebnis von oben liefern.
Nachtrag 2:
Jetzt habe ich mit C-Programm gestestet, kompiliert mit gcc 4.7.2. Erste Ergebnis ist wie in Java 2.Ergebnis anders:
3645.1578947368466000
3545.7444976076558000
Code: Alles auswählen
// gcc -o ztest ztest.c
#include <stdio.h>
int main(void)
{
double d1=(37.5789473684211 * 97.0);
printf("%.16f %s",d1,"\n");
d1=(33.1377990430622 * 107.0);
printf("%.16f %s",d1,"\n");
return 0;
}
Jetzt habe ich selber (Hirn

-
- Beiträge: 3444
- Registriert: Mo 11. Sep 2006, 10:24
- OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
- CPU-Target: X32 / X64 / ARMv5
- Wohnort: Krefeld
Re: Berechnung bei double Fehler im letzten Digit.
Wie gesagt: Bei Gleitpunktzahlen sind solche Effekte völlig normal. Da gibt es kein "richtig" oder "falsch".Soner hat geschrieben:Freepascal und Windows-Taschenrechner rechnen richtig alle anderen Falsch oder die C/C++ Biblotheken haben merkwürdige Rundungsregel.
-Michael
-
- Beiträge: 134
- Registriert: So 30. Nov 2008, 21:53
Re: Berechnung bei double Fehler im letzten Digit.
Ich denke nicht, daß das weiter hilft (eigentlich macht das ja der Kompiler), aber hier ist die Rechnung:Rob hat geschrieben:Wie hast du die Umrechnung von 4Byte Double in die Gleitpunktzahl durchgeführt? Vielleicht komme ich damit weiter.
x = 37.5789473684211 = 0.5871710526315796875*2^6, also double(x) = int(0.5871710526315796875*2^53)/2^53*2^6 = 37.57894736842109750796225853264331817626953125. Hier noch mal als Programm:
Code: Alles auswählen
program test;
uses
math,sysutils;
var
x: double;[code=dos]
i: integer;
m: int64;
begin
x := 37.5789473684211;
for i:=0 to 3 do write(' $',IntToHex(bx,4));
writeln(' = ',x);
m := 5288766667668648;
x := m;
x := ldexp(x,-53)*64;
for i:=0 to 3 do write(' $',IntToHex(bx,4));
writeln(' = ',x);
end. [/code]Der Doublewert von x ist halt die zu x nächste darstellbare Doublezahl, hier die Umgebung von x:
Code: Alles auswählen
x1 = ($BCA7,$F286,$CA1A,$4042) = 37.57894736842109040253490093164145946502685546875
x2 = ($BCA8,$F286,$CA1A,$4042) = 37.57894736842109750796225853264331817626953125
x3 = ($BCA9,$F286,$CA1A,$4042) = 37.57894736842110461338961613364517688751220703125
x-x1 = 9.597465099E-15
x-x2 = 2.492037741E-15
x-x3 =-4.613389616E-15
Zu Deinem Problem: Wenn es nur Multiplikationen wären, könntest Du Dich eventuell mit skalierten Int64 retten, aber bei Divisionen ist das i.d.R. nicht möglich (bedenke daß z.B. 1/10 nicht exakt darstellbar ist). Wenn Du es allerdings unbedingt brauchst (was ich bezweifle), hilt nur Fließkomma-Multipräzisions-Arithmetik (suche nach: MPArith Pascal source code for multi precision floating point arithmetic).
Gruß Frank