Autor |
Beitrag |
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Fr 13.01.17 14:33
Moin,
kann mir jemand dieses Phänomen erklären:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var x : Double; y : integer; begin
x := 8.6; y := Trunc(x * 100); self.Caption := IntToStr(y); end; |
Warum ergibt das 859?
Macht man einen Zwischenschritt mit einer Doublevariable, dann kommt das erwartete Ergebnis raus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| var x, z : Double; y : integer; begin
x := 8.6; z := x * 100; y := Trunc(z); self.Caption := IntToStr(y); end; |
Was passiert da im Hintergrund, warum ist das so?
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Fr 13.01.17 14:56
Hallo,
"schönes" Ergebnis.
Ich habe mal noch etwas getestet.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| var x : double; y : integer; begin x := 8.6; y := Trunc(x * 100); listbox1.Items.Add(IntToStr(y)); y := Trunc(x * 1000); listbox1.Items.Add(IntToStr(y)); y := Trunc(x * 10000); listbox1.Items.Add(IntToStr(y)); y := Trunc(x * 100000); listbox1.Items.Add(IntToStr(y)); listbox1.Items.Add(floatToStr(x-8.6)); end; |
Verrückt ist, dass beim Typ single das korrekte Ergebnis kommt; bei Extended auch.
Irgendwie muss 8.6 beim Typ double als 8.5999999999999994 abgespeichert werden, aber 8.6*100 eben "genauer", denn dort ergibt floatToStr(z-860) glatt 0.
Dann wird aber beim Typ Extended keine 0 sondern 5,55...E-17. Verrückt.
Steffen
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
t.roller
Beiträge: 118
Erhaltene Danke: 34
|
Verfasst: Fr 13.01.17 15:03
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TForm1.Button4Click(Sender: TObject); var x, z : Double; begin x := 8.6; z := x * 100; self.Caption := FloatToStr(z); end; |
|
|
jaenicke
Beiträge: 19286
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 13.01.17 16:05
Der Grund ist sehr einfach, wenn man mal einen Blick in den generierten Assemblercode wirft. Der funktionierende Code mit Zwischenvariable benutzt nach der Multiplikation fstp und fld um den Wert in die Variable zu speichern und wieder daraus zu laden. Dabei wird der Wert in Single oder Double Genauigkeit konvertiert.
Ohne Zwischenvariable wird der Wert direkt an Trunc verfüttert. Der Rohwert ist noch nicht auf eine bestimmte Genauigkeit gerundet, so dass das bekannte Ergebnis dabei herauskommt.
Deshalb empfehle ich auch bei Rechnungen nicht zu viel in eine Zeile zu schreiben.
Etwas langsamer sind die Zwischenvariablen zwar, aber dafür funktioniert es in der Regel eher so wie man es meinte.
Für diesen Beitrag haben gedankt: Nersgatt
|
|
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Fr 13.01.17 16:28
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
GuaAck
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: Sa 14.01.17 02:20
Hallo,
der Hintergrund liegt in der binären Darstellung (IEEE o.ä.). Dezimal werden Zahlen als Potenzen von 10 dargestellt, im Rechner binär als Potenzen von 2.
8,6 = 8+ 6*10^-1 ist klar.
Binar: Die Reihe 8 + 1/2 + 1/16+1/32+1/256 ... konvergiert zwar gegen 8.6, kann es aber nicht exakt treffen.
Anders ausgedrückt: Von allen reellen Zahlen kann man im dezimalem Format x.y nur die treffen, die ab 0 auf dem 0.1-Raster liegen. Im Binärsytsem nur die, die im 1/2 Raster liegen. In beiden kann man das Raster beliebig verfeinern (Nachkommatsellen erhöhen), die Raster kommen aber nie zur Deckung, es passt nur an einigen Stellen (z. B. 0.5 und 0.75)
So wird aus der dezimalen 8.6 eine binäre Zahl, die um einen Rundungsfehler von der 8.6 abweicht. Daher auch die alte Progrmmiererweisheit: Real-Zahlen nie per "=" auf Gleichheit abfragen.
Gruß Guaack
Für diesen Beitrag haben gedankt: Nersgatt
|
|
ub60
Beiträge: 762
Erhaltene Danke: 127
|
Verfasst: Sa 14.01.17 02:58
Ich möchte die IEE 754-Darstellung noch etwas konkretisieren.
Die Zahl 860 wird dargestellt als:
"0 10000001000 1010111000000000000000000000000000000000000000000000" (binär, Vorzeichen-Exponent-Mantisse),
das sind genau 860.0, hier klappt alles.
Die Zahl 9.6 hingegen wird intern zu:
"0 10000000010 0001001100110011001100110011001100110011001100110011" (binär, Vorzeichen-Exponent-Mantisse), das sind
8.5999999999999996447286321199499070644378662109375 dezimal.
Hier erkennt man die Fehlerursache.
Die nächst größere Binärzahl wäre ja
"0 10000000010 0001001100110011001100110011001100110011001100110100" (binär, Vorzeichen-Exponent-Mantisse),
und diese Zahl ist genau
8.60000000000000142108547152020037174224853515625
Zwischen diesen beiden Zahlen gibt es also im Double-Speicherbereich keine Zahl mehr.
ub60
Für diesen Beitrag haben gedankt: Nersgatt
|
|
|