Autor |
Beitrag |
WeBsPaCe
      
Beiträge: 2322
Erhaltene Danke: 1
FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
|
Verfasst: Sa 05.11.11 19:14
Hoi... verstehe nicht ganz, wie IEEE 754 Floats (z.B. Single) gespeichert werden. Beispiel:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| var x: Single; begin x := 10000.0005; ShowMessage(FloatToStr(x)); end; |
Ich komme da (nach Normierung, Exponent schon gespeichert) auf:
Quelltext 1: 2: 3: 4:
| 10000,0005 (dezimal) 1,001110001000000000000001000001100010010011011101001011... (binär) 1234567890123456789012345678901234567890 10 20 30 |
Und da steht nun mal die 1 erst an der 24sten Stelle, trotzdem speichert er irgendwie eine Dezimalstelle und ich bekomm eben zurück, was am nähsten dran ist. Nehme ich x := 10000.0004 steht sie an 25ster Stelle und es passiert, was ich mir eben schon vorher gewünscht habe: die Dezimalstellen werden verschluckt und ShowMessage bringt 10000 statt 10000.0004
Frage also: warum ist die 24ste Stelle meine Grenze, wenn doch für die Mantisse beim Single-Typ nur 23 Bit zur Verfügung stehen?!
Also werd wohl aufm Schlauch stehen, aber wollte grad da nicht mehr weiterdenken. Hoffe einfach, es hat jemand ne Antwort parat.  Grüße! Und Danke schonmal.
//EDIT: Ausprobieren ergibt: er "rundet" auf die 23ste Stelle. Also falls 0 an der 23sten Stelle steht, wird bei 24 geschaut: wenn 1, dann auch 23ste zur 1, sonst 0 lassen. Kann das jemand bestätigen?! :suspect: Moderiert von Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Sa 05.11.2011 um 21:50
_________________ Steht der Bauer im Gemüse, hat er später grüne Füße.
|
|
GuaAck
      
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: Sa 05.11.11 22:01
Hallo,
die ab 10000.00 nächst größere Zahl bei single ist 10000+2^(-10)=10000.000976525. (13 Binärstellen werden für den Vorkommateil verbraucht, deshalb -10) Der Compiler versteht also anscheinend die 10000.0005 zunächst als double und bei der Zuweisung auf single rundet er richtig auf. Bei 10000.0004 rundet er entsprechend richtig nach unten ab. Bei mir gibt showMessage übrigens auch genau 10000.0009756525 aus, weil da sicher single zunächst in double gewandelt wird, was dann eine nicht vorhandenen Geanauigkeit vortäuscht, denn die 9 ist ja schon falsch.
Gruß GuaAck Moderiert von Narses: Beiträge zusammengefasstNachtrag: Folgender Code hilft vielleicht (Memo1 ist ein TMemo im Formular):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| PROCEDURE TForm1.Button1Click(Sender: TObject); TYPE t_vx = RECORD CASE boolean OF true: (s: single); false: (i: integer); END; VAR x: t_vx; j: integer; BEGIN x.s := 10000.0; FOR j := 0 TO 9 DO BEGIN Memo1.Lines.Add(floattostr(x.s)); x.i := x.i + 1; END; END; |
|
|
WeBsPaCe 
      
