Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Soner
Beiträge: 510
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von Soner »

PascalDragon hat geschrieben:
Di 14. Jun 2022, 09:24
Der Typ eines Ausdrucks hängt in Pascal grundsätzlich nicht davon ab wohin der Wert des Ausdrucks zugewiesen wird, sondern einzig von den im Ausdruck selbst involvierten Typen. Sind also alle im Ausdruck involvierten Typen Currency, so wird auch in Currency berechnet und nicht in einem Fließkommatyp.
Ich denke das ist falsch, ich kann mich jetzt erinnern, in einem Buch gelesen zu haben, dass wenn das Ergebnis einer Berechnung die Grenzen der involvierten Typen überschreitet, dann wird das Ergebnis zu nächst höheren Typ umgewandelt und wenn die zugewiesene Variable von der kleineren Typ ist, dann wird eine Compiler-Meldung ausgegeben.

Was wäre eine Sprache Wert, wenn die rechte Seite immer als Integer zurückgegeben wird, wenn die Multiplikation die Integer-Grenze überschreitet, aber in Grenzen von LongInt bleibt?
LongInt:= Integer * Integer;

Außerdem habe ich hier mit (StSatz / 100) keine reine Currency-Variablen mehr:
PreisRunden(Netto * ((StSatz / 100) +1));

Edit: Ignoriert folgenden Text, ich habe bei der Hektik Fehler gemacht. Einer der Variablen in der Formel war Double, dann wurde alles Double.
Ich habe bei 64Bit Compiler noch ein merkwüriges Verhalten entdeckt, wenn man diese Funktion:

Code: Alles auswählen

function PreisRunden(const Value: extended): extended;
var multi, X: extended;
begin
  writeln('Value: ',Format('%.6f', [Value])); ///test

  multi := IntPower(10, gZfPreiseStelle);
  X:= Value * multi;
  Result := (Trunc(X) + Trunc(Frac(X) * 2)) / multi;
end;
in diese ändert:

Code: Alles auswählen

function PreisRunden(const Value: extended): extended;
var multi, X: extended;
begin
  writeln('Value: ',Format('%.6f', [Value]));
  Result := Value;
end;
dann wird nichts abgeschnitten.
Es wird am Ende sogar beim zuweisen zu Brutto1-Variable (Currency) aufgerundet und das ist das richtige Verhalten, nämlich beim Zuweisen der Variable auf 4 Nachkommastellen aufrunden und nicht bei Zwischenergebnisse.
Es ist eindeutiger Fehler von 64Bit-Compiler.


Der Typ Extended hat in dieser ganzen Diskussion überhaupt keine Bedeutung. Das alles passiert auch wenn man Extended mit Double ersetzt-
Zuletzt geändert von Soner am Di 14. Jun 2022, 20:08, insgesamt 3-mal geändert.

Soner
Beiträge: 510
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von Soner »

wp_xyz hat geschrieben:
Di 14. Jun 2022, 13:39
Soner hat geschrieben:
Di 14. Jun 2022, 12:46
Das Problem liegt bei Parameterübergabe.
Nein. Das Problem liegt darin, wie der Int64-Wert 39996600 als Currency-Wert interpretiert wird. 32-bit und 64-bit verhalten sich dabei offensichtlich unterschiedlich - ich behaupte, dass der 32-bit Compiler das Ergebnis nach der Division durch 10000 rundet, der 64-Bit Compiler aber abschneidet.

Einfaches Testprogramm:

Code: Alles auswählen

program Project1;
var
  a, b, c: Currency;
begin
  a := 0.3738;
  b := 1.07;
  c := a * b;
  WriteLn(c);
  ReadLn;
end. 
Output (alles auf Win 11)
mit Laz 2.2.2/FPC 3.2.2/64 bit: 3.999000000000000000E-01
mit Laz 2.2.2/FPC 3.2.2/32 bit: 4.000000000000000000E-01
mit Delphi XE 10.3.3: 4.00000000000000E-0001 (sowohl für 32-bit und 64-bit Plattform).

Ich denke, allein wegen dieses Unterschieds solltest du einen Bugreport schreiben. Leider habe ich z.Zt keinen 64-bit FPC/main fertig, das sollte vorher noch getestet werden.
Das ist ja mein Problem, der 64Bit-Compiler schneidet das Zwischenergebnis auf 4 Nachkommastellen ab. Du hast nur die Formel abgekürzt. Bei dir hätte es aufrunden müssen, bei mir hätte es das Zwischenergebnis weiterreichen müssen, weil ich Extended/Double habe.
Zuletzt geändert von Soner am Di 14. Jun 2022, 20:07, insgesamt 1-mal geändert.

Soner
Beiträge: 510
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von Soner »

@Warf
Ich habe auch den Currency-Datentyp wegen der genaugkeit genommen. In der FB-Datenbank verwende ich um die Werte zu speichern den Typ Numeric(18,4).

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5098
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von af0815 »

Soner hat geschrieben:
Di 14. Jun 2022, 20:00
Ich habe auch den Currency-Datentyp wegen der genaugkeit genommen.
Meiner Meinung nach Nachvollziehbar, nur sollte man dann komplett durchgängig damit arbeiten und keine Konvertierungen implzit oder explizit machen.
Dann dürfen IMHO auch keine Unterschiede auf den verschiedenen Plattformen sein.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Soner
Beiträge: 510
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von Soner »

af0815 hat geschrieben:
Di 14. Jun 2022, 21:09
Soner hat geschrieben:
Di 14. Jun 2022, 20:00
Ich habe auch den Currency-Datentyp wegen der genaugkeit genommen.
Meiner Meinung nach Nachvollziehbar, nur sollte man dann komplett durchgängig damit arbeiten und keine Konvertierungen implzit oder explizit machen.
Dann dürfen IMHO auch keine Unterschiede auf den verschiedenen Plattformen sein.
Ich glaube du hast das Problem nicht verstanden. Das Problem ist, dass der FPC-Compiler fehler hat.
Das sieht man am besten, wenn man wp's vereinfachtes Beispiel anschaut. Obwohl alle Variablen vom Typ Currency sind, der 32Bit-Compiler rundet auf, der 64Bit-Compiler schneidet ab, was ist jetzt richtig? In diesem Beispiel ist der Unterschied sehr gering, nur 0,0001, Aber wenn man jetzt jedes Ergebnis mit 1.000.000.000 multipliziert, dann hat man ein Differenz von 100.000 Euros.

PascalDragon
Beiträge: 535
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von PascalDragon »

Soner hat geschrieben:
Di 14. Jun 2022, 19:28
PascalDragon hat geschrieben:
Di 14. Jun 2022, 09:24
Der Typ eines Ausdrucks hängt in Pascal grundsätzlich nicht davon ab wohin der Wert des Ausdrucks zugewiesen wird, sondern einzig von den im Ausdruck selbst involvierten Typen. Sind also alle im Ausdruck involvierten Typen Currency, so wird auch in Currency berechnet und nicht in einem Fließkommatyp.
Ich denke das ist falsch, ich kann mich jetzt erinnern, in einem Buch gelesen zu haben, dass wenn das Ergebnis einer Berechnung die Grenzen der involvierten Typen überschreitet, dann wird das Ergebnis zu nächst höheren Typ umgewandelt und wenn die zugewiesene Variable von der kleineren Typ ist, dann wird eine Compiler-Meldung ausgegeben.
Die in einem Ausdruck involvierten Typen können dafür sorgen, dass der Typ des Ausdrucks an sich geändert wird. Wenn du zum Beispiel Currency und LongInt zusammen verwendest wird das Ergebnis Currency, wenn du aber Currency und Double verwendest wird das Ergebnis Double (oder Extended, falls dieser existiert). Wird das Ergebnis dann zugewiesen, findet die Umwandlung dieses Typs in den Typ auf der linken Seite statt (welche auch Informationen abschneiden oder - falls aktiviert - eine Range Check Exception auslösen kann). Und der Typ des Ausdrucks wird ebenfalls genutzt, um den passenden Overload im Fall von Funktionsparametern zu finden.

Jonas Maebe hat schon immer und immer wieder erklärt, warum das so ist, ich müsste aber erstmal wieder einen dieser Bugreports finden...
Soner hat geschrieben:
Di 14. Jun 2022, 19:28
Was wäre eine Sprache Wert, wenn die rechte Seite immer als Integer zurückgegeben wird, wenn die Multiplikation die Integer-Grenze überschreitet, aber in Grenzen von LongInt bleibt?
LongInt:= Integer * Integer;
Solche Berechnungen werden mit der nativen Weite durchgeführt und dann auf die Zielgröße runtergebrochen.
Soner hat geschrieben:
Di 14. Jun 2022, 19:28
Außerdem habe ich hier mit (StSatz / 100) keine reine Currency-Variablen mehr:
PreisRunden(Netto * ((StSatz / 100) +1));
Unter der Voraussetzung, dass die involvierten Variablen Currency sind: der Typ des Ausdrucks bleibt Currency, da eine Operation von einem Currency mit einem ordinalen Wert wieder einen Currency hervorbringt.
Soner hat geschrieben:
Di 14. Jun 2022, 21:36
af0815 hat geschrieben:
Di 14. Jun 2022, 21:09
Soner hat geschrieben:
Di 14. Jun 2022, 20:00
Ich habe auch den Currency-Datentyp wegen der genaugkeit genommen.
Meiner Meinung nach Nachvollziehbar, nur sollte man dann komplett durchgängig damit arbeiten und keine Konvertierungen implzit oder explizit machen.
Dann dürfen IMHO auch keine Unterschiede auf den verschiedenen Plattformen sein.
Ich glaube du hast das Problem nicht verstanden. Das Problem ist, dass der FPC-Compiler fehler hat.
Das sieht man am besten, wenn man wp's vereinfachtes Beispiel anschaut. Obwohl alle Variablen vom Typ Currency sind, der 32Bit-Compiler rundet auf, der 64Bit-Compiler schneidet ab, was ist jetzt richtig? In diesem Beispiel ist der Unterschied sehr gering, nur 0,0001, Aber wenn man jetzt jedes Ergebnis mit 1.000.000.000 multipliziert, dann hat man ein Differenz von 100.000 Euros.
Du verlässt dich hier eben darauf, dass die fünfte Nachkommastelle signifikant ist, diese ist jedoch ein Implementierungsdetail, da für Currency nur 4 angegeben sind. Die Dokumentation spricht auch nicht umsonst vom Minimieren von Rundungsfehlern, nicht vom Ausschließen.
FPC Compiler Entwickler

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2461
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von m.fuchs »

Moderationshinweis: Der Offtopic-Teil zu zugelassener Finanzsoftware wurde verschoben.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Soner
Beiträge: 510
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von Soner »

Ich möchte nochmal hinweisen, dass das Problem nicht die Rundung ist, sondern dass der Compiler anders rechnet. 32 Bit rundet auf und 64 Bit schneidet es ab. Und der zweite Fehler ist, dass 32Bit-Compiler Zwischenergebnisse einfach weitergibt, wenn der zugewiesene Variable keine Currency ist und der 64 Bit wieder abscheidet. Wenn der Compiler konsistent bleibt, dann können wir auch entsprechend reagieren.

Edit:
Ich habe Fehlermeldung gemacht.

BrunoK
Beiträge: 5
Registriert: So 19. Jun 2022, 13:42

Re: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von BrunoK »

Currency multiply and divide on win64 - x86_64

This subject has been annoying me for a long time.

Do you allow me to comment in english in this discussion ?

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5098
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von af0815 »

BrunoK hat geschrieben: Do you allow me to comment in english in this discussion ?
The best is, to translate to German and post the englisch original too.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

BrunoK
Beiträge: 5
Registriert: So 19. Jun 2022, 13:42

Re: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von BrunoK »

Attached file contains some helper routines for the currency data type that handles RoundTo_Curr, RoundTo5_Curr, Ceil_Curr and Trunc_Curr both on CPU32 and CPU64.
Roundings are done 'away' from 0.
function SimpleFmtCurr(aCurr) returns decimal raw string for Currency value.
Except DblToCurr4, functions in this unit avoid multiplication, division and implicit datatype conversion for better control of away from 0 roundings.

Translated :

Angehängte Unit enthält einige Hilfsroutinen für den Currency Typ die RoundTo_Curr, RoundTo5_Curr, Ceil_Curr und Trunc_Curr übernimmt sowohl auf CPU32 als auch auf CPU64.
Rundungen werden ab 0 "weg" gemacht.
function SimpleFmtCurr(aCurr) gibt eine dezimale unformatierte Zeichenfolge für den Currency Typ zurück.
Funktionen in dieser Unit, mit Ausnahme von DblToCurr4, vermeiden die Multiplikation, Divisions und implizite Datentypkonvertierung für eine bessere Kontrolle von ab 0 "weg" Rundungen.
Dateianhänge
currutil.pas
(6.79 KiB) 4-mal heruntergeladen

Soner
Beiträge: 510
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von Soner »

Bruno, the problem isn't the rounding.
Fpc 32 and 64 return different results for currency types.
As an example look at the calculation from your unit:
DblToCurr4(3738 * 1.07);
The input-variable aDBl from the function DblToCurr4 has at the fpc32 the value 0,39996600 and at the fpc64 always the value 0,39990000.

BrunoK
Beiträge: 5
Registriert: So 19. Jun 2022, 13:42

Re: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von BrunoK »

win10, x86_64 fpc 3.2.2

WriteLn(SimpleFmtCurr(DblToCurr4(0.3738 * 1.07)));
WriteLn(DblToCurr4(0.3738 * 1.07));

prints on my computer
0.4000
4.000000000000000000E-01

if counting in EURO (or CHF) you should store variables to Currency type using either RoudTo_Curr or RoundTo5_curr depending on the required rounding mode.

BrunoK
Beiträge: 5
Registriert: So 19. Jun 2022, 13:42

Re: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von BrunoK »

and also win32 with FPC 3.2.2 i386

Soner
Beiträge: 510
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: Funktionparameter-Typen werden bei 3.2.3. anders behandelt.

Beitrag von Soner »

Bruono did you modify your compiler?
I have other result with fpc 3.2.1 (64 bit) and fpc 3.2.3. (64 bit), look at the picture.

This is my test program:

Code: Alles auswählen

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

uses
  Classes, SysUtils, Math, CurrUtil;

function ShowVariable(const Value: Double): Double;
begin
  writeln('Value: ',Format('%.8f', [Value])); ///test
  Result := Value;
end;


var
  Netto, Brutto: Double;
  Netto1, Brutto1: Currency;
begin
  Writeln('Result of the calculation:');
  Writeln('Brutto:= ShowVariable(Netto * 1.07);');
  WriteLn('Netto is always 0.3738'#10#13);


  Netto:= 0.3738;

  Writeln('The variables Brutto, Netto and parameter are double ---');
  Brutto:= ShowVariable(Netto * 1.07);  // Okay, Value is for both 0,399966 because variables are double
  writeln('Brutto: ',Format('%.8f', [Brutto]),#10#13);


  Netto1:= 0.3738;

  Writeln('The variables Brutto, Netto are curreny and the parameter is double ---');
  Brutto1:= ShowVariable(Netto1 * 1.07);  // Error 1! The variable Value is with 32Bit 0,399966
                                          // and with the 64Bit compiler 0,3999. Why not same?
  writeln('Brutto: ',Format('%.8f', [Brutto1]),#10#13);


  Writeln('All variables  are currency -----');
  Brutto1:= (Netto1 * 1.07);             // Error 2! All variables are currency
                                         // and Brutto1 is with the 32bit-compiler 0,4
                                         // but with the 64bit-compiler 0,3999
                                         // Again all variables are currency.
                                         // Why different value?
  writeln('Brutto: ',Format('%.6f', [Brutto1]),#10#13#10#13);

  writeln('* * * The calculation above has not round function, it only print out the input parameter. * * *'#10#13#10#13);

  writeln('The variables Brutto, Netto are curreny and the parameter is double(Bruono''s DblToCurr4)');
  Brutto1:= DblToCurr4(Netto1 * 1.07);  //RoundTo5_Curr(Netto1 * 1.07,5);

  writeln('Brutto: ',Format('%.6f', [Brutto1]));
  writeln('Brutto: ',Brutto1);

  ReadLn;
end.    
In screenshot the variables value and aDb1 are function-parameter and brutto is result of the function.
Dateianhänge
fpccurreny2.png
fpccurreny2.png (14.31 KiB) 66 mal betrachtet

Antworten