Fliesskommazahlen und Ungenauigkeiten

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Mathias »

Joh hat geschrieben:
Mi 20. Dez 2023, 09:43
Vor allem wirkt sich SetRoundMode auch auf die Oberfläche aus; die Anordnung der Button / die Schriftgröße etc.
Irgendwie habe ich das Gefühl, das ein riessen Bug in FPC-round steckt.
C kann es ja richtig, somit kann es nicht an der FPU liegen.

Auch das mit dem Bankerrunden ist so eine komische Sache. Round wird in der Praxis eher für Mathe gebraucht und für Ausgaben in einen Canvas und da können extreme Darstellungsfehler entstehen.
Bänker rechnen sowieso nur mit Fliesskomma. Und der rechnet sowieso mit Currency wen es ihm auf jeden Cent darauf kommt.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4895
Registriert: Fr 8. Apr 2011, 09:01

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von wp_xyz »

Mathias hat geschrieben:
Mi 20. Dez 2023, 13:18
Round wird in der Praxis eher für Mathe gebraucht und für Ausgaben in einen Canvas und da können extreme Darstellungsfehler entstehen.
In der Graphik kann durch das Bankers-Rounding der Fehler höchstens 1 Pixel betragen. Wenn das "extrem" ist, ok...

Ehrlich gesagt, in der Mathematik wird kaum gerechnet, und in Physik und den Ingenieurswissenschaften ist Runden verpöhnt, weil es die Genauigkeit verschlechtert. Und wenn man mit gerundeten Zahlen weiterrechnet, wird alles viel schlimmer. Runden darf man nur, um das Endergebnis zu präsentieren, also in der Float-zu-String-Umwandlung, und dafür gibt es erprobte Routinen. Insbesondere spielt es dann keine Rolle mehr, wenn die Zahl 0.2 in der internen Darstellung als - ich sag mal - 0.200000000045 behandelt wird.

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Mathias »

In der Graphik kann durch das Bankers-Rounding der Fehler höchstens 1 Pixel betragen. Wenn das "extrem" ist, ok...
Ja, dies ist ein extremer Fehler. Dies ist viel störender, als wen 1 Cent auf dem Konto fehlt.
und den Ingenieurswissenschaften ist Runden verpöhnt, weil es die Genauigkeit verschlechtert. Und wenn man mit gerundeten Zahlen weiterrechnet, wird alles viel schlimmer. Runden darf man nur, um das Endergebnis zu präsentieren, also in der Float-zu-String-Umwandlung,
Dies ist mir schon klar, aber für die Ausgabe auf den Bildschirm muss man halt zum Schluss halt runden. Aber nicht einmal richtig und dann wieder falsch.
Ein Konstrukteur nervt es sicher mehr als den Bänker, wen seine Linien auf dem CAD wie Sägeblätter aussehen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von photor »

wp_xyz hat geschrieben:
Mi 20. Dez 2023, 13:43

...

Ehrlich gesagt, in der Mathematik wird kaum gerechnet, und in Physik und den Ingenieurswissenschaften ist Runden verpöhnt, weil es die Genauigkeit verschlechtert. Und wenn man mit gerundeten Zahlen weiterrechnet, wird alles viel schlimmer. Runden darf man nur, um das Endergebnis zu präsentieren, also in der Float-zu-String-Umwandlung, und dafür gibt es erprobte Routinen. Insbesondere spielt es dann keine Rolle mehr, wenn die Zahl 0.2 in der internen Darstellung als - ich sag mal - 0.200000000045 behandelt wird.
Dicke Off-Topic-Flagge!!!!

Als Physiker, der als Ingenieur arbeitet: das ist so nicht richtig. Die halbe Ingenieurarbeit besteht aus Abschätzen, Vereinfachen, Nachschauen, was welche DIN- oder EN-Richtlinie sagt ...

Gerade als Ingenieur wird es niemanden interessieren, ob dein Ergebnis "0.2" oder "0.20000045" oder auch "0,199999933" heißt. Meist reicht esaber eh, unter (oder über) einem bestimmten "Sicherheitsfaktor" zu bleiben, der von der Richtlinie (und einem eventuellen Zertifizierer) vorgegeben wird.