Beiträge: 2322
Erhaltene Danke: 1
FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
|
Verfasst: Sa 05.11.11 22:51
Hi, danke für die Antworten!
Leider versteh ich die Syntax vom hervorgehobenen Code gar nicht...?! (ist aber glaub ich nicht weiter tragisch, s.u.)
GuaAck hat folgendes geschrieben : | Nachtrag: Folgender Code hilft vielleicht (Memo1 ist ein TMemo im Formular):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| PROCEDURE TForm1.Button1Click(Sender: TObject); TYPE t_vx = RECORD CASE boolean OF true: (s: single); false: (i: integer); END; VAR x: t_vx; j: integer; BEGIN x.s := 10000.0; FOR j := 0 TO 9 DO BEGIN Memo1.Lines.Add(floattostr(x.s)); x.i := x.i + 1; END; END; | |
GuaAck hat folgendes geschrieben : | Der Compiler versteht also anscheinend die 10000.0005 zunächst als double und bei der Zuweisung auf single rundet er richtig auf. Bei 10000.0004 rundet er entsprechend richtig nach unten ab. Bei mir gibt showMessage übrigens auch genau 10000.0009756525 aus, weil da sicher single zunächst in double gewandelt wird, was dann eine nicht vorhandenen Geanauigkeit vortäuscht, denn die 9 ist ja schon falsch. |
Das kann so doch leider nicht richtig sein, oder?! Siehe z.B.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var x: Single; begin x := 1000.00003; ShowMessage(FloatToStr(x)); x := 1000.00004; ShowMessage(FloatToStr(x)); x := 1000.00005; ShowMessage(FloatToStr(x)); end; |
1000 verbraucht 10 Stellen im Binärsystem, also 9 von der Mantisse. Mit .00003 wandert die erste 1 auf Platz 25 (es wird schön 1000 in der ShowMessage ausgegeben), mit .00004 auf Platz 24 und ich bekomm eine Überraschungs-1 auf Position 23, damit innerhalb der Mantisse und schön 1000 + 2^(-23+9) in der ShowMessage.
Bin für Berichtigungen offen... 
_________________ Steht der Bauer im Gemüse, hat er später grüne Füße.
|
|
Gammatester
      
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: Sa 05.11.11 23:32
Bei normalisierten Single- und Double-Werten werden die werthöchsten Mantissenbits nicht gespeichert (bei Extended schon). Deshalb kann man im Prinzip bei Singles in 23 Bits praktisch 24 Bits unterbringen.
|
|
WeBsPaCe 
      
Beiträge: 2322
Erhaltene Danke: 1
FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
|
Verfasst: So 06.11.11 00:08
_________________ Steht der Bauer im Gemüse, hat er später grüne Füße.
|
|
Gammatester
      
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: So 06.11.11 00:55
|
|
WeBsPaCe 
      
