Modulo rechnet falsch

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.

Modulo rechnet falsch

Beitragvon Mathias » 13. Feb 2018, 18:00 Modulo rechnet falsch

Ich wollte das Modulo vom Wiki ausprobieren: http://wiki.freepascal.org/Mod
Da bin ich auf folgenden Bug gestossen. Modulo(6.6, 1.1) spuckt 1.1 anstelle von 0.0 aus. Probiert mit FPC 3.0.4.
Code: Alles auswählen
program Project1;
 
  function Modulo(Dividend, Quotient: double): double;
  begin
    Result := Dividend - Quotient * Int(Dividend / Quotient);
  end;
 
begin
  WriteLn(Modulo(8.8, 1.1): 10: 5);
  WriteLn(Modulo(7.7, 1.1): 10: 5);
  WriteLn(Modulo(6.6, 1.1): 10: 5); // Falsch !
  WriteLn(Modulo(5.5, 1.1): 10: 5);
  WriteLn(Modulo(5.9, 1.1): 10: 5);
end.


Ab FPC 3.1.1 wird Modulo in der Unit Math unterstützt.
Folgender Versuch mit FPC 3.1.1. macht einen ähnlichen Fehler, dort rechnet er mit 7.7 falsch.
Code: Alles auswählen
uses
  Math;
begin
  WriteLn(8.8 mod 1.1: 10: 5);
  WriteLn(7.7 mod 1.1: 10: 5); // Falsch !
  WriteLn(6.6 mod 1.1: 10: 5);
  WriteLn(5.5 mod 1.1: 10: 5);
end

Rechnet mein PC falsch, oder könnt ihr das auch nachvollziehen, bevor ich eine Bug-Report schreibe.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 4327
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon indianer-frank » 13. Feb 2018, 18:28 Re: Modulo rechnet falsch

Das kommt darauf an, was Du als Bug bezeichnest. Es sind halt die Probleme, die auftreten, wenn Deine Eingaben nicht exakt als Binärzahlen darstellbar sind. Wenn Du die einzelnen Werte mal ausgibst, siehst Du mit
Code: Alles auswählen
program Project1;
uses math;
 
  function Modulo(Dividend, Quotient: double): double;
  begin
    Modulo := Dividend - Quotient * int(Dividend / Quotient);
  end;
 
var
  a,b,c,d,e: double;
begin
  WriteLn(Modulo(8.8, 1.1): 10: 5);
  WriteLn(Modulo(7.7, 1.1): 10: 5);
  WriteLn(Modulo(6.6, 1.1): 10: 5); // Falsch !
  WriteLn(Modulo(5.5, 1.1): 10: 5);
  WriteLn(Modulo(5.9, 1.1): 10: 5);
  a := 6.6;    writeln(a:30);
  b := 1.1;    writeln(b:30);
  c := a/b;    writeln(c:30);
  d := int(c); writeln(d:30);
  e := a-d*b;  writeln(e:30);
end.
 
die Ergebnisse
Code: Alles auswählen
   0.00000
   1.10000
   1.10000
   1.10000
   0.40000
       6.5999999999999996E+000
       1.1000000000000001E+000
       5.9999999999999991E+000
       5.0000000000000000E+000
       1.0999999999999992E+000

Wenn Du int durch round ersetzt ( int ist ja im Prinzip ein trunc für double ist) erhältst Du
Code: Alles auswählen
      0.00000
  -0.00000
  -0.00000
  -0.00000
   0.40000
       6.5999999999999996E+000
       1.1000000000000001E+000
       5.9999999999999991E+000
       6.0000000000000000E+000
      -8.8817841970012523E-016 

Dann kann es allerdings für andere Werte 'falsche' Ergebnisse geben.
indianer-frank
 
Beiträge: 133
Registriert: 30. Nov 2008, 21:53

Beitragvon wp_xyz » 13. Feb 2018, 18:53 Re: Modulo rechnet falsch