Das nur Nebenbei!!

Zum eigentlichen Thema ein paar Anmerkungen:
  • egal, welche Zahlendarstellung für "Real"-Zahlen du auch wählst, es wird immer schwierig. Das liegt halt daran, dass dieser Zahlenraum (so sagen die Mathematiker, glaube ich) dicht mit Zahlen besetzt ist; es gibt also zwischen jedem Zahlenpaar, dass du wählst, immer unendlich viele andere.
  • Zur Darstellung der Zahlen hast du im Computer immer nur eine begrenzte Zahl an Bytes/Bits/Stellen zur Verfügung. Das heißt, das kann einfach nicht unendlich dicht sein.
  • man kann jetzt nur versuchen, ein möglichst passendes Zahlenformat zu wählen, so dass die, die du brauchst möglichst genau abgebildet werden. Das können "real", "single", "double", "extended" sein.
  • Das können auch andere spezielle Formate (teils mit variabler Genauigkeit) sein; dann braucht man aber auch spezielle Libraries.
  • In meiner Praxis bin ich schon an einigen Ecken an die Grenzen der Genauigkeit der "normalen Floats" gestoßen (z.B. Quotienten aus sehr kleinen oder sehr großen Zahlen; daraus dann womöglich noch Logarithmus ...) - da muss man schon manchmal genau prüfen, ob das noch sinnvolle Ergebnisse gibt..
Ob das alles bei dem Ausgangsproblem wichtig wird ...

Ciao,
Photor

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Mathias »

Als Physiker, der als Ingenieur arbeitet: das ist so nicht richtig. Die halbe Ingenieurarbeit besteht aus Abschätzen, Vereinfachen, Nachschauen, was welche DIN- oder EN-Richtlinie sagt ...
Die Genauigkeit ist mir beim Rendern nicht so wichtig, was mich nervt, ist das der PC sich nicht einigen kann, ob er bei x.5 auf oder abrunden soll.
Wen er es wenigsten immer gleich machen würde, egal ob die linke Kommaseite ein gerade oder ungerade Zahl ist.
Ich kann mir einfach nicht vorstellen, das die in FPC nicht gehen soll, C kann es ja.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Ich934
Lazarusforum e. V.
Beiträge: 317
Registriert: So 5. Mai 2019, 16:52
OS, Lazarus, FPC: ArchLinux und Windows mit FPCUPdeluxe (L: 2.0.X, FPC 3.2.0)
CPU-Target: x86_64, i386
Wohnort: Bayreuth

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Ich934 »

Mathias hat geschrieben:
Mi 20. Dez 2023, 17:01
Die Genauigkeit ist mir beim Rendern nicht so wichtig, was mich nervt, ist das der PC sich nicht einigen kann, ob er bei x.5 auf oder abrunden soll.
Wen er es wenigsten immer gleich machen würde, egal ob die linke Kommaseite ein gerade oder ungerade Zahl ist.
Ich kann mir einfach nicht vorstellen, das die in FPC nicht gehen soll, C kann es ja.
Was spricht hier gegen ceil oder floor? Dann hast du es immer gleich.
Tipp für PostgreSQL: www.pg-forum.de

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 338
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon (Windows wenn notwendig), Lazarus 3.0 FPC 3.3.1

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Niesi »

Mathias hat geschrieben:
Mi 20. Dez 2023, 17:01
...
Die Genauigkeit ist mir beim Rendern nicht so wichtig, was mich nervt, ist das der PC sich nicht einigen kann, ob er bei x.5 auf oder abrunden soll.
Wen er es wenigsten immer gleich machen würde, egal ob die linke Kommaseite ein gerade oder ungerade Zahl ist.
Ich kann mir einfach nicht vorstellen, das die in FPC nicht gehen soll, C kann es ja.
Der PC - genauer: die CPU - macht exakt das, was die Menschen ihr durch Programmierung aufgetragen haben. Und da scheint mir beim Runden in Free-Pascal einiges nicht in Ordnung zu sein.
wp_xyz hat geschrieben:
Mi 20. Dez 2023, 13:43
Mathias hat geschrieben:
Mi 20. Dez 2023, 13:18
Round wird in der Praxis eher für Mathe gebraucht und für Ausgaben in einen Canvas und da können extreme Darstellungsfehler entstehen.
In der Graphik kann durch das Bankers-Rounding der Fehler höchstens 1 Pixel betragen. Wenn das "extrem" ist, ok...

