Unterschied 32 Bit 64 Bit Pascalprogramm

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1436
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von fliegermichl »

Guten Morgen,

Mir ist eine unterschiedliche Programmausführung zwischen der 32 Bit und der 64 Bit Version aufgefallen, die sich auf folgenden Codeschnipsel zurückführen lässt.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var d : Double;
begin
  d := -0.2;
  if (d = -0.2) then
    showmessage('gleich')
  else
    showmessage('nicht gleich');
end;
Die Delphi und die 32 Bit FPC Version zeigen "nicht gleich" an. Die 64 Bit Version zeigt "gleich" an.
Irgendwie merkwürdig.

sstvmaster
Beiträge: 576
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.6
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von sstvmaster »

Hab zwar nicht viel Ahnung, aber ich würde sagen das der Compiler eine andere precision für das "-0.2" bei "d = -0.2" annimmt.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var d : Double;
begin
  d := -0.2;
  if (d = double(-0.2)) then
    showmessage('gleich')
  else
    showmessage('nicht gleich');
end;
Wenn du die "-0.2" als Double castest dann geht es.
LG Maik

Windows 10,
- Lazarus 2.2.6 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.7 (fixes) + fpc 3.3.1 (main/trunk)

Benutzeravatar
KodeZwerg
Beiträge: 102
Registriert: Mo 6. Feb 2023, 11:04

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von KodeZwerg »

Bei Fließkommazahlen kommt es halt drauf an ob ein Wert perfekt reinpasst oder nicht, meistens eher das letztere.

Code: Alles auswählen

function IsNearFloat(const AValue1, AValue2: Double; const ADigits: Integer = 2): Boolean;
var
  s1, s2: string;
begin
  s1 := FloatToStrF(AValue1, ffFixed, 15, ADigits);
  s2 := FloatToStrF(AValue2, ffFixed, 15, ADigits);
  Result := s1 = s2;
end;
Das sollte unter allen bit-Systemen immer ein gleiches Resultat liefern.
Zuletzt geändert von KodeZwerg am Sa 39. Okt 6043, 29:87, insgesamt 43-mal geändert.

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von wp_xyz »

Man sollte Float-Zahlen nie mit '=' vergleichen, sondern mit der Funktion SameValue() aus der Unit Math:

Code: Alles auswählen

uses
  Math;
procedure TForm1.Button1Click(Sender: TObject);
var
  d: Double;
begin
  d := -0.2;
  if SameValue(d, -0.2) then
//  if (d = -0.2) then
    ShowMessage('gleich')
  else
    ShowMessage('nicht gleich');
end; 
Und selbst das kann noch schief gehen, wenn man mit den Zahlen viel herumgerechnet hat und dabei die Rundungsfehler vergrößert hat. Dann braucht man in SameValue als dritten Parameter eine Toleranzschwelle.

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Mathias »

Die Delphi und die 32 Bit FPC Version zeigen "nicht gleich" an. Die 64 Bit Version zeigt "gleich" an.
Irgendwie merkwürdig.
Könnte es sein, das der 32bitter in der if-Abfrage eine Float-Konstante für -0.2 nimmt und der 64bitter nimmt da eine Double ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von fliegermichl »

Was ich mich frage ist, SizeOf(d) ergibt sowohl im 32 als auch im 64 Bit Programm 8.

Wenn es nicht möglich ist, -0.2 exakt in einen Double zu packen, wieso geht es dann in dem 64 Bit Programm?

Ich habe mir dann auch den Wert mittels Format('%g', [d]) ausgeben lassen und da spuckt das 32 Bit Programm -0.20000000000000001 und das 64 Bit -0.2 aus.

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Mathias »

Was ich mich frage ist, SizeOf(d) ergibt sowohl im 32 als auch im 64 Bit Programm 8.
Ein Float hat immer 4 Byte und ein Double, wie man es im Namen schon erahnen kann 8Byte.

Was eher das Problem ist, das die Konstante in der if-Anweisung der Plattform angepasst wird.
Ich habe mir dann auch den Wert mittels Format('%g', [d]) ausgeben lassen und da spuckt das 32 Bit Programm -0.20000000000000001 und das 64 Bit -0.2 aus.
Die ist echt merkwürdig, anscheinen arbeite Format je nach Plattform anders.
Hast du mal versucht d mit Writeln auszugeben ?

Code: Alles auswählen

Writeln(d);
Writeln(d:10);
Writeln(d:10:5);
Zuletzt geändert von Mathias am Mi 23. Aug 2023, 09:07, insgesamt 1-mal geändert.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Frank Ranis
Beiträge: 201
Registriert: Do 24. Jan 2013, 21:22

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Frank Ranis »

