Genauigkeit beim Rechnen

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
Zvoni
Beiträge: 408
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: Genauigkeit beim Rechnen

Beitrag von Zvoni »

Ich denke nicht, dass es ein Bug ist.
Wenn man Klammern setzt, ist das ein Hinweis für den Expression-Parser diesen Term präferiert zu betrachten/zu bearbeiten.
Mit Klammern ändert man also die Operator-Präferenz.
Dabei ist es dem Compiler aber dann egal, ob die Klammern mathematisch überhaupt sinnvoll sind.

Ich denke die Moral dieses Threads ist: Wenn du von Anfang an hohe Genauigkeit benötigst, benutze keine literale bzw. untypsierten Konstanten.

In deinem Fall also (Aircode)

Code: Alles auswählen

Const 
   Teiler:Extended=180;
Var
   Grad:Extended;
   a_rad:Extended;
Begin
//.............
Grad:=15;
a_rad:=pi*Grad/Teiler;
Writeln(a_rad);
a_rad:=Grad/Teiler * pi;
Writeln(a_rad);
a_rad:=pi*(Grad/Teiler);
Writeln(a_rad);
// usw....
End;
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.

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 612
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: Genauigkeit beim Rechnen

Beitrag von Niesi »

Zvoni hat geschrieben: Fr 27. Jun 2025, 09:51 Ich denke nicht, dass es ein Bug ist.
Wenn man Klammern setzt, ist das ein Hinweis für den Expression-Parser diesen Term präferiert zu betrachten/zu bearbeiten.
Mit Klammern ändert man also die Operator-Präferenz.
Dabei ist es dem Compiler aber dann egal, ob die Klammern mathematisch überhaupt sinnvoll sind.

Ich denke die Moral dieses Threads ist: Wenn du von Anfang an hohe Genauigkeit benötigst, benutze keine literale bzw. untypsierten Konstanten.

In deinem Fall also (Aircode)

Code: Alles auswählen

Const 
   Teiler:Extended=180;
Var
   Grad:Extended;
   a_rad:Extended;
Begin
//.............
Grad:=15;
a_rad:=pi*Grad/Teiler;
Writeln(a_rad);
a_rad:=Grad/Teiler * pi;
Writeln(a_rad);
a_rad:=pi*(Grad/Teiler);
Writeln(a_rad);
// usw....
End;

Ich schreibe es mal so: Wenn behauptet wird dass

a_rad := pi * (15.0 / 180); ungleich a_rad := pi * 15 / 180; sein darf,

dann gehe ich von einer gefühlten Wahrheit aus. :lol:

Denn hier ist auch das Binärsystem keine Begündung - die Klammern werden mathematisch nicht korrekt verarbeitet.

Und wenn in Pascal keine Zahlenangaben im Quelltext gemacht werden dürften, dann wäre das wirklich nicht besonders toll - denn in allen naturwissenschaftlichen Anwendungen muss mit Radien (das ist der halbe Durchmesser) gerechnet, mit Grad zu Radiant zu Grad jongliert werden. Oder Umrechnungen von Einheiten, Angaben von Toleranzen, welche nicht berechenbar sind. Da sind immer wieder Zahlenangaben auch im Quelltext notwendig.

Daher habe ich eine Fehlermeldung geschrieben.
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

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

Re: Genauigkeit beim Rechnen

Beitrag von wp_xyz »

Das Binärsystem ist auch keine Begründung, sondern die Endlichkeit der Bits in einer Floatingpoint-Zahl. In der Mathematik liegen zwischen zwei beliebigen reellen Zahl wieder unendlich viele reelle Zahlen. Im Computer können zwischen zwei geeignet nahe beisammenliegenden Zahlen überhaupt keine anderen Zahlen mehr liegen. Die Konsequenz davon ist, dass Vergleiche zwischen Floats im Computer sehr gefährlich sind. In der Regel darf man nicht "if a=b" fragen, sondern muss eine (genau überlegte) Fehlertoleranz zulassen, z.B. wie in der SameValue-Funktion der Math-Unit: "if SameValue(a, b, 1E-8)" (1E-8 ist ein guter Wert in vielen Fällen)

Vor diesem Hintergrund ist die Diskussion, ob "pi * 15/360 = pi * (15.0/360)" irrelevant:

Code: Alles auswählen

  if SameValue(pi*(15.0/360), pi*15/360, 1E-8) then
    WriteLn('Gleich'); 

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 612
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: Genauigkeit beim Rechnen

Beitrag von Niesi »

wp_xyz hat geschrieben: Fr 27. Jun 2025, 14:04 Das Binärsystem ist auch keine Begründung, sondern die Endlichkeit der Bits in einer Floatingpoint-Zahl. In der Mathematik liegen zwischen zwei beliebigen reellen Zahl wieder unendlich viele reelle Zahlen. Im Computer können zwischen zwei geeignet nahe beisammenliegenden Zahlen überhaupt keine anderen Zahlen mehr liegen. Die Konsequenz davon ist, dass Vergleiche zwischen Floats im Computer sehr gefährlich sind. In der Regel darf man nicht "if a=b" fragen, sondern muss eine (genau überlegte) Fehlertoleranz zulassen, z.B. wie in der SameValue-Funktion der Math-Unit: "if SameValue(a, b, 1E-8)" (1E-8 ist ein guter Wert in vielen Fällen)