Ehrlich gesagt, in der Mathematik wird kaum gerechnet, und in Physik und den Ingenieurswissenschaften ist Runden verpöhnt, weil es die Genauigkeit verschlechtert. Und wenn man mit gerundeten Zahlen weiterrechnet, wird alles viel schlimmer. Runden darf man nur, um das Endergebnis zu präsentieren, also in der Float-zu-String-Umwandlung, und dafür gibt es erprobte Routinen. Insbesondere spielt es dann keine Rolle mehr, wenn die Zahl 0.2 in der internen Darstellung als - ich sag mal - 0.200000000045 behandelt wird.
Normalerweise runde ich nicht. Ich brauche das nicht, aber aus Neugier beschäftige ich mich einfach mal damit - es ist sehr seltsam gelöst, besonders seltsam ist der Typ "Currency". https://www.freepascal.org/docs-html/ref/refsu5.html
Der Bezeichnung "Currency" entnehme ich, dass dieser Typ für Währungen verwendet werden soll, richtig? In Free-Pascal wird ja als Standard beim Runden das "Banker-Runden" verwendet, um zu verhindern, dass durch stetiges Aufrunden bei x.5 ein "Drang" nach oben entsteht.

Banker-Runden: bei x.5 wird auf die nächst runde Zahl gerundet:

2.5 ist dann 2
3.5 ist dann 4

So weit so gut - ist für mich nachvollziehbar.

(Anmerkung: Runden mit Round ist ja auf Nachpunktstellen sowieso nur mit Round(aFloat * 10000) / 10000 möglich. Warum eigentlich? Das kann jede Tabellenkalkulation besser.)

Nun erwarte ich, dass beim Banker-Runden auf 3 Nachpunktstellen
2.0025 zu 2.002 wird und
2.0055 zu 2.004 wird.

Wird es aber nicht. Es kommt für den Typ Currency
2.0025 als 2.003 und
2.0035 als 2.004 heraus.

Für den Typ Extended kommt
2.0025 als 2.002 und
2.0035 als 2.003 heraus.

W A R U M ?

Warum wird Banker-Runden ( = Finanzwelt) also NICHT durchgängig für einen Typ benutzt, der eine Währung beschreiben soll??????

Ich kann mir vorstellen, dass Banker auch mal nicht in der Einheit € rechnen, sondern mit der Einheit T€, M€ oder G€ - und da wird es dann interessant.

RoundTo ist nicht besser ...

Ich denke, da ist irgendwie etwas grundsätzlich nicht in Ordnung, oder seht Ihr das anders (und ich habe mich geirrt?)?

Round_2.0035.png
Round_2.0035.png (36.73 KiB) 2680 mal betrachtet
Round_2.0025.png
Round_2.0025.png (36.37 KiB) 2680 mal betrachtet
Dateianhänge
CurrencyCheck.7z
(174.97 KiB) 32-mal heruntergeladen
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von photor »

Mathias hat geschrieben:
Mi 20. Dez 2023, 17:01
Die Genauigkeit ist mir beim Rendern nicht so wichtig, was mich nervt, ist das der PC sich nicht einigen kann, ob er bei x.5 auf oder abrunden soll.
Naja. Die Frage ist halt, was ist x.5 denn genau? x.5000000000000001 oder x.49999999999999 (keine Ahnung, ob diese beiden Zahlen überhaupt darstellbar sind!!).

Aber: Das eine rundet auf, das andere ab. DAS hängt auch von der internen Zahlendarstellung ab. Aber auch davon, wie z.B. das Runden selbst implementiert wird.

Ob C das jetzt anders macht (z.B. die Zahlen schon anders darstellt), kann ich so aus dem Lameng auch nicht sagen. Da müssen "wirklich wissende" ran!

Ciao,
Photor

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Mathias »

