Thema: Delphi Gleitkommadivision...?

Einzelnen Beitrag anzeigen

Benutzerbild von dizzy
dizzy

Registriert seit: 26. Nov 2003
Ort: Lünen
1.932 Beiträge
 
Delphi 7 Enterprise
 

Re: Gleitkommadivision...?

  Alt 29. Sep 2004, 16:06
Diese minimalen Abweichungen sind vollkommen normal, und haben nichts mit einem Bug zu tun. Das Thema hatten wir hier auch schon des öfteren .
Ursache: Die Art und Weise nach der ein Float-Typ gebildet wird - und zwar nicht nur in Delphi, sondern überall.

Um das evtl. mal abschließend zu klären hier mal der Aufbau einer 32-Bit Gleitkommazahl (also einfache Genauigkeit = "Single")
Code:
Binärer Aufschrieb: 32 Bits

00000000000000000000000000000000
|\_____/\______________________/
|   |           mantisse
| exponent
|
Vorzeichenbit

Die dezimale Darstellung errechnet sich nach folgender Formel:

x = (-1)^Vorzeichenbit * 2^(exponent - T) * 1.mantisse

wobei T = 127 (bei Single; bei Double ist T = 1023)

angenommen wir wollen die Zahl 0.2 so darstellen, so ergäbe sich:

Vorzeichenbit = 0
exponent = 124  { 2^(124 - 127) = 2^(-3) = 0.125 }
mantisse = 0.6 { und das wird unser Problemkind }

Warum ist die Matisse das Problem? Weil sie [b]binär[/b] dargestellt werden muss! Und das ist:
0.6(d) = 0.1001100110011001100110011001100110011001100110011001100110011.....(b)
=> Also [b]periodisch[/b]! Aber die Matisse bietet uns keinen Platz für eine Periode, also wird irgendwo abgeschnitten: nämlich nach 24 Bits.
Also gilt binär:
Vorzeichenbit = 0
exponent = 1111100
mantisse = 100110011001100110011001

Gesamte Zahl binär: 01111100100110011001100110011001
(In Hex: 7C999999)

Jetzt wollen wir mal zurück rechnen:
Vorzeichenbit ist = 0, also positives Vorzeichen.
Den Exponenten haben wir oben schon mal, 2^(-3) = 0.125
Jetzt die Mantisse. Nach dem Komma rechnent sich die binäre Zahl folgendermaßen um:
erstes Bit*(2^(-1)) plus
zweites Bit*(2^(-2)) plus
drittes Bit*(2^(-3)) usw.
(von [b]links[/b] angefangen, nicht wie bei ganzen Zahlen von rechts!)
Für unsere Mantisse ergibt sich daher:
[size=9](Jetzt wirds unschön *g*)[/size]
2^(-1)+2^(-4)+2^(-5)+2^(-8)+2^(-9)+2^(-12)+2^(-13)+2^(-16)+2^(-17)+2^(-20)+2^(-21)+2^(-24)

Die Summanden ausgerechnet:
0.5 +
0.0625 +
0.03125 +
0.00390625 +
0.001953125
und ab hier verlässt mich der Taschenrechner mit seiner Anzeige, daher sei nur gesagt, dass noch einige weiter winzige Werte aufaddiert werden, bis zu einem Endergebnis von: ca. 0.5999999[b]64[/b]
Und das fett gedruckte ist nun der Fehler der beim Rechnen mit Floatzahlen nunmal prinzipbedingt auftreten kann. Ob, und wie stark dieser ist hängt davon ab, wie gut sich die nötige Mantisse binär umgerechnet in die dafür vorgesehenen Bits passen.

Um unsere 0.2 zu ende zu rechnen:
Wir haben jetzt alle nötigen Werte, und die Formel lautet: (allerdings nur ungefähr, da mein TR ja nur so wenige Stellen hat ;))
2^(-3) * 1.599999964
= 0.125 * 1.599999964
= 0.19999995 (cirka)
Und schwupps ist unsere 0.2 etwas kleiner geworden, und zwar ohne dass wir etwas dagegen tun könnten!

Dass die 0.2 im obigen Beitrag größer, und nicht wie hier kleiner geworden ist, liegt wahrscheinlich daran, dass nicht Single als Datentyp benutzt wurde. Je nach Typ verändert sich die Darstellbarkeit der Werte drastisch!
Ich hoffe es war die Mühe wert, und es wurde klar warum Float-Typen immer nur begrenzt akkurat sein können.
Allerdings erklärt dieser Umstand das ursprüngliche Problem hier nicht! Dafür ist der Fehler zu groß, und muss andere Ursachen haben.

Gruss,
Fabian

\\edit: Korrektur: Der Exponent hat bei mir hier 7 binäre Stellen - er hat aber 8! Dafür hat die Mantisse nicht 24, sondern 23 Stellen... denkt's euch bidde zurecht
Fabian K.
INSERT INTO HandVonFreundin SELECT * FROM Himmel
  Mit Zitat antworten Zitat