Autor Beitrag
hjl
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22
Erhaltene Danke: 2

Windows 10 Enterprise (64 Bit)
Delphi 7, Delphi XE2, Delphi Berlin (10.1), Delphi Tokyo (10.2)
BeitragVerfasst: Mo 06.11.17 10:43 
Über den Datentyp Extended ist ja bekannt, dass dieser im 64Bit-Code nur 8 Bytes groß ist, während er im 32Bit-Code 10 Bytes lang ist.
An einigen Stellen warnt Embarcadero auf seiner Webseite davor, den 10 Bytes großen Extended-Datentyp zu verwenden, ohne die Probleme wirklich ganz konkret zu nennen.
Es gibt nur wenige die Programmiersprache Delphi beschreibende Webseiten, die Angaben über den darstellbaren Zahlenbereich von Extended machen.
Embarcadero gibt an: 3.37e-4932 .. 1.18e+4932 bei 10 Bytes, 2.23e-308 .. 1.79e+308 bei 8 Bytes.
( siehe docwiki.embarcadero....fache_Typen_(Delphi) )

Inzwischen bin ich auf ein Problem gestoßen.
Gegeben sei dieser Quellcode als vereinfachtes Beispiel, compiliert in 32 Bit mit Delphi 7, ausgeführt auf Windows 7 Professional 64 Bit,
eZahl und eMant seien Extended-Variablen, iExp eine Integer-Variable:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  eMant := 1e3670;
  iExp  := 0;
  
  repeat
    eMant := eMant / 10;
    iExp := iExp +1;
  until eMant < 10;


Die repeat-until-Schleife soll hier so oft durchlaufen werden, bis eMant < 10 ist.
Bei 1e4900 passiert das auch, auch bei z.B. 1e3670 und 1e4225, der Zähler iExp hat zuletzt den Wert des 10er-Exponenten.
Aber in dem Eingabebereich von 1e3671 bis 1e4224 hat eMant zuletzt den Wert 10, nicht 1 !
Man kann auch am Zähler iExp sehen, dass die Schleife im Problembereich 1x weniger durchlaufen wird als der Exponent groß ist.

Übrigens kann man dies auch bei Delphi 7 beobachten!
Selbst auf einem alten 32Bit-Rechner mit Windows XP zeigt sich dieser Fehler.
In einem 64Bit-Programm habe ich einen solchen Fehler noch nicht beobachtet.

Mir stellt sich die Frage, ob die Extended-Variable für Vergleichsoperationen unzulässig ist.
In meinem etwa 1200 Seiten starken Delphi-Buch steht scheinbar nichts davon.

Moderiert von user profile iconChristian S.: Delphi-Tags hinzugefügt
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 06.11.17 13:07 
user profile iconhjl hat folgendes geschrieben Zum zitierten Posting springen:
Übrigens kann man dies auch bei Delphi 7 beobachten!
Selbst auf einem alten 32Bit-Rechner mit Windows XP zeigt sich dieser Fehler.
In einem 64Bit-Programm habe ich einen solchen Fehler noch nicht beobachtet.

Mir stellt sich die Frage, ob die Extended-Variable für Vergleichsoperationen unzulässig ist.
In meinem etwa 1200 Seiten starken Delphi-Buch steht scheinbar nichts davon.

Das Hauptproblem ist, das 1e3671 nicht exakt als Extended darstellbar ist, und zweitens die Divisionen durch 10 (meistens) Rundungsfehler erzeugen mit einem relativen Fehler der Größe ca 1E-19. Hier der letzte Schritt und die finale Ausgabe für eMant := 1e3671;
ausblenden Quelltext
1:
2:
3:
Pre:  4005C7FFFFFFFFFFFFE4          99.99999999999999980
post: 40029FFFFFFFFFFFFFEA           9.99999999999999998
 3670           9.99999999999999998
Die Hex-Zahlen sind die genauen internen Darstellung (siehe meine Units AMath und DAMath).

Übrigens ist auch der Ausgabe-Wert für eMant := 1e3670 nicht gleich 1, das wird nur durch die Dezimalausgabe verschleiert.
ausblenden Quelltext
1:
2:
3:
4:
5:
Pre:  4005C80000000000000D         100.00000000000000010
post: 4002A00000000000000A          10.00000000000000001
Pre:  4002A00000000000000A          10.00000000000000001
post: 3FFF8000000000000008           1.00000000000000000
 3670           1.00000000000000000

Auch für 64Bit / double tritt das Phänomen auf, zB für emant := 1e89;
ausblenden Quelltext
1:
2:
3:
Pre:  4058FFFFFFFFFFFE          99.99999999999996160
post: 4023FFFFFFFFFFFE           9.99999999999999616
   88           9.99999999999999616


Eine Lösung für das Problem ist die Ersetzung der Repeat-Klausel durch  until abs(eMant - 10) <= 1e-17; man erhält dann für 1e3671
ausblenden Quelltext
1:
2:
3:
Pre:  4005C80000000000000D         100.00000000000000010
post: 4002A00000000000000A          10.00000000000000001
 3669          10.00000000000000001


Zuletzt bearbeitet von Gammatester am Mo 06.11.17 13:15, insgesamt 2-mal bearbeitet
hjl Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22
Erhaltene Danke: 2

Windows 10 Enterprise (64 Bit)
Delphi 7, Delphi XE2, Delphi Berlin (10.1), Delphi Tokyo (10.2)
BeitragVerfasst: Mo 06.11.17 14:28 
Danke für das Einrücken in meinem Quelltext und für die schnelle Antwort!

So etwas habe ich mir im Grunde schon gedacht.
Zum einen wähnte ich solche Zeiten, in denen z.B. 10 nach einer Floatingpoint-Operation mit 9.999999999 dargestellt wird, eigentlich als vorbei.
Hier kommt ja auch hinzu, dass durch eine einfache ganze Zahl dividiert wurde.

Außerdem hätte ich dann eigentlich erwartet, dass man am Ende die wahre Darstellung von eMant durch die Debug-Anzeige mit 10 bzw. 14 Nachkommastellen zu sehen bekommt,
hier habe ich, ausgehend von 1e3671, auch 10 gesehen. Aber hier liegen die Differenzen wohl tiefer.
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 06.11.17 15:47 
user profile iconhjl hat folgendes geschrieben Zum zitierten Posting springen:
Hier kommt ja auch hinzu, dass durch eine einfache ganze Zahl dividiert wurde.
Das muß jedem klar sein, nur die Division durch zwei ist (für normalisierte) Zahlen immer exakt. Basiswechsel auf 10 (aka BCD-Arithmetik) hilft auch nicht, dort können 1/3 oder 2^100 nicht exakt dargestellt werden.

user profile iconhjl hat folgendes geschrieben Zum zitierten Posting springen:
Außerdem hätte ich dann eigentlich erwartet, dass man am Ende die wahre Darstellung von eMant durch die Debug-Anzeige mit 10 bzw. 14 Nachkommastellen zu sehen bekommt
Das kannst Du dir natürlich ansehen, zB. mit der Angabe emant, f19 bei Evaluate/Modify, dort steht dann 9.99999999999999998. Man kann auch die exakte Hex-Darstellung ansehen mit emant, mh, Du erhältst $EA $FF $FF $FF $FF $FF $FF $9F $02 $40 (das ist gleiche Ergebnis wie in meinen ersten Beitrag, nur spiegelverkehrt).