Ich934 hat geschrieben:
Mi 20. Dez 2023, 17:47
Mathias hat geschrieben:
Mi 20. Dez 2023, 17:01
Die Genauigkeit ist mir beim Rendern nicht so wichtig, was mich nervt, ist das der PC sich nicht einigen kann, ob er bei x.5 auf oder abrunden soll.
Wen er es wenigsten immer gleich machen würde, egal ob die linke Kommaseite ein gerade oder ungerade Zahl ist.
Ich kann mir einfach nicht vorstellen, das die in FPC nicht gehen soll, C kann es ja.
Was spricht hier gegen ceil oder floor? Dann hast du es immer gleich.
Da kann ich genauso gut Trunc erwenden.
Ist ja alles beschrieben:
https://wiki.freepascal.org/Round/de
Das Beispiel mit den Punkten ist sehr gut dargestellt.
Auch das SetRoundMode Probleme verursachen kann steht auch dort.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4895
Registriert: Fr 8. Apr 2011, 09:01

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von wp_xyz »

photor hat geschrieben:
Mi 20. Dez 2023, 16:42
Als Physiker, der als Ingenieur arbeitet: das ist so nicht richtig. Die halbe Ingenieurarbeit besteht aus Abschätzen, Vereinfachen, Nachschauen, was welche DIN- oder EN-Richtlinie sagt ...

Gerade als Ingenieur wird es niemanden interessieren, ob dein Ergebnis "0.2" oder "0.20000045" oder auch "0,199999933" heißt. Meist reicht esaber eh, unter (oder über) einem bestimmten "Sicherheitsfaktor" zu bleiben, der von der Richtlinie (und einem eventuellen Zertifizierer) vorgegeben wird.
Bin auch Physiker, und du hast mich wahrscheinlich nicht verstanden, weil das Wort "verpöhnt" natürlich irreführend ist. Natürlich muss in einem Bericht das Endergebnlis gerundet werden, je nach erlaubter Toleranz/Größe der Fehlerbalken, und ich weiß noch, wie ich in meiner aktiven Zeit auf die Studenten einreden musste, für das Ergebnis nicht blind alle 10 Stellen vom Taschenrechner abzuschreiben. Aber das betrifft die Float-zu-String-Konvertierung, denn in den Bericht schreibe ich den String "0.2" und nicht die 8 Byte der Double-Precision Zahl 0.2000000045, die der Computer intern verwendet. Was für mich verpöhnt ist, das ist das Round() mit Zahlen, bloß weil einem die vielen Dezimalstellen nicht gefallen, denn dabei wirft man die Dezimalstellen weg und hat ein ungenaues Ergebnis, (und selbst das kann der Computer oft nicht genauso vorstellen, wie sich der unbedarfte User es sich wünscht). Round verwende ich in der Regel nur, wenn ein Float in einen Integer konvertiert werden muss, so wie in der Graphik auf dem Monitor mit ganzzahligen Pixeln. Und da, ganz ehrlich, fällt es in den allermeisten Fällen nicht auf, wenn wegen Bankers-Rounding die Mitte eines Rechtecks um 1 Pixel nach oben rutscht, je nachdem ob die Rechteckhöhe gerade oder ungerade ist.

wp_xyz
Beiträge: 4895
Registriert: Fr 8. Apr 2011, 09:01

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von wp_xyz »

Mathias hat geschrieben:
Mi 20. Dez 2023, 19:18
Ich934 hat geschrieben:
Mi 20. Dez 2023, 17:47
Was spricht hier gegen ceil oder floor? Dann hast du es immer gleich.
Da kann ich genauso gut Trunc erwenden.
ceil, floor, trunc - da heißt es genau aufpassen, wenn die Ausgangszahl wegen Rundungsfehlern ein bisschen vom Integer in der "falschen" Richtung abweicht, hat man im Nu einen Fehler um eine ganze Stelle, und bei negativen Zahlen ist es wieder anders...

Code: Alles auswählen

program Project1;

uses
  Math;

procedure Test(x: Double);
begin
  WriteLn('x=', x, ' --> round(x)=', round(x), ', aber: ceil(x)=', ceil(x), ' floor(x)=', floor(x), ' trunc(x)=', trunc(x));
end;

