ERLEDIGT: Rundung unter Pascal

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Joh
Lazarusforum e. V.
Beiträge: 271
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

ERLEDIGT: Rundung unter Pascal

Beitrag von Joh »

Moin,
abgesehen davon, das mich das Runden bei Pascal gewaltig nervt (ich sehe im Bankers Rounding als einzige Existenzgrundlage, das Bänker sich unrechtmäßig bereichern), ist mir die nächste komische Situation aufgefallen:
Beim Runden von double-Werten rundet das System mit dem Bankers-Rounding, beim Runden von Currency-Werten so, wie ich es mal gelernt habe und damit auch so, wie ich es gerne auf meinen Rechnungen hätte:

17,50€ netto gerundet als Currency ergibt 3,325€ MwSt, also 3,33€. So weit, so schön dachte ich mir...

Um die Werte zu überprüfen, habe mir ein kleines Konsolenprogrämmchen geschrieben:
(meine internen Funktionen myRound, myRoundc spielen hier keine Rolle)

Code: Alles auswählen

program project3;

function myRound(wert: double): double;
begin
  myRound := trunc(wert + 0.5);
end;

function myRoundc(wert: currency): currency;
begin
  myRoundc := trunc(wert + 0.5);
end;

var x, zins: double;
    y: currency;

begin
  zins := 0.19;
  x := 17.5;
  y := 17.5;
  writeln;
  writeln(x * zins, ' ', Round(x * zins*100)/100, ' ', myRound(x * zins*100)/100, ' ', myRoundc(x * zins*100)/100);
  writeln(y * zins, ' ', Round(y * zins*100)/100, ' ', myRound(y * zins*100)/100, ' ', myRoundc(y * zins*100)/100);

  readln;
end


Doof nur, das die Werte unter Windows und Linux nicht konsistent sind:

Code: Alles auswählen

Windows, Lazarus 3.6 / fpc 3.2.2 / 64bit und
Windows, Lazarus 2.2.6 / fpc 3.2.2 / 64bit
 3.3250000000000002E+000   3.3199999999999998E+000  3.3300000000000001E+000  3.3300000000000001E+000
 3.325000000000000000E+00  3.3300000000000001E+000  3.3300000000000001E+000  3.3300000000000001E+000

Windows, fpc 3.2.2 (aus dem Lazarusverzeichnis) /64 bit & 32bit
 3.3250000000000002E+000   3.3199999999999998E+000  3.3300000000000001E+000  3.3300000000000001E+000
 3.325000000000000000E+00  3.3300000000000001E+000  3.3300000000000001E+000  3.3300000000000001E+000
		 
Linux, Lazarus 3.0 / fpc 3.2.2 / 64bit
 3.3250000000000002E+000   3.3199999999999998E+000  3.3300000000000001E+000  3.330000000000000000E+00
 3.325000000000000000E+00  3.3199999999999998E+000  3.3300000000000001E+000  3.330000000000000000E+00
	 

Linux, fpc 3.2.2 / 64 bit
 3.3250000000000002E+000   3.3199999999999998E+000  3.3300000000000001E+000  3.330000000000000000E+00
 3.325000000000000000E+00  3.3199999999999998E+000  3.3300000000000001E+000  3.330000000000000000E+00
Die erste Zeile ist jeweils die Ausgabe mit double-Variablen,
die zweite Zeile die mit den Currency-Variablen.

Wichtig sind hier die Werte der 2. Spalte:
3.3199 (Bankers-Rounding, abgerundet; auf dies .9999999x scheiXX ich mal, wird mit double zusammenhängen)
3.3300 (Standard-Rundung; 0.5 wird aufgerundet)

Unter Linux scheint, anders als unter Windows, auch bei Currency-Werten das Bankers-Round genutzt zu werden.
Jetzt stellt sich nur noch die Frage: was ist daran gewollt?

Es hat sich mal jemand dran gemacht, diese Funktion zu definieren:
(Pfade bei mir:)
F:\lazarus\fpc\3.2.2\source\rtl\inc\currh.inc
F:\lazarus\fpc\3.2.2\source\rtl\inc\gencurr.inc