Hallo Mathias,
Mathias hat geschrieben:
Mi 23. Aug 2023, 08:34
Ein Float hat immer 4 Bit und ein Double, wie man es im Namen schon erahnen kann 8bit.
Hast Du dich verschrieben ?
Sollte es nicht 4Byte und 8Byte sein .

Gruß

Frank
www.flz-vortex.de

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Mathias »

Frank Ranis hat geschrieben:
Mi 23. Aug 2023, 08:57
Hallo Mathias,
Mathias hat geschrieben:
Mi 23. Aug 2023, 08:34
Ein Float hat immer 4 Bit und ein Double, wie man es im Namen schon erahnen kann 8bit.
Hast Du dich verschrieben ?
Sollte es nicht 4Byte und 8Byte sein .

Gruß

Frank
Hast recht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Warf »

fliegermichl hat geschrieben:
Mi 23. Aug 2023, 08:20
Was ich mich frage ist, SizeOf(d) ergibt sowohl im 32 als auch im 64 Bit Programm 8.
Ja aber das = hat 2 Seiten, wenn d auf beiden Seiten gleich ist dann liegt der Unterschied wohl in der konstanten
fliegermichl hat geschrieben:
Mi 23. Aug 2023, 08:20
Ich habe mir dann auch den Wert mittels Format('%g', [d]) ausgeben lassen und da spuckt das 32 Bit Programm -0.20000000000000001 und das 64 Bit -0.2 aus.
Schau dir Mal den source von Format an

Code: Alles auswählen

 'G' : begin
              if CheckArg(vtCurrency,false) then
                ToAdd:=TFormatString(FloatToStrF(Args[doarg].VCurrency^,ffGeneral,Prec,3,FormatSettings))
              else if CheckArg(vtExtended,true) then
                ToAdd:=TFormatString(FloatToStrF(Args[doarg].VExtended^,ffGeneral,Prec,3,FormatSettings));
              end;
Das benutzt intern entweder extended oder currency, beides Typen die auf 32 und 64 bit sich anders verhalten.

Zu deinem Problem, also das Ding ist Recht einfach, floats, egal ob single, double oder extended length floats können, da es sich um das binär Zahlensystem handelt, nur zahlen die sich als Summe von 2er Potenzen zusammensetzen genau darstellen, also sowas wie 0,5 (2^-1), 0,25 (2^-2) oder 0,75 (0,5 + 0,25).
In der realen Welt benutzen wir aber das 10er system, und da 10=2*5 ist können wir alle zahlen darstellen die sich als Summen und produkt aus 2er und 5er Potenzen darstellen lassen, also z.b. 0,2 (5^-1) oder 0,1 (0,2 * 0,5).

Deshalb kannst du 0,2 niemals genau darstellen und jede Darstellung ist eine Rundung.
In deinem Beispiel wird jetzt für d die 48 Bit Rundung von 0.2 genommen, weil double eine 48 Bit mantisse hat. Für die konstante 0.2 in der Gleichung wird dann aber für 32 Bit ein anderer Typ genommen, entweder single mit 24 bit Rundung oder extended mit 64 bit. Egal ob mehr oder weniger Genauigkeit, die Rundung ist anders und kann nicht unbedingt trivial verglichen werden

Die Lösung wie bereits schon erwähnt ist für das spezielle Beispiel der Cast der konstante auf Double und damit auf die selbe 48 Bit Rundung. Das ist natürlich aber nur ein fix für dieses spezielle Beispiel, das folgende sollte trotzdem kaputt gehen

Code: Alles auswählen

d := 0.1;
d *= 3;
if d = double(0.3) then
Denn der Rundungsfehler akkumuliert sich über arithmetische Operationen.
Allerdings sollte das dann auf 32 und 64 bit genauso schief gehen, also immerhin konsistentes Verhalten

Eine wirkliche Lösung ist einfach nicht = für floats zu verwenden, sondern nur <= und >= (wie SameValue es tut), oder noch besser, einfach keine floats verwenden sondern fixpunkt und direkt korrekt rechnen ohne Rundungsfehler

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von fliegermichl »

Warf hat geschrieben:
Mi 23. Aug 2023, 10:11
Eine wirkliche Lösung ist einfach nicht = für floats zu verwenden, sondern nur <= und >= (wie SameValue es tut), oder noch besser, einfach keine floats verwenden sondern fixpunkt und direkt korrekt rechnen ohne Rundungsfehler
Danke erstmal für die Erklärung.
Wie funktioniert das mit der Fixpunktberechnung?
Gibt es da einen Datentyp für?

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Mathias »

Wie funktioniert das mit der Fixpunktberechnung?
In etwa so, wie wen du in mm anstelle von Meter rechnest. Wen du alles in mm hast, könnte ein Int64 reichen.
Ich hoffe, du kannst dir jetzt etwas darunter vorstellen.
Dieser Trick hatte man früher auch viel angewendet, als die FPUs noch rar waren.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Warf »

Es gibt theoretisch den Currency Typ, der fixpunktzahlen mit 4 Nachkommastellen implementiert. Aber der ist auch auf 32 und 64 bit unterschiedlich, ich weiss nicht mehr auf welchem aber auf einem der beiden Systeme wird einfach ein float verwendet.

Ansonsten ist es ganz einfach, du wählst einfach einen Faktor mit dem du alles multipliziert.
Z.b. für 4 Nachkommastellen ist der Faktor 10000. Also ist 1 dann die Zahl 10000 und 0,02 dann 200, etc.
Addition ist trivial einfach Addition also 1+0.02 ist 10200=1,02.
Multiplikation und Division ist ein bisschen komplexer da du hier einmal mit dem Faktor teilen/multiplizieren musst
Also 1*2=10000*20000 div 10000
Bzw 1/2 = 10000*10000/20000

Solang du in einem abgesteckten zahlenbereich bleibst ist, z.b. Geldbeträge die nicht über die Milliarden Grenze gehen und du keine Multiplikation von sehr grossen oder sehr kleinen zahlen hast ist das dir beste Art zu rechnen.

Floats sind nur dann relevant wenn du mit verschiedenen Größenordnungen rechnen musst, also glaichzeitig extrem große und extrem kleine zahlen darstellen willst.

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von wp_xyz »

Warf hat geschrieben:
Mi 23. Aug 2023, 10:11
oder noch besser, einfach keine floats verwenden sondern fixpunkt und direkt korrekt rechnen ohne Rundungsfehler
Dem würde ich in dieser Allgemeinheit nicht zustimmen. Es trifft für die Grundrechenarten Addition, Subtraktion, und Multiplikation zu, ja, aber schon bei der Division kommen mir Bedenken. Der Ausdruck 1/3 hat theoretisch unendlich viele Nachkommastellen, wie soll man da bei Fixkomma-Zahlen keinen Rundungsfehler haben? Und von sowas wie pi oder der Euler-Zahl e will ich gar nicht reden...

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

Re: Unterschied 32 Bit 64 Bit Pascalprogramm

Beitrag von Warf »

wp_xyz hat geschrieben:
Mi 23. Aug 2023, 18:54
Dem würde ich in dieser Allgemeinheit nicht zustimmen. Es trifft für die Grundrechenarten Addition, Subtraktion, und Multiplikation zu, ja, aber schon bei der Division kommen mir Bedenken. Der Ausdruck 1/3 hat theoretisch unendlich viele Nachkommastellen, wie soll man da bei Fixkomma-Zahlen keinen Rundungsfehler haben? Und von sowas wie pi oder der Euler-Zahl e will ich gar nicht reden...
Bei fixpunkt bestimmst aber du den Rundungsfehler für deinen Wertebereich. Klar ein Double kann pi auf log10(2^48) also ungefähr 14 Nachkommastellen genau darstellen, wenn du aber dann mit 7 addiert, also zwei großenordnung über Pi (Pi passt in 2^2=4, plus 7 ist ungefähr 10, passt also erst wieder in 4^4=16) und somit hat man schon 2 Bits Genauigkeit verloren.

Wenn du 48 Bit Genauigkeit brauchst, kannst du dir ganz einfach fixpunkt zahlen mit 48 Bit Genauigkeit bauen und die sind immer 48 Bit. Fpc unterstützt nativ 64 bit Integer, damit kannst du die 48 nachkommabits haben und hast trotzdem noch 16 vorkomma Bit übrig, kannst also problemlos Pi+7 rechnen und hast immer noch eine höhere Genauigkeit als mit double

Wie gesagt floats sind dafür gedacht gleichzeitig sehr hohe und der kleine Werte in einem Datentyp darstellen zu können. Sie sind grundsätzlich "gut genug" im Sinne von sie können alles solang man einen gewissen Fehler toleriert. Damit sind sie z.b. super für Grafik Berechnungen und Kram, wie OpenGL oder so, wo der Rundungsfehler nicht wirklich eine Rolle spielt.

Wenn man aber genau rechnen will sind sie eine Katastrophe weil man nicht nur einen Rundungsfehler hat, sondern durch den flieskomma Teil auch noch 15 Bit für den Exponenten abtreten muss

Antworten