begin
  Test(1.99999999999);
  Test(2.00000000001);
  Test(-1.99999999999);
  Test(-2.00000000001);

  ReadLn;
end.
----------------------------------
Output:

x= 1.9999999989999999E+000 --> round(x)=2, aber: ceil(x)=2 floor(x)=1 trunc(x)=1
x= 2.0000000000100000E+000 --> round(x)=2, aber: ceil(x)=3 floor(x)=2 trunc(x)=2
x=-1.9999999989999999E+000 --> round(x)=-2, aber: ceil(x)=-1 floor(x)=-2 trunc(x)=-1
x=-2.0000000000100000E+000 --> round(x)=-2, aber: ceil(x)=-2 floor(x)=-3 trunc(x)=-2 
Da ist das "Problem" durch das Bankers-Rounding eine ganz andere Klasse.

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von photor »

wp_xyz hat geschrieben:
Mi 20. Dez 2023, 20:15
Bin auch Physiker, und du hast mich wahrscheinlich nicht verstanden, weil das Wort "verpöhnt" natürlich irreführend ist. Natürlich muss in einem Bericht das Endergebnlis gerundet werden, je nach erlaubter Toleranz/Größe der Fehlerbalken, und ich weiß noch, wie ich in meiner aktiven Zeit auf die Studenten einreden musste, für das Ergebnis nicht blind alle 10 Stellen vom Taschenrechner abzuschreiben. Aber das betrifft die Float-zu-String-Konvertierung, denn in den Bericht schreibe ich den String "0.2" und nicht die 8 Byte der Double-Precision Zahl 0.2000000045, die der Computer intern verwendet.
Das ist wohl das eigentlichen Problem: die Trennung von GUI (= Anzeige) und Daten (= der gespeicherte Wert).

Die Daten sollte man eigentlich immer so lassen wie sie sind bzw. berechnet wurden. Erst wenn man den Wert bracht (Anzeige direkt bzw. hier Dimension des Rechtecks in Pixeln) wird eine Konvertierung fällig. Da muss der User dann entscheiden, was er braucht (aufrunden, abrunden, auf wie viele Stellen denn, die kleine oder große Grenze - z.B. für eine Rechteck, dass etwas sicher abdecken soll).
wp_xyz hat geschrieben:
Mi 20. Dez 2023, 20:15
Was für mich verpöhnt ist, das ist das Round() mit Zahlen, bloß weil einem die vielen Dezimalstellen nicht gefallen, denn dabei wirft man die Dezimalstellen weg und hat ein ungenaues Ergebnis, (und selbst das kann der Computer oft nicht genauso vorstellen, wie sich der unbedarfte User es sich wünscht). Round verwende ich in der Regel nur, wenn ein Float in einen Integer konvertiert werden muss, so wie in der Graphik auf dem Monitor mit ganzzahligen Pixeln. Und da, ganz ehrlich, fällt es in den allermeisten Fällen nicht auf, wenn wegen Bankers-Rounding die Mitte eines Rechtecks um 1 Pixel nach oben rutscht, je nachdem ob die Rechteckhöhe gerade oder ungerade ist.
Richtig. Wenn man Round() nutzt wird wieder ein Float draus, der selbst wieder nur schlecht dargestellt werden kann (siehe mein vorheriger Post). Wenn man schon irgendwie "rundet" (ab- auf-, kaufmännisch, Bankers round ...) dann erst da, wo es gebraucht wird und dann in einem Format, dass das auch so darstellt (z.B. Integer). Sonst fängt man sich Nebeneffekte ein.

Ciao,
Photor

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 338
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon (Windows wenn notwendig), Lazarus 3.0 FPC 3.3.1

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Niesi »

Der Rundungsfehler beim Currency ist wohl künftig behoben - ich wollte gerade einen Bug melden, habe aber noch mal mit FPC 3.3.1 gerundet. Da stimmt es mit dem Runden Extended überein. Daher denke ich, dass ich keinen Bug melden brauche.

Lazarus 2.2.6 mit FPC 3.2.2:

Round(12,5) as Extended = 12 Round(12,5) as Currency = 13
Round(13,5) as Extended = 14 Round(13,5) as Currency = 14