Beiträge: 2322
Erhaltene Danke: 1
FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
|
Verfasst: So 06.11.11 01:16
Hi, danke für deine Antwort!
Gammatester hat folgendes geschrieben : | Jetzt wird auf die letzte Stelle gerundet, [...] |
Das ist genau, worum es mir geht!  Du hattest jetzt andere Zahlen genommen, wie ich. Ich schreib's nochmal mit meinen Zahlen hin:
Quelltext 1: 2: 3:
| 1000,00003 = 1, 111101000 00000000000000 0111110111010100010000010011010101010100... 1000,00004 = 1, 111101000 00000000000000 1010011111000101101011000100011100011011... ^^wird hier abgeschnitten oder gerundet? |
Mir ist einfach aufgefallen, dass - obwohl die ersten 23 Zeichen (also die komplette Mantisse für Single) komplett gleich sind - unterschiedliche Ergebnisse angezeigt werden => dachte also, ich hätte was falsch gemacht.
Dann aber festgestellt, dass es wohl auf die 24ste Stelle mit ankommt, siehe:
WeBsPaCe hat folgendes geschrieben : | //EDIT: Ausprobieren ergibt: er "rundet" auf die 23ste Stelle. Also falls 0 an der 23sten Stelle steht, wird bei 24 geschaut: wenn 1, dann auch 23ste zur 1, sonst 0 lassen. Kann das jemand bestätigen?!  |
Und wollte jetzt also nur wissen: wird da gerundet, sobald mehr als 23 Stellen da sind?
Hoffe, mein Problem kommt rüber. 
_________________ Steht der Bauer im Gemüse, hat er später grüne Füße.
|
|
Gammatester
      
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: So 06.11.11 01:48
Ja es wird auf die 23. Stelle gerundet, allerdings mit round-to-even. D.h. nicht immer wird aufgerundet, wenn in der 24. Stelle ein 1 steht, bei Deinem Beispiel nicht für 1000+1/32768.0 = 1000 + 2^-15:
Quelltext 1: 2: 3:
| 1000.00003 = 1.111 1010 0000 0000 0000 0000 01111101110101000100000100110101011 1000.00004 = 1.111 1010 0000 0000 0000 0000 10100111110001011010110001000111001 1000+2^-15 = 1.111 1010 0000 0000 0000 0000 10000000000000000000000000000000000 | Die Hexwerte sind jetzt:
Quelltext 1: 2: 3:
| 1000.00003 = 447A0000 1000.00004 = 447A0001 1000+2^-15 = 447A0000 | Also wenn das Rundungsbit an der 24.Stelle ein 1 und der Rest 0 ist (das sogenannte Sticky-Bit als or aller restlichen ist Bits ist 0), dann wird auf even gerundet. Also wie oben für 1000+2^-15 oder für
Quelltext 1: 2: 3:
| Bin: 1000 + 2^-14 + 2^-15 = 1.111 1010 0000 0000 0000 0001 100000000000000 gerundet = 1.111 1010 0000 0000 0000 0010 100000000000000 Hex: 447A0002 |
Für diesen Beitrag haben gedankt: WeBsPaCe
|
|
GuaAck
      
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: So 06.11.11 19:55
Hallo,
a) Zur Syntax: Dies ist ein varianter Record (gefährlich, deshalb gibt der Compiler eine Warnung.) x ist eine 32-Bit Variable. Bei x.s werden die Bits als single interpretiert, bei x.i als integer. x.i+1 inkementiert also das letzte Bit der Mantisse von x.s. Ist wohl klar, warum variante Records "gefährlich" sind.
b) Dein Beispiel bestätigt das Runden: Im Dezimalsystem wird ab 0.5 aufgerundet, darunter wird abgerundet. Im Binärsystem bedeutet das bei Deinem Problem: Wenn auf Platz 24 eine 1 steht, dann wird aufgerundet, also wird auf Platz 23 eine 1 addiert (bzw. gesetzt, wenn es vorher null ist.) Die 1 auf Platz 24 ist ja eben halb (=0.5) so viel Wert wie die 1 auf Platz 23.
Gruß
GuaAck
Für diesen Beitrag haben gedankt: WeBsPaCe
|
|
WeBsPaCe 
      
Beiträge: 2322
Erhaltene Danke: 1
FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
|
Verfasst: Di 08.11.11 15:39
GuaAck hat folgendes geschrieben : | b) Dein Beispiel bestätigt das Runden: Im Dezimalsystem wird ab 0.5 aufgerundet, darunter wird abgerundet. Im Binärsystem bedeutet das bei Deinem Problem: Wenn auf Platz 24 eine 1 steht, dann wird aufgerundet, also wird auf Platz 23 eine 1 addiert (bzw. gesetzt, wenn es vorher null ist.) Die 1 auf Platz 24 ist ja eben halb (=0.5) so viel Wert wie die 1 auf Platz 23. |
Hatte das wohl falsch verstanden. Dachte, du meinst, dass da "noch im Dezimalsystem" gerundet wird. Weil mein Beispiel ja grad blöderweise genau mit .0004 und .0005 war, was ja aber gar nichts mit Stelle 23 bzw. 24 zu tun hat, wo in Wirklichkeit (im Binärsystem) gerundet wird.
Danke euch beiden! Wie kann man sowas denn rausfinden/nachlesen, ohne (wie ich...) alle möglichen Kombinationen auszuprobieren und erst dann zu merken wie es vielleicht sein könnte?
Grüße!
_________________ Steht der Bauer im Gemüse, hat er später grüne Füße.
|
|
Gammatester
      
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: Di 08.11.11 16:31
Freizugängliche Quellen sind u.a. Brent/Zimmermann Modern Computer Arithmetic (zB Kap. 3.1.9 Rounding), Vorlesungsunterlagen wie Floating Point Arithmetic oder Goldbergs What Every Computer Scientist Should Know About Floating-Point Arithmetic (z.B. Hier).
Die langen binären Darstellungen habe ich mit dem Beispielrechner T_RCalc aus meinem MPArith-Paket berechnet.
|
|
|