aus gencurr.inc

Code: Alles auswählen

function round(c : currency) : int64;
      var
        rem, absrem: currency;
      begin
        { (int64(tmyrec(c))(+/-)5000) div 10000 can overflow }
        result := int64(c);
        rem := c - currency(result);
        absrem := rem;
        if absrem < 0 then
          absrem := -absrem;
        if (absrem > 0.5) or
           ((absrem = 0.5) and
            (rem > 0)) then
          if (rem > 0) then
            inc(result)
          else
            dec(result);
      end; 

Da steckt viel Überlegung hinter, ob die ganze rumspielerei mit den beiden Variablen sinnführender ist, als die trunc-Variante, mag ich gerade nicht beurteilen.
Aber warum schafft es Code in den fpc, wenn dieser scheinbar nicht unter allen Betriebssystemen ausgeführt wird?

PS: bei der Mehrwertsteuerberechnung tritt dieser Fehler bei allen Werten auf, bei denen der Eurobetrag ungerade ist und der Centbetrag 50.
Also 1,50€, 3,50€, 5,50€, ... 151,50€, ... 7681,50€ ...

PPS: Es muß nur einer der Werte im Round ein Currency-Wert sein, dann wird das Currency-Round durchgeführt; egal an welcher Stelle der Operanden
Zuletzt geändert von Joh am Do 9. Jan 2025, 22:11, insgesamt 1-mal geändert.
just my two Beer

Benutzeravatar
Zvoni
Beiträge: 178
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Rundung unter Pascal

Beitrag von Zvoni »

Laz2.2.2/FPC3.2.2 - 32 Bit auf Win10 64Bit

Mit deinem Code:
3.3250000000000000E+000 3.32999999999999999993E+0000 3.3300000000000000E+000 3.330000000000000000E+00
3.325000000000000000E+00 3.32999999999999999993E+0000 3.3300000000000000E+000 3.330000000000000000E+00

Mit diesem Code (Achte auf die "100.0" im Teiler!!)

Code: Alles auswählen

//Alles wir bei dir oben, AUSSER:
  writeln(x * zins, ' ', Round(x * zins*100)/100.0, ' ', myRound(x * zins*100)/100.0, ' ', myRoundc(x * zins*100)/100.0);
  writeln(y * zins, ' ', Round(y * zins*100)/100.0, ' ', myRound(y * zins*100)/100.0, ' ', myRoundc(y * zins*100)/100.0);
bekomme ich dieses Ergebnis:
3.3250000000000000E+000 3.330000000E+00 3.3300000000000000E+000 3.330000000000000000E+00
3.325000000000000000E+00 3.330000000E+00 3.3300000000000000E+000 3.330000000000000000E+00
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

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

Re: Rundung unter Pascal

Beitrag von Mathias »

abgesehen davon, das mich das Runden bei Pascal gewaltig nervt (ich sehe im Bankers Rounding als einzige Existenzgrundlage, das Bänker sich unrechtmäßig bereichern), ist mir die nächste komische Situation aufgefallen:
Das Bankes Runden ist auch eines der wenigen Sachen die mich in FPC nerven. Mathe hat bei mir mehr Priorität als diese sche*** Kohle.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 253
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Rundung unter Pascal

Beitrag von Jorg3000 »

Moin!
Dies ist mein Vorschlag für das kaufmännische Runden (ab .5 aufrunden), das wir aus der Schule kennen und das wir zum Erstellen von Rechnungen z.B. mit ZUGFeRD brauchen.
Siehe procedure Test;

Code: Alles auswählen

var FormatSettingsUSA: TFormatSettings;  // see initialization


function roundCommercial(const x: Double): Int64;  // kaufmännisches Runden (DIN 1333), engl. "Rounding half away from zero"
begin
  if Frac(x) >  0.4999999 then Exit( Trunc(x) +1 );  // eigentlich einfach >= 0.5 aber Float-Genauigkeits-Problem
  if Frac(x) < -0.4999999 then Exit( Trunc(x) -1 );
  Result := Trunc(x);
end;


function roundCommercial_2DecimalPlaces(const x: Double): Double;
begin
  Result := roundCommercial(x*100) / 100;
end;


function formatDecimal_extPrecision(const Value: Double): String;
begin
  Result := FormatFloat('0.00##', Value, FormatSettingsUSA);  // 2 decimal places or up to 4 decimal places if needed
end;


procedure Test;
begin
  Writeln( formatDecimal_extPrecision( roundCommercial_2DecimalPlaces( 1.005000) ));  // =>   1.01
  Writeln( formatDecimal_extPrecision( roundCommercial_2DecimalPlaces(-1.005000) ));  // =>  -1.01
  Writeln( formatDecimal_extPrecision( roundCommercial_2DecimalPlaces( 1.004999) ));  // =>   1.00
  Writeln( formatDecimal_extPrecision( roundCommercial_2DecimalPlaces(-1.004999) ));  // =>  -1.00
end;


initialization
  FormatSettingsUSA := SysUtils.DefaultFormatSettings;
  FormatSettingsUSA.ThousandSeparator := ',';
  FormatSettingsUSA.DecimalSeparator  := '.';
end. 

Das kaufmännische Runden rundet ab .5 auf.
Hingegen wird von Pascal standardmäßig das mathematische Runden verwendet, das im Englischen Banker’s Rounding heißt und auch in technischen Bereichen genutzt wird.

Meine Funktion roundCommercial() zum kaufmännischen Runden enthält eine gewollte Besonderheit, nämlich dass der Vergleich nicht einfach auf >= .5 lautet, sondern > 0.4999999 vergleicht.
Dies ist leider aufgrund der Diskrepanz von Dezimialsystem und Fließkomma-Ungenauigkeit nötig, denn das in meinem Test-Beispiel verwendete dezimale 1.005 lässt sich mit einer Fließkomma-Variable nicht exakt speichern: es wird intern zu 1.0049999999... irgendwas. Das Problem ist altbekannt, sollte aber für den kaufmännischen Bereich vernachlässigbar und irrelevant sein.
Grüße, Jörg

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2726
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: Rundung unter Pascal

Beitrag von m.fuchs »

Mathias hat geschrieben: Do 9. Jan 2025, 17:16
abgesehen davon, das mich das Runden bei Pascal gewaltig nervt (ich sehe im Bankers Rounding als einzige Existenzgrundlage, das Bänker sich unrechtmäßig bereichern), ist mir die nächste komische Situation aufgefallen:
Das Bankes Runden ist auch eines der wenigen Sachen die mich in FPC nerven. Mathe hat bei mir mehr Priorität als diese sche*** Kohle.
Ich muss an dieser Stelle mal kritisch einhaken: da wird sich nicht unrechtmäßig bereichert und die Aussage "Mathe hat bei mir mehr Priorität" ist vollkommen unsinnig in diesem Zusammenhang. Im Gegenteil es ist das mathematische Runden und senkt die Fehlerquote. Das in der Schule oftmals gelehrte kaufmännische Runden erzeugt kleine systematische Fehler, da das Aufrunden um 0,5 vorkommt, das Abrunden um 0,5 jedoch nie.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Joh
Lazarusforum e. V.
Beiträge: 271
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: Rundung unter Pascal

Beitrag von Joh »

Hallo zusammen...

lest ihr eigentlich, was ich schrieb?

Es geht nicht um: Bankers Round vs. Standard-Round.

Die Rundungen unter Windows und Linux sind unterschiedlich!

Das ist das aktuelle Problem!
just my two Beer

Joh
Lazarusforum e. V.
Beiträge: 271
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: Rundung unter Pascal

Beitrag von Joh »

ich mach noch mal die Ingrid:

unter Windows werden currency-Werte nach Standard-Rundung gerundet.
Unter Linux wie Double-Werte nach Bankers-Round.

Write Once, run Anywhere...
just my two Beer

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2726
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: Rundung unter Pascal

Beitrag von m.fuchs »

Joh hat geschrieben: Do 9. Jan 2025, 20:29 Die Rundungen unter Windows und Linux sind unterschiedlich!
Wie kommst du denn darauf eigentlich? Dein Testprogramm zeigt das jedenfalls nicht auf.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Joh
Lazarusforum e. V.
Beiträge: 271
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: Rundung unter Pascal

Beitrag von Joh »

hab die letzen beiden Spalten gelöscht:


Windows, fpc 3.2.2 (aus dem Lazarusverzeichnis) /64 bit & 32bit
3.3250000000000002E+000 3.3199999999999998E+000
3.325000000000000000E+00 3.3300000000000001E+000

Linux, Lazarus 3.0 / fpc 3.2.2 / 64bit
3.3250000000000002E+000 3.3199999999999998E+000
3.325000000000000000E+00 3.3199999999999998E+00

Edit: Ich wollte bereits im Ursprungspost die Ziffern fett auszeichnen; das ging bei Code leider nicht
just my two Beer

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2726
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: Rundung unter Pascal

Beitrag von m.fuchs »

Das zeigt nur dass die Fließkommadarstellungen unter Windows und Linux anders sind. Aber das hat mit dem Runden nichts zu tun.

Ich weiß auch nicht so recht was du da in deinem Testprogramm da machst.
Du hast einen Currency und befüllst den mit 17.5. Dann multiplizierst du das mit 0.19 und 100. Das ganze rundest du mit Round und erhältst einen Integer.
Den Integer dividierst du dann wieder durch 100 und erhältst irgend einen Float-Type und gibst den dann aus.

BTW: Kann man einen Currency überhaupt als Ergebnis einer Rundungsfunktion in FPC erhalten? Mir ist da gerade keine Rundungsfunktion bekannt.
Und warum wird überhaupt gerundet?
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Joh
Lazarusforum e. V.
Beiträge: 271
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: Rundung unter Pascal

Beitrag von Joh »

Das Grundproblem: 19% Mehrwertsteuer.

Der Vorgang: Betrag x wird genommen, darauf werden 19% Mehrwertsteuer gerechnet.
Theoretische Berechnung: ROUND(x*19/100) / 100 um die Nachcentbeträge sauber abzuschneiden.

Das aktuelle Problem:

Die Funktion Round() liefert unter Windows und unter Linux unterschiedliche Werte, wenn ein Currency-Wert im Spiel ist.
Für mich sieht das nach einem fpc-BUG aus:

Der Wert unter Linux wird IMMER per Bankers-Rounding gerundet.
Der Wert unter Windows wird nach einer Funktion aus ..lazarus\fpc\3.2.2\source\rtl\inc\gencurr.inc gerundet, wenn ein übergebener Wert currency ist.

Das heißt: die ausgegebenen Werte der Funktion unterscheiden sich gravierend und nicht nur ab der 10. NK-Stelle je nach verwendetem Betriebssystem.
just my two Beer

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2726
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: Rundung unter Pascal

Beitrag von m.fuchs »

Joh hat geschrieben: Do 9. Jan 2025, 21:31 Der Vorgang: Betrag x wird genommen, darauf werden 19% Mehrwertsteuer gerechnet.
Theoretische Berechnung: ROUND(x*19/100) / 100 um die Nachcentbeträge sauber abzuschneiden.
Warum rundest du dafür?

Code: Alles auswählen

program Project1;
{$mode objfpc}{$H+}

uses
  Classes, SysUtils;

var
  Netto, USt, Brutto: Currency;

begin
  Netto := 17.50;
  Ust := Netto * 0.19;
  Brutto := Netto + Ust;
  WriteLn('Netto:  ', FormatCurr('0.00 €', Netto));
  WriteLn('USt:    ', FormatCurr('0.00 €', USt));
  WriteLn('Brutto: ', FormatCurr('0.00 €', Brutto));
end.
Ausgabe (unter Linux):

Code: Alles auswählen

Netto:  17.50 €
USt:    3.33 €
Brutto: 20.83 €
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Joh
Lazarusforum e. V.
Beiträge: 271
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: Rundung unter Pascal

Beitrag von Joh »

ich mach hier mal dicht und einen neuen Thread auf.
just my two Beer

Antworten