Lazarus 3.99 mit FPC 3.3.1:

Round(12,5) as Extended = 12 Round(12,5) as Currency = 12
Round(13,5) as Extended = 14 Round(13,5) as Currency = 14

Ob das Banker-Runden nun richtig oder falsch ist? Ich weiß nicht so Recht, was ich davon halten soll.
Ich werde dabei bleiben die Rundungsfunktionen nicht zu nutzen. Lediglich bei der Ausgabe von Werten begrenze ich die Dezimalstellen - und zwar so, dass die letzte Stelle unwichtig ist. Für den Millimeterbereich gebe ich mindestens 4 Stellen nach dem Punkt an - die Genauigkeit wird meines Wissens in keiner Fertigung erreicht. Die Variablen selbst werden dabei auf keinen Fall verändert, NUR die Anzeige der Werte ist begrenzt.
photor hat geschrieben:
Do 21. Dez 2023, 10:13

...
Richtig. Wenn man Round() nutzt wird wieder ein Float draus, der selbst wieder nur schlecht dargestellt werden kann (siehe mein vorheriger Post). Wenn man schon irgendwie "rundet" (ab- auf-, kaufmännisch, Bankers round ...) dann erst da, wo es gebraucht wird und dann in einem Format, dass das auch so darstellt (z.B. Integer). Sonst fängt man sich Nebeneffekte ein.

Ciao,
Photor
So sehe ich das auch ...
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Warf »

Der Grund warum C weg von 0 runden kann und Pascal nicht ist das C zwei Rundungsfunktionen besitzt:
round wozu der POSIX Standard das folgende sagt
These functions shall round their argument to the nearest integer value in floating-point format, rounding halfway cases away from zero, regardless of the current rounding direction.
Sowie rint
These functions shall return the integral value (represented as a double) nearest x in the direction of the current rounding mode. The current rounding mode is implementation-defined.

If the current rounding mode rounds toward negative infinity, then rint() shall be equivalent to floor. If the current rounding mode rounds toward positive infinity, then rint() shall be equivalent to ceil. If the current rounding mode rounds towards zero, then rint() shall be equivalent to trunc. [MX] [Option Start] If the current rounding mode rounds towards nearest, then rint() differs from round in that halfway cases are rounded to even rather than away from zero. [Option End]
Der Grund dafür ist, das die FPUs verschiedene Rounding Modes implementieren und damit rint, was das Implementierungsspezifische Runden der FPU ausnutzen kann deutlich effizienter ist als round, was zwar "konsistent" ist, dafür emuliert werden muss.

Beispiel:

Code: Alles auswählen

#include <stdio.h>
#include <math.h>
#include <time.h>

int main()
{
    int acc=0;
    clock_t start = clock();
    for(int i=0;i<100000000; ++i) {
        acc += (int)round(i+0.5);
    }
    clock_t end = clock();
    
    printf("round: %d: %f\n", acc, (float)(end-start)/CLOCKS_PER_SEC);
    
    acc=0;
    start = clock();
    for(int i=0;i<100000000; ++i) {
        acc += (int)rint(i+0.5);
    }
    end = clock();
    
    printf("rint: %d: %f\n", acc, (float)(end-start)/CLOCKS_PER_SEC);

    return 0;
}

Code: Alles auswählen

round: 987459712: 0.669109
rint: 937459712: 0.295144
rint, was die Hardware der FPU voll ausnutzt ist extrem viel effizienter.

Und wie ich immer schreibe wenn dieses Thema aufkommt, Nearest-Even-Rounding (also das was FPC bei Round macht), ist eigentlich in fast allen Situationen die bessere Option, da es im Durchschnitt genauer ist:

Code: Alles auswählen

program Project1;

{$mode objfpc}{$H+}

uses
  sysutils, math;

function RoundAway(d: Double): Integer;
begin
  if d < 0 then
    Result := trunc(d-0.5)
  else
    Result := trunc(d+0.5);
end;

function RoundError(Iters: Integer): Double;
var
  Sum, rnd: Double;
  RoundedSum, i: Int64;
begin
  Sum := 0;
  RoundedSum := 0;
  for i:=1 to Iters do
  begin
    rnd := Random(100)/10;
    Sum += rnd;
    RoundedSum += Round(rnd);
  end;
  Result := Sum - RoundedSum;
end;   

function RoundAwayError(Iters: Integer): Double;
var
  Sum, rnd: Double;
  RoundedSum, i: Int64;
begin
  Sum := 0;
  RoundedSum := 0;
  for i:=1 to Iters do
  begin
    rnd := Random(100)/10;
    Sum += rnd;
    RoundedSum += RoundAway(rnd);
  end;
  Result := Sum - RoundedSum;
end;

begin
  Randomize;   
  WriteLn('RoundError(1000): ', RoundError(1000).ToString);
  WriteLn('RoundError(10000): ', RoundError(10000).ToString);
  WriteLn('RoundError(100000): ', RoundError(100000).ToString);
  WriteLn('RoundAwayError(1000): ', RoundAwayError(1000).ToString);
  WriteLn('RoundAwayError(10000): ', RoundAwayError(10000).ToString);
  WriteLn('RoundAwayError(100000): ', RoundAwayError(100000).ToString);    
end. 
Ergebnis:

Code: Alles auswählen

RoundError(1000): 11,7000000000062
RoundError(10000): -19,199999999837
RoundError(100000): -8,19999999884749
RoundAwayError(1000): -55,8000000000002
RoundAwayError(10000): -507,600000000035
RoundAwayError(100000): -5056,80000000255
Der Rundungsfehler beim Away-From-0 Rounding (also das "Schulrunden") ist nicht nur deutlich größer, sondern akkumuliert sich über mehrere Berechnungen. Nearest-Even-Rounding der FPU hingegen hat im durchschnitt einen sehr kleinen Fehler der auch egal wie viele Operationen man durchführt nicht zunimmt.
wp_xyz hat geschrieben:
Mi 20. Dez 2023, 20:15
Round verwende ich in der Regel nur, wenn ein Float in einen Integer konvertiert werden muss, so wie in der Graphik auf dem Monitor mit ganzzahligen Pixeln. Und da, ganz ehrlich, fällt es in den allermeisten Fällen nicht auf, wenn wegen Bankers-Rounding die Mitte eines Rechtecks um 1 Pixel nach oben rutscht, je nachdem ob die Rechteckhöhe gerade oder ungerade ist.
Da muss man aber auch dazu sagen das das mehr eine Limitation des Canvas in Lazarus ist als alles andere. Moderne Grafikbibliotheken unterstützen Subpixel rendering, womit man Problemlos auch halbe Pixel ansteuern kann ohne das man Hässliche Sprünge oder Knicks hat. Zum Beispiel das HTML5 Canvas das Browser unterstützen:

Code: Alles auswählen

<canvas id="myCanvas" width="200" height="100" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML canvas tag.</canvas>

<script>
var pos = 0;
function line() {
  var c = document.getElementById("myCanvas");
  var ctx = c.getContext("2d");
  ctx.clearRect(0,0,c.width,c.height);
  ctx.beginPath();
  ctx.moveTo(pos,0);
  ctx.lineTo(pos,100);
  ctx.stroke();
  pos+=0.5;
  if (pos>c.width) pos=0;
  window.setTimeout(line, 1);
}
line();
</script>
Gibt eine Animation mit einer Linie die sich ganz sauber von links nach rechts bewegt in halbpixel Schritten.

Das Problem ist halt eher das wir mit dem LCL Canvas bei der Technologie von Win32 Forms aus den 90ern Hängen geblieben sind.

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fliesskommazahlen und Ungenauigkeiten

Beitrag von Mathias »

Aber: Das eine rundet auf, das andere ab. DAS hängt auch von der internen Zahlendarstellung ab. Aber auch davon, wie z.B. das Runden selbst implementiert wird.
Ich habe vorhin das Round von pas2js ausprobiert. Dies rundet korrekt, so wie wir es in der Schule gelernt haben.
Auch LibreOffice macht es richtig.
Ich habe immer mehr das Gefühl, das dies ein Bug von FPC ist. Vor allem weil man es nicht mal mit SetRoundMode() richtig umstellen kann.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten