real auf integer runden

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
oliver2104
Beiträge: 22
Registriert: Sa 26. Dez 2020, 13:22

real auf integer runden

Beitrag von oliver2104 »

Hallo,
ich möchte real Zahlen auf integer runden.
hab mir die Doku angesehen und zu Round() folgendes gefunden:
In the case of .5, the algorithm uses "banker's rounding": .5 values are always rounded towards the even number.

Writeln (Round(2.5)); { Prints 2 (down) }
Writeln (Round(3.5)); { Prints 4 (up) }
Diese Rundungsregel unterscheidet sich von dem, was ich in der Schule gelernt habe.
Was ist "banker's rounding" ?
Hör ich zum ersten Mal !

Benutzeravatar
Ally
Beiträge: 263
Registriert: Do 11. Jun 2009, 09:25
OS, Lazarus, FPC: Win und Lazarus Stable release
CPU-Target: x64

Re: real auf integer runden

Beitrag von Ally »

Hallo,

um "klassisch" zu runden kannst du mal folgendes probieren:

Code: Alles auswählen

function MyRound(X: Double): Int64;
begin
  Result := Trunc((X * 2 + 1) / 2);
end;
Gruß Ally

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

Re: real auf integer runden

Beitrag von theo »

oliver2104 hat geschrieben:
Fr 9. Dez 2022, 14:49
Was ist "banker's rounding" ?
Eigentlich steht es da. Im Zweifel (bei .5) rundet er immer auf die gerade Zahl hin.
https://rounding.to/understanding-the-bankers-rounding/

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: real auf integer runden

Beitrag von Winni »

Hi!

Schneller und unkomplizierter als Allys Lösung:

Code: Alles auswählen

function NonIdiotRound(X: Double): Int64;
begin
  Result := Trunc(X + 0.5);
end;
Winni

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: real auf integer runden

Beitrag von fliegermichl »

Gibt es beim FPC eigentlich eine builtin Lösung um einen Wert auf eine bestimmte Anzahl Stellen zu runden?
Bei Delphi habe ich das so gelöst

Code: Alles auswählen

function  RundeMitXStellen(Wert : Double; Stellen : integer) : Double;
var Multi : cardinal;
    i : integer;
begin
 Multi := 1;
 for i := 1 to Stellen do Multi := Multi * 10;
 Result  := Trunc(Wert * Multi + 1/10*(Stellen +1)) / Multi;
end;
ist aber einigermassen umständlich.

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

Re: real auf integer runden

Beitrag von Warf »

Die Idee hinter bankers rounding ist das man die hälfte aller Zahlen auf und die andere hälfte aller Zahlen abrundet. Grund dafür ist das damit der Rundungsfehler im mittel reduziert wird.

Beispiel:

Code: Alles auswählen

program Project1;

{$mode objfpc}{$H+}

uses SysUtils;

function RoundUp(X: Double): Int64;
begin
  Result := Trunc(X + 0.5);
end;

var
  Numbers: array[0..99] of Double;
  Sum: Int64;
  SumDouble: Double;
  i: Integer;
begin
  Randomize;
  for i:=0 to 99 do
    Numbers[i] := Random(10) + Random(10)*0.1; // Zufällige zahl zwischen 0 und 10 mit einer Nachkommastelle
  Sum := 0;
  SumDouble := 0;
  for i:=0 to 99 do
  begin
    Sum += Round(Numbers[i]);
    SumDouble += Numbers[i];
  end;
  WriteLN('Bankers Rounding Fehler: ', (Sum-SumDouble).ToString);
  Sum := 0;
  for i:=0 to 99 do
    Sum += RoundUp(Numbers[i]);
  WriteLN('Aufrunde Fehler: ', (Sum-SumDouble).ToString);
  ReadLn;
end.
Ein beispiel Ergebnis:

Code: Alles auswählen

Bankers Rounding Fehler: -1,70000000000005
Aufrunde Fehler: 5,29999999999995
Das macht analytisch natürlich auch Sinn, die Wahrscheinlichkeit das .5 bei einer zahl rauskommt ist 10% (10 möglichkeiten für eine kommastelle). Bei 100 zahlen also durchschnittlich bei 10 Zahlen. Wenn man jetzt alle aufrundet ist der Rundungsfehler also +5, da man bei jeder zahl um 0.5 aufrundet. Wenn man jetzt aber die hälfte aufrundet ist der rundungsfehler +2,5, da man 5 stück aufrundet und bei der anderen hälfte -2,5 da dort 5 mal um 0,5 abgerundet wird. In summe gleicht sich dieser Fehler also aus, daher ist bei dem beispiel oben im durchschnitt Bankers Rounding um 5 genauer

Bankers rounding wird daher verwendet weil es im endeffekt genauer ist.
Zuletzt geändert von Warf am Fr 9. Dez 2022, 17:50, insgesamt 2-mal geändert.

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

Re: real auf integer runden

Beitrag von Mathias »

Das Problem hatte ich auch mal:
viewtopic.php?f=10&t=11620&hilit=round
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: real auf integer runden

Beitrag von wp_xyz »

fliegermichl hat geschrieben:
Fr 9. Dez 2022, 17:19
Gibt es beim FPC eigentlich eine builtin Lösung um einen Wert auf eine bestimmte Anzahl Stellen zu runden?
RoundTo(Value, n): https://www.freepascal.org/docs-html/rt ... undto.html.

Aber Achtung: n ist nicht die Zahl der Dezimalstellen, sondern diese muss als negative Zahl angegeben werden, denn 10^n ist die Genauigkeit nach dem Runden. Also, wenn man auf 2 Stellen nach dem Komma runden möchte, muss man RoundTo(value, -2) aufrufen, denn die Genauigkeit ist dann 0.01 = 10^-2. Genauso kann man natürlich auch auf Stellen vor dem Komma runden. RoundTo(value, 3) z.B. rundet auf volle Tausender.

Noch ein Achtung: Da das Ergebnis von Roundto ein Floatingpoint-Typ ist, bedeutet die Verwendung von RoundTo nicht, dass das Ergebnis nur die gewünschten Dezimalstellen enthält. Denn in der Regel ist das Ergebnis im Binärsystem nicht exakt darstellbar, und man bekommt die Zahl, die am nähesten an der gewünschten ist, und die hat halt evtl noch viele Nuller oder Neuer am Ende!

Wenn man nur für Ausgabezwecke runden möchte, muss man die String-Umwandlungs-Funktionen verwenden.

Code: Alles auswählen

program Project1;
uses
  math;
var
  x,y: Double;
begin
  x := 1.34562;
  y := RoundTo(x, -2);
  WriteLn(y);       // --> Ausgabe: 1.3500000000000001E+000
  WriteLn(x:0:2);   // --> Ausgabe: 1.35
  ReadLn;
end.

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: real auf integer runden

Beitrag von Winni »

Hallo!

Wenn ich ein mathematisches Problem habe, dann möchte ich nie und nimmer Bankers rounding.

Das ist für Statistiker, Banker und sonstige Betrüger.

Und ein schönes Beispiel, wie die Wirtschaftsmacht in den letzten Jahren mehr und mehr Oberhand über die Intelligenz bekommt. In der gesamten Gesellschaft, also auch in der IT.

Winni

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

Re: real auf integer runden

Beitrag von siro »

Das war ein langer und sehr interessanter Thread damals mit dem Runden.
viewtopic.php?f=10&t=11620&hilit=round

Kurzform:
Das Round wird heute direkt in die Prozessoren in Hardware verankert,
das entspricht dem "Bankers Rounding".

Das Original, so wie ich (Ü50), es mal gelernt haben wäre:

Code: Alles auswählen

function XRound(Num:Real):Integer;
begin
  if Num >=0 then result:=Trunc(num+0.5)
             else result:=Trunc(num-0.5);
end;
und genau das benutze ich auch, da ich mit Banken nix am Hut habe, sondern mit Zeichenfunktionen... :wink:
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: real auf integer runden

Beitrag von Warf »

siro hat geschrieben:
Fr 9. Dez 2022, 20:06

Code: Alles auswählen

function XRound(Num:Real):Integer;
begin
  if Num >=0 then result:=Trunc(num+0.5)
             else result:=Trunc(num-0.5);
end;
Kleine Optimierung um das Branchen zu vermeiden:

Code: Alles auswählen

function XRound(Num:Real):Integer;
begin
  Result := Trunc(num+0.5)*Ord(Num > 0) + Trunc(num-0.5)*Ord(Num < 0);
end;
Nach meinen Messungen auf O4 etwa doppelt so schnell

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

Re: real auf integer runden

Beitrag von siro »

coole Sache :shock:
ich danke Dir Warf, werde ich testen...
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: real auf integer runden

Beitrag von fliegermichl »

wp_xyz hat geschrieben:
Fr 9. Dez 2022, 17:58
...
RoundTo(Value, n): https://www.freepascal.org/docs-html/rt ... undto.html.
Danke. Das ist natürlich viel einfacher.
Ich hab auch grad mal in die Docs geschaut. Da gibt es auch eine Funktion SetRoundMode. Der Modus Bankers Round lässt sich da aber nicht einstellen.

oliver2104
Beiträge: 22
Registriert: Sa 26. Dez 2020, 13:22

Re: real auf integer runden

Beitrag von oliver2104 »

Danke für die Antworten,
interessantes Thema, hab wieder was neues gelernt.
anscheinend hat das "banker's rounding" wirklich Vorteile beim Summieren von Zahlen:
hab folgendes ausprobiert: (ist kein Code, kann's aber nicht besser darstellen)

Code: Alles auswählen

wert         klassisch      banker
0,5           1              0
1,5           2              2
2,5           3              2
3,5           4              4
4,5           5              4
5,5           6              6
----------------------------------- Summe
18           21             18
mir geht's aber um Umwandlung einer Real Zahl in eine Pixelkoordinate (Integer).
da dürfte die klassische Rundung, eine schönere Kurve ergeben.

oliver2104
Beiträge: 22
Registriert: Sa 26. Dez 2020, 13:22

Re: real auf integer runden

Beitrag von oliver2104 »

Für klassische Rundung werd ich die Lösung von @Winni nehmen:

Code: Alles auswählen

function NonIdiotRound(X: Double): Int64;
begin
  Result := Trunc(X + 0.5);
end;
ist die einfachste Lösung von allen, aber dürfte funktionieren.

Antworten