Die sind die Leute, die diese Funktion implementiert haben, zu sorglos vorgegangen. Du erwartest zu recht, dass 7.7 / 1.1 = 7.0 ist, also keinen Rest hat. Wenn du dir das Zwischenergebnis 7.7/1.1 aber ausgeben lässt sieht du auf Grund der endlichen Genauigkeit von Gleitkommazahlen den Wert 6.99999999999. Mit dem Int() wird daraus 6, und schon ist das Ergebnis massiv falsch. Um einen Divisor genaugenommen.

Da bei der Division der Rest aber immer kleiner ist als der Divisor, könnte man hieran aber auch den Fehler erkennen, indem man das Funktionsergebnis auf Gleichheit mit dem Divisor testet und dabei die Funktion SameValue verwendet, die Maschinengenauigkeit berücksichtigt:

Code: Alles auswählen
uses
  Math;
 
function Modulo(Dividend, Divisor: double): double;
begin
  Result := Dividend - Divisor * Int(Dividend / Divisor);
  if SameValue(abs(Result), abs(Divisor)) then Result := 0.0;
end

Wie das mit negativen Zahlen ausgeht, müsste man noch prüfen.

[EDIT]
Mit abs() für die beiden Zahlen unter SameValue geht es auch für negative Zahlen (oben geändert).

Ich habe einen Bug-Report geschrieben: https://bugs.freepascal.org/view.php?id=33167
wp_xyz
 
Beiträge: 2642
Registriert: 8. Apr 2011, 08:01

Beitragvon indianer-frank » 13. Feb 2018, 19:33 Re: Modulo rechnet falsch

wp_xyz hat geschrieben:
Code: Alles auswählen
uses
  Math;
 
function Modulo(Dividend, Divisor: double): double;
begin
  Result := Dividend - Divisor * Int(Dividend / Divisor);
  if SameValue(Result, Divisor) then Result := 0.0;
end

Wie das mit negativen Zahlen ausgeht, müsste man noch prüfen.

Es geht schief. Die Ergebnisse für die entsprechenden negativen Dividenden sind
Code: Alles auswählen
   0.00000
  -1.10000
  -1.10000
  -1.10000
  -0.40000

IMO arbeitet die brauchbarste Lösung mit der floor-Funktion (wie es mathematisch korekt ist) und SameValue:
Code: Alles auswählen
program Project1;
{$mode delphi}
 
uses math;
 
function Modulo(Dividend, Divisor: double): double;
begin
  Result := Dividend - Divisor * floor(Dividend / Divisor);
  if SameValue(Result, Divisor) then Result := 0.0;
end;
 
begin
  WriteLn(Modulo(8.8, 1.1): 10: 5);
  WriteLn(Modulo(7.7, 1.1): 10: 5);
  WriteLn(Modulo(6.6, 1.1): 10: 5);
  WriteLn(Modulo(5.5, 1.1): 10: 5);
  WriteLn(Modulo(5.9, 1.1): 10: 5);
  WriteLn(Modulo(-8.8, 1.1): 10: 5);
  WriteLn(Modulo(-7.7, 1.1): 10: 5);
  WriteLn(Modulo(-6.6, 1.1): 10: 5);
  WriteLn(Modulo(-5.5, 1.1): 10: 5);
  WriteLn(Modulo(-5.9, 1.1): 10: 5);
end.
 

Die Ausgabe ist dann,
Code: Alles auswählen
   0.00000
   0.00000
   0.00000
   0.00000
   0.40000
   0.00000
   0.00000
   0.00000
   0.00000
   0.70000 

Man beachte, daß bei auch 5.9 mod 1.1 der Rest die Relation 0 <= Rest < Divisor erfüllt. Allerdings muß der Quotient in ein Integer passen.
indianer-frank
 
Beiträge: 133
Registriert: 30. Nov 2008, 21:53

Beitragvon wp_xyz » 13. Feb 2018, 19:51 Re: Modulo rechnet falsch

Ja, mit Floor() erhalte ich auch die Vorzeichen, die Wolfram Alpha ausspuckt.
wp_xyz
 
Beiträge: 2642
Registriert: 8. Apr 2011, 08:01

Beitragvon Mathias » 13. Feb 2018, 20:04 Re: Modulo rechnet falsch

So wie ich sehe, gibt dies ein Bug-Report.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 4327
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon Mathias » 10. Mär 2018, 17:39 Re: Modulo rechnet falsch

Ich habe es gerade nochmals mit folgendem Code probiert.
Code: Alles auswählen
uses
  Math;
begin
  WriteLn(8.8 mod 1.1: 10: 5);
  WriteLn(7.7 mod 1.1: 10: 5); // Falsch !
  WriteLn(6.6 mod 1.1: 10: 5);
  WriteLn(5.5 mod 1.1: 10: 5);
end.

Mit einer fast neuen Trunk, kommt jetzt bei allen 4 Varianten 0.0.

Anscheinend wurde es jetzt gemacht, wie wp_xyz er vorgeschlagen hat.
Code: Alles auswählen
operator mod(const a,b:float) c:float;inline;
begin
  c:= a-b * Int(a/b);
  if SameValue(abs(c),abs(b)) then
    c:=0.0;
end
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 4327
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon wp_xyz » 10. Mär 2018, 18:01 Re: Modulo rechnet falsch

Ja, das wurde behoben, hat aber noch den Fehler, dass es nur mit float und extended funktioniert, nicht aber mit Double und Single (https://bugs.freepascal.org/view.php?id=33167). Leider wird der Bugtracker zur Zeit überhäuft mit Bugs zum FpReport, und daher lohnt es sich momentan nicht, den Report nach oben zu holen.
wp_xyz
 
Beiträge: 2642
Registriert: 8. Apr 2011, 08:01

Beitragvon Mathias » 10. Mär 2018, 18:43 Re: Modulo rechnet falsch

Ja, das wurde behoben, hat aber noch den Fehler, dass es nur mit float und extended funktioniert, nicht aber mit Double und Single

Ist Float und Single nicht das gleiche ?

Was nimmt FPC als Default bei ?
Code: Alles auswählen
WriteLn(8.8 mod 1.1: 10: 5);

float oder extended ?
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 4327
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon wp_xyz » 10. Mär 2018, 18:50 Re: Modulo rechnet falsch

Mathias hat geschrieben:
Ja, das wurde behoben, hat aber noch den Fehler, dass es nur mit float und extended funktioniert, nicht aber mit Double und Single

Ist Float und Single nicht das gleiche ?

Was nimmt FPC als Default bei ?
Code: Alles auswählen
WriteLn(8.8 mod 1.1: 10: 5);

float oder extended ?

Kann man pauschal nicht sagen: siehe Antwort von Thaddy de Konig in dem oben zitierten Bugtracker-Link. Ansonsten "math" zu "uses" hinzufügen, eine Variable als "float" deklarieren und auf "float" mit gedrückter CTRL-Taste klicken -> die IDE springt zu der Stelle, wo "float" deklariert ist. Bei mir, Win 10, Laz 32-bit, steht da: "float = extended".
wp_xyz
 
Beiträge: 2642
Registriert: 8. Apr 2011, 08:01

Beitragvon Mathias » 10. Mär 2018, 20:13 Re: Modulo rechnet falsch

Bei mir, Win 10, Laz 32-bit, steht da: "float = extended".

Bei mir auch Linux 64Bit.
Somit ist man besser bedient, wen man Single nimmt, dieser ist immer 32Bit gross.

In OpenGL gibt es noch einen glFloat, dieser ist auch 32Bit. Dies muss garantiert sein, ansonsten würde man Probleme bekommen.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 4327
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

• Themenende •

Zurück zu Lazarus - Bugs



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast

porpoises-institution
accuracy-worried