Rundungsfehler ?

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

Rundungsfehler ?

Beitrag von Mathias »

Wieso wird bei x.5 einmal aufgerundet, und das andere mal abgerundet ?

Folgender Code,

Code: Alles auswählen

var
  i: Integer;
  x: single;
begin
  x := 0.5;
  for i:= 0 to 10 do begin
    WriteLn('Input:', x: 10: 5, ' round: ', Round(x));
    x := x + 1.0;
  end;
  WriteLn();
end;

hat folgende Ausgabe:

Code: Alles auswählen

Input:   0.50000 round:   0
Input:   1.50000 round:   2
Input:   2.50000 round:   2
Input:   3.50000 round:   4
Input:   4.50000 round:   4
Input:   5.50000 round:   6
Input:   6.50000 round:   6
Input:   7.50000 round:   8
Input:   8.50000 round:   8
Input:   9.50000 round:  10
Input:  10.50000 round:  10


Wen ich x := 0.500001; einfüge, dann wird richtig gerundet.
Wen ich anstelle von Single Double nehme, habe ich den gleichen Fehler.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Rundungsfehler ?

Beitrag von m.fuchs »

Einen Blick ins Handbuch kann man schon mal werfen.
https://www.freepascal.org/docs-html/rtl/system/round.html
In the case of .5, the algorithm uses "banker's rounding": .5 values are always rounded towards the even number.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Rundungsfehler ?

Beitrag von MacWomble »

m.fuchs hat geschrieben:Einen Blick ins Handbuch kann man schon mal werfen.
https://www.freepascal.org/docs-html/rtl/system/round.html
In the case of .5, the algorithm uses "banker's rounding": .5 values are always rounded towards the even number.


Ich bezweifle, dass diese Regelung für eine Programmiersprache irgendeinen Sinn ergibt. Auch ich würde eine 'gewohnte' Rundung erwarten und nicht einen Spezialfall. Solche Sachen bringen einen in FPC wirklich zum Verzweifeln. :shock:
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

mischi
Beiträge: 206
Registriert: Di 10. Nov 2009, 18:49
OS, Lazarus, FPC: macOS, 10.13, lazarus 1.8.x, fpc 3.0.x
CPU-Target: 32Bit/64bit

Re: Rundungsfehler ?

Beitrag von mischi »

MacWomble hat geschrieben:Ich bezweifle, dass diese Regelung für eine Programmiersprache irgendeinen Sinn ergibt. Auch ich würde eine 'gewohnte' Rundung erwarten und nicht einen Spezialfall. Solche Sachen bringen einen in FPC wirklich zum Verzweifeln. :shock:

.Net round macht das gleiche. IEEE 754 lautet auch so (https://en.wikipedia.org/wiki/Rounding# ... lf_to_even). So viel ich weiss, ist mit Bankers rounding die Summer der Mehrwertsteuer gleiche der Mehrwertsteuer der Summe bis auf einen Cent.
MiSchi macht die fink-Pakete

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Rundungsfehler ?

Beitrag von siro »

Ohje, das finde ich auch katastrophal,
aber in Delphi 6 ist das leider auch so. Grad nochmal probiert round 9.5 und round 10.5 gibt tatäschlich 10
Ich hatte schon recht früh meine eigene Rundungsfunktion geschrieben, nicht ohne Grund..... :cry:
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Rundungsfehler ?

Beitrag von theo »


Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Rundungsfehler ?

Beitrag von m.fuchs »

Verstehe den Unmut nicht. Das ist kein Spezialfall, sondern (wie von mischi schon erwähnt) einfach standardkonform.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

indianer-frank
Beiträge: 134
Registriert: So 30. Nov 2008, 21:53

Re: Rundungsfehler ?

Beitrag von indianer-frank »

Die meisten Rechnungen würden ungenauer, da die Fehlerschranke für Floatingpoint-Operationen sich verdoppelt. Außerdem gibt es systematische Instabilitäten. Das folgende Programm addiert und subtrahiert 1e-100 zu 1. Das Ergebnis ist eine Katastrophe

Code: Alles auswählen

program t_rmf;
 
uses
  math;
 
const
  Max = 10000000;
  Max10 = Max div 10;
var
  x,y,d: double;
  dummy: TFPURoundingMode;
  i: longint;
begin
  d := 1e-100;
  writeln('Round to Nearest Even');
  dummy := SetRoundMode(rmNearest);
  x := 1;
  for i:=0 to Max do begin
    y := x + d;
    x := y - d;
    if i mod Max10 = 0 then writeln(i:12, x:30:15);
  end;
  writeln('Round up');
  SetRoundMode(rmUp);
  x := 1;
  for i:=0 to Max do begin
    y := x + d;
    x := y - d;
    if i mod Max10 = 0 then writeln(i:12, x:30:15);
  end;
end.
 

Mit den Ergebnissen

Code: Alles auswählen

Round to Nearest Even
           0             1.000000000000000
     1000000             1.000000000000000
     2000000             1.000000000000000
     3000000             1.000000000000000
     4000000             1.000000000000000
     5000000             1.000000000000000
     6000000             1.000000000000000
     7000000             1.000000000000000
     8000000             1.000000000000000
     9000000             1.000000000000000
    10000000             1.000000000000000
Round up
           0             1.000000000000000
     1000000             1.000000000222045
     2000000             1.000000000444089
     3000000             1.000000000666134
     4000000             1.000000000888179
     5000000             1.000000001110223
     6000000             1.000000001332268
     7000000             1.000000001554313
     8000000             1.000000001776357
     9000000             1.000000001998402
    10000000             1.000000002220446

Als Anregung kann man auch einmal https://stackoverflow.com/questions/452 ... table?rq=1 lesen.

mschnell
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: Rundungsfehler ?

Beitrag von mschnell »

siro hat geschrieben:Ohje, das finde ich auch katastrophal,

????
0.5 ist binär exakt darstellbar. Deshalb ist "round" nicht "natürlich" eindeutig definiert. Es ist also für den Eintelfall hochgradig egal (und man darf sich nicht darauf verlassen), ob round(0,5) 1 oder 0 ergibt. In entsprechenden Anwendungen ist das "banker's rounding" ein guter Kompromiss, in allen anderen in keinem Fall verkehrt.

Ich vermute, der Algorithmus ist bereits von der Hardware vorgegeben.

-Michael

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Rundungsfehler ?

Beitrag von Erwin »

Also wenn man etwas nachdenkt, fällt einem doch auf/ein, dass z.B. bei einfach Dezimalstelle zwischen der Ganzen Zahl und 0,5, jeweils 4 Zahlen sind (1,2,3,4 und 6,7,8,9).
Würde bei 0,5 immer nur in eine Richtung gerundet werden, würde eine Seite (Kunde oder Bank) von der Rundung profitieren.
Somit hat dieses Bank-Runden durchaus seine Richtigkeit/Sinn. Es ist um einiges gerechter.

Danke für den Hinweis, Mathias.
Habe mich damals schon gewundert, warum man bei dem Befehlt round vom Bankerrunden geschrieben wurde. Jetzt weiß ich es. Ärgerlich dass dies im Buch aber nicht genau erklärt wurde, was mit Bankerrundung gemeint ist. Ging bis jetzt auch immer davon aus, dass ab x.5 aufgerundet wird.

Wenn man ab 0,5 generell auf oder ab gerundet haben will, kommt man um einen eigenen aufwendigeren Code nicht herum.
Lazarus 2.2.0 / FP 3.2.4

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Rundungsfehler ?

Beitrag von MacWomble »

OK, so gesehen sehe ich es auch ein. Nur gab es mal eine Zeit, da lehrte man ab .5 aufzurunden. Das hat sich natürlich fest gesetzt.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

mschnell
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: Rundungsfehler ?

Beitrag von mschnell »

Lustig wirds, wenn man nicht sicher sein kann, ob es wirklich genau 0.5 ist:

Code: Alles auswählen

x := 0.5;
x := x / 3;
x := x * 3;
x := round (x);
writeln(x);
 
x := 1.5;
x := x / 3;
x := x * 3;
x := round (x);
writeln(x);


-Michael

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Rundungsfehler ?

Beitrag von siro »

Der Satz von Erwin trifft es sehr gut : "GERECHTER"
"Somit hat dieses Bank-Runden durchaus seine Richtigkeit/Sinn. Es ist um einiges gerechter."
Da stimme ich absolut zu. Ob das nun für die jeweilige Anwendung sinvoller ist, bleibt mal außen vor.

Wenn ich ein Zeichenprogramm habe und mal nach oben und mal nach unten runde, wird es unweigerlich Pixelabweichungen geben,
die abhängig von gerade oder ungerade entstehen. Ich fand es in diesem Falle eher störend.

Code: Alles auswählen

 
  x1:=1.5;
  x2:=4.5;
  xd:=round(x2)-round(x1);   // Breite 2 Pixel ist falsch, es sind 3
 
  x1:=2.5;
  x2:=4.5;
  xd:=round(x2)-round(x1);   // Breite 2 Pixel ist richtig
 
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

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

Re: Rundungsfehler ?

Beitrag von Warf »

Das problem ist nunmal das es kein "richtiges" runden gibt. 0.5 ist genauso nach an 1 wie an 0 dran. Daher wird das Bänker runden verwendet, da wenn man sich alle möglichen .5er zahlen anschaut es genauso viele gibt die auf wie abgerundet werden. Wenn man das ganze finanziell betrachtet würde immer aufrunden dazu führen das auf dauer der Kunde mehr zahlt als eigentlich nötig, immer abrunden wäre wiederum unfäir dem Verkäufer gegenüber da dieser dann immer Geld verlieren würde. Den Kaufpreis Modulo 2 (die Entscheidung ob auf oder abgerundet wird) kann man praktisch als zuvallsvariable sehen Pr(x mod 2 = 1) = 0.5, was bedeutet das bei genung transaktionen über bankmänisches Runden der Käufer genauso oft bevorteilt werden sollte wie der Verkäufer.

Es ist nicht intuitiv, aber fair. In den meisten fällen ist es sogar egal. Um ehrlich zu sein (auch wenn ich denke das es sowas wohl geben wird) fällt mir kein Fall ein bei dem es wirklich wichtig wäre

indianer-frank hat geschrieben:Das folgende Programm addiert und subtrahiert 1e-100 zu 1. Das Ergebnis ist eine Katastrophe

Die mantissenlänge bei Extended ist 64 Bit (du verwendest sogar nur den kleinen double typen der noch weniger hat, da kenn ich aber die zahl ned auswendig). Wenn mich nicht alles täuscht dürfte, da 10^100 deutlich größer ist als 2^64, 1-10^-100 rein technisch nicht korrekt berechnet werden können. Wenn du so eine Rechnung mit Double (oder sogar extended) Produktiv verwendest ist das die eigentliche Katastrophe, nicht die Rundung. Bei solchen Größenunterschieden sollte man immer Fixpunktzahlen verwenden

indianer-frank
Beiträge: 134
Registriert: So 30. Nov 2008, 21:53

Re: Rundungsfehler ?

Beitrag von indianer-frank »

Warf hat geschrieben: Wenn mich nicht alles täuscht dürfte, da 10^100 deutlich größer ist als 2^64, 1-10^-100 rein technisch nicht korrekt berechnet werden können. Wenn du so eine Rechnung mit Double (oder sogar extended) Produktiv verwendest

Zwei Bemerkungen. Erstens hat das Null mit exakter Darstellung zu tun. Wenn Du

Code: Alles auswählen

program t_rmf;
uses
  math;
const
  Max = 10000000;
  Max10 = Max div 10;
var
  x,y,d: double;
  db: array[0..7] of byte absolute d;
  dummy: TFPURoundingMode;
  i: longint;
begin
  d := 134217728.0{2^27}
  d := sqr(1/d);        {d=2^(-54)}
  write('d intern: ');
  for i:=0 to 7 do write(db[i]:4);
  writeln;
  writeln('Round to Nearest Even');
  dummy := SetRoundMode(rmNearest);
  x := 1;
  for i:=0 to Max do begin
    y := x + d;
    x := y - d;
    if i mod Max10 = 0 then writeln(i:12, x:30:15);
  end;
  writeln('Round up');
  SetRoundMode(rmUp);
  x := 1;
  for i:=0 to Max do begin
    y := x + d;
    x := y - d;
    if i mod Max10 = 0 then writeln(i:12, x:30:15);
  end;
end.
 
Hier ist d exakt darstellbar, und das Ergebnis ist identisch

Code: Alles auswählen

d intern:    0   0   0   0   0   0 144  60
Round to Nearest Even
           0             1.000000000000000
     1000000             1.000000000000000
     2000000             1.000000000000000
     3000000             1.000000000000000
     4000000             1.000000000000000
     5000000             1.000000000000000
     6000000             1.000000000000000
     7000000             1.000000000000000
     8000000             1.000000000000000
     9000000             1.000000000000000
    10000000             1.000000000000000
Round up
           0             1.000000000000000
     1000000             1.000000000222045
     2000000             1.000000000444089
     3000000             1.000000000666134
     4000000             1.000000000888179
     5000000             1.000000001110223
     6000000             1.000000001332268
     7000000             1.000000001554313
     8000000             1.000000001776357
     9000000             1.000000001998402
    10000000             1.000000002220446
 
Zweitens: Das Problem ist grundsätzlicher Natur, da jede Summe, die nicht exakt darstellbar ist, noch oben gerundet wird. Wenn Du das nicht haben willst, bleibt nicht viel übrig: Hauptsächlich Integer-Addition und ein paar Additionen mit Zweier-Potenzen größer als 2^(-52).

Antworten