Vor diesem Hintergrund ist die Diskussion, ob "pi * 15/360 = pi * (15.0/360)" irrelevant:

Code: Alles auswählen

  if SameValue(pi*(15.0/360), pi*15/360, 1E-8) then
    WriteLn('Gleich'); 
Yo, ich habe es gelernt - auch von Dir. Danke dafür.

Hier ist es jedoch so, dass beide Ausdrücke genau den gleichen Wert ergeben müssen, denn sonst wird das in den Klammern anders "behandelt", als der mathematisch gleichwertige Ausdruck ohne Klammern.

Daher meine Meinung, dass dies ein Fehler ist, der nichts mit einem Vergleich oder mit Genauigkeit zu tun hat.
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

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

Re: Genauigkeit beim Rechnen

Beitrag von wp_xyz »

Naja, man könnte auch Folgendes sagen:

In dem Ausdruck pi * (15.0/360) wird zuerst 15.0 durch 360 dividiert; 15.0 ist, weil für literale Werte der kleinstmögliche Datentyp genommen wird, ein Single, und 15.0/360 ist damit auch ein Single mit dem Wert 4.166666791E-02.

Im Ausdruck pi * 15 /360 wird zuerst pi * 15 berechnet. pi ist ein Extended, das Produkt ist damit auch ein Extended, genauso wie der Quotient. Da alles mit Extended gerechnet wird, können wir uns im Vergleich zum oberen Fall den Ausdruck 15/360 auch als extended ansehen. Dazu kann man (zur Sicherheit) Extended(15)/360 rechnen, und nun erhält man 4.16666666666666666678E-0002.

Ich gebe zu, dass das ganze aber auch recht verwirrend ist, denn wie das folgende kleine Programm zeigt, ist auch der einfache Ausdruck 15/360 schon ein Extended, während 15.0/360 ein Single ist. Man sieht: Wenn man's gut meint, und einen literalen Integer durch Anfügen von ".0" zu einem Float-Typ macht, kann man auch ungenauer werden...

Code: Alles auswählen

program Project1;
uses
  Math;

  procedure WriteType(ASize: Integer);
  begin
    Case ASize of
      4: WriteLn(' --> Single');
      8: WriteLn(' --> Double');
     10: WriteLn(' --> Extended');
    end;
  end;

begin
  Write('pi = ' , pi);
  WriteType(SizeOf(pi));

  Write('15.0/360 = ', 15.0/360);
  WriteType(SizeOf(15.0/360));

  Write('Extended(15) / 360 = ', Extended(15.0)/360);
  WriteType(SizeOf(Extended(15)/360));

  Write('15/360 = ', 15/360);
  WriteType(SizeOf(15/360));

  ReadLn;
end.

Output:
pi =  3.14159265358979323851E+0000 --> Extended
15.0/360 =  4.166666791E-02 --> Single
Extended(15) / 360 =  4.16666666666666666678E-0002 --> Extended
15/360 =  4.16666666666666666678E-0002 --> Extended

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

Re: Genauigkeit beim Rechnen

Beitrag von Warf »

Niesi hat geschrieben: Fr 27. Jun 2025, 14:37 Hier ist es jedoch so, dass beide Ausdrücke genau den gleichen Wert ergeben müssen, denn sonst wird das in den Klammern anders "behandelt", als der mathematisch gleichwertige Ausdruck ohne Klammern.

Daher meine Meinung, dass dies ein Fehler ist, der nichts mit einem Vergleich oder mit Genauigkeit zu tun hat.
Das ist aber technisch schlicht weg nicht möglich, Rundungsfehler treten bei jedem rechenschritt auf, und der Fehler kommt auf das Zwischenergebnis an. Nehmen wir mal ein ganz einfaches Beispiel, du hast eine Maschiene die nur Ganzzahlen kann, und dann hast du 4 * 3 / 2. Wenn du (4*3)/2 rechnest ist die erste operation 4*3 = 12 und hat einen rundungsfehler von 0. Die Zweite Operation ist 12/2 = 6 auch wieder mit Rundungsfehler 0.
Anders rum wenn man rechnet 4*(3/2) ist die erste Operation 3/2=1.5, mit standard rundung = 2, und dann 4*2 = 8.

(4*3)/2=4*(3/2) ist nur wahr wenn die Division das echte multiplikative inverse der Multiplikation ist. Bei Computer Arithmetik ist das nicht der Fall es ist eine Approximation mit rundungsfehler. D.h. die Reihenfolge der Operationen ist relevant. Computer rechnen nicht genau, sie rechnen ledignlich "genau genug"

Antworten