Hallo, ich verwende Python mit Tkinter. Da habe ich zwei DoubleVars, und da stehen Zahnen drinnen: A = tkinter.DoubleVar() A.set(233728.06805843) B = tkinter.DoubleVar() B.set(0.35376) So, jetzt soll A - B berechnet werden: C.set(A.get()-B.get()) Was erwarte ich? Natürlich 233727.71429843. Was erhalte ich? 233727.71429843002 Im Anhang ein funktionierendes Beispiel. Jetzt bin ich echt sauer, das hat mich echt viel Zeit gekostet bis ich am Python gezweifelt habe.
Gustl B. schrieb: > Was erwarte ich? > Natürlich 233727.71429843. Diese Zahl ist vermutlich mit Double einfach nicht darstellbar. > Was erhalte ich? > 233727.71429843002 Das ist dann die nächstliegende, die Double kann. Befass dich mal mit der Gleitkommadarstellung. Double hat 64Bit, davon sind 52 die Mantisse und 11 der Exponent (https://de.wikipedia.org/wiki/Doppelte_Genauigkeit ). Damit sind einfach nicht alle reellen Zahlen darstellbar.
C hat exakt das selbe Verhalten:
1 | #include <stdio.h> |
2 | |
3 | int main () { |
4 | double a = 233728.06805843; |
5 | double b = 0.35376; |
6 | |
7 | double c = a - b; |
8 | |
9 | printf ("%6.11f\n", c); // Ausgabe: 233727.71429843002 |
10 | }
|
Das hängt immer davon ab, wie man bei der Ausgabe rundet. Binary Floating Point kann nunmal nicht jede Zahl darstellen. Additionen und Subtraktionen, insbesondere bei großen und kleinen Zahlen zusammen, sind da besonders gefährlich. Leider wird das in den meisten Programmier-Büchern und -Kursen nicht vernünftig erklärt. Man sollte Floating Point nur nutzen, wenn man genau weiß dass die Genauigkeit ausreicht...
Lesestoff: https://docs.python.org/3/tutorial/floatingpoint.html Tipp: https://docs.python.org/3/library/decimal.html
OK, verstanden. Aber wieso bekomme ich dann keinen Fehler? Oder wieso wird nicht eine längere Zahl verwendet (mehr Bits)?
mal_fix schrieb: > Tipp: > https://docs.python.org/3/library/decimal.html Wichtig ist hier natürlich anzumerken, dass das auch nicht alle Zahlen darstellen kann, aber halt die mit denen man im Dezimalsystem normalerweise rechnet (bis zu einer bestimmten Anzahl Ziffern). 1/3 geht damit auch nicht - das geht dann im 3er oder 9er System... :-) Wenn man wirklich mit allen Zahlen rechnen möchte braucht man ein CAS, z.B. Mathematica.
HildeK schrieb: > Befass dich mal mit der Gleitkommadarstellung. Double hat 64Bit, davon > sind 52 die Mantisse und 11 der Exponent Mit 52 Bits Mantisse könnte man Integers von 0 bis 2^52-1 beschreiben. Das entspricht einem Unfang von: 2^52 ist ca (10^0,3)^52=10^(0,3*52)=10^15,6. Der Fehler tritt bei Dir an der 17.Stelle auf => kein Rechenfehler.
Gustl B. schrieb: > OK, verstanden. Aber wieso bekomme ich dann keinen Fehler? Weil solche Fehler ständig auftreten, und man nicht ständig Fehlermeldungen bekommen möchten. Die meisten Algorithmen kommen mit derart kleinen Abweichungen klar. Gustl B. schrieb: > Oder wieso > wird nicht eine längere Zahl verwendet (mehr Bits)? Der Prozessor hat nunmal eine fixe Anzahl an Bits (64 bei Double), da werden nicht live welche nachgerüstet. Das bringt eh nicht bei allen Zahlen was - z.B. 1/10 ist im Binärsystem periodisch, das kann man mit endlich vielen Bits nicht darstellen.
Gustl B. schrieb: > Aber wieso bekomme ich dann keinen Fehler? Oder wieso > wird nicht eine längere Zahl verwendet (mehr Bits)? 1. Weil es keinen Fehler gibt. (aus prozessorsicht) 2. Weil du definiert hast, es müsse eine DoubleVar sein. Versuchs mal mit decimal oder bigfloat
:
Bearbeitet durch User
HildeK schrieb: > Gustl B. schrieb: >> Was erwarte ich? >> Natürlich 233727.71429843. > Diese Zahl ist vermutlich mit Double einfach nicht darstellbar. Nein, das akzeptiere ich jetzt doch nicht, denn: C.set(round(A.get()-B.get(),8)) Liefert das richtige Ergebnis. Sprich diese Zahl lässt sich sehr wohl als Double abspeichern.
Gustl B. schrieb: > Liefert das richtige Ergebnis. Sprich diese Zahl lässt sich sehr wohl > als Double abspeichern. Dann lass dir das Ergebnis mal mit mehr Genauigkeit ausgeben (mind. 12 Ziffern).
Was speichert denn eine DoubleVar? Wenn ich da C.set(round(A.get()-B.get(),8)) reinschreibe in C, wird dann die Genauigkeit zusätzlich gespeichert? In meiner Vorstellung ist das nur eine Zahl ohne weitere Eigenschaften. Wenn ich da direkt 233727.71429843 speichere C.set(233727.71429843) dann bleibt das da auch drinnen ohne dass da 233727.71429843002 draus wird. Also kann eine DoubleVar 233727.71429843 speichern oder nicht?
Oder schau bei WA: https://www.wolframalpha.com/input/?i=233727.71429843+to+binary&wal=header Klick ein paar mal auf "More Digits"... Da kommen mehr Ziffern als 52. Wenn irgendeine Ausgaberoutine die abschneidet ist das geschummelt. Gustl B. schrieb: > Also kann eine DoubleVar 233727.71429843 speichern oder nicht? Nein. Es kann nur bei der Ausgabe etwas abgeschnitten werden. Was Python wo wie speichert, abschneidet und ausgibt weiß ich aber nicht.
:
Bearbeitet durch User
Niklas G. schrieb: > Nein. Es kann nur bei der Ausgabe etwas abgeschnitten werden. OK, also Speichert Python noch irgendwie mit wieviele Stellen ausgegeben werden sollen bzw. mit wievielen Stellen es gespeichert wurde. Danke, das macht es für mich verständlich. Für mich ist eine Zahl im Rechner nämlich nur eine Folge an Bits die eine Zahl entweder speichern kann oder nicht.
Hallo Gustl B., hallo Niklas, Gustl B. schrieb: > OK, verstanden. Aber wieso bekomme ich dann keinen Fehler? Oder wieso Weil es dann nur noch Fehler hageln würde. Das auf Computern verbreitete Binärsystem kann nur Zahlen aus Zweierpotenzen zusammensetzen. ...32; 16; 8; 4; 2; 1; 0,5; 0,25; 0,125 Die Zahl 0,1 beispielsweise ist durch einen Computer nicht mehr korrekt darstellbar. 0,1= 1/ (2*5). Der Computer kann die 0,1 nur annähern. > wird nicht eine längere Zahl verwendet (mehr Bits)? Dann wachsen Speicherplatz und Rechenzeit, weil Du nicht mehr auf schnelle Hardware-Fließpunktarithmetik zurückgreifen kannst. Außerdem kannst Du den benötigten Speicherplatz schlecht abschätzen. Es gibt spezielle Bibliotheken, z.B. in Matlab, mit denen kannst Du in beliebiger Genauigkeit rechnen. Niklas G. schrieb: > Der Prozessor hat nunmal eine fixe Anzahl an Bits (64 bei Double), da > werden nicht live welche nachgerüstet. Das bringt eh nicht bei allen > Zahlen was - z.B. 1/10 ist im Binärsystem periodisch, das kann man mit > endlich vielen Bits nicht darstellen. Das ist ein Trugschluss, siehe oben. Du kannst auch auf einer alten Z80-CPU mit 128-Bit-Fließpunktzahlen rechnen . Die Genauigkeit der Computer-Arithmetik ist weder von Busbreite noch von implementierter Hardware-Arithmetik begrenzt, sondern nur von Speicherplatz und Rechenzeit.
:
Bearbeitet durch User
Peter M. schrieb: > Du kannst auch auf einer alten Z80-CPU mit 128-Bit-Fließpunktzahlen > rechnen . Ja, in Software. Aber Python und viele andere Sprachen rechnen standardmäßig erstmal mit der Hardware-FPU, wenn man nicht spezielle Bibliotheken dafür nutzt.
OK, es wird also immer dazugespeichert mit wieviel Stellen diese Zahl gespeichert wurde um sie dann mit der gleichen Anzahl an Stellen wieder auszugeben?
Gustl B. schrieb: > OK, also Speichert Python noch irgendwie mit wieviele Stellen ausgegeben > werden sollen bzw. mit wievielen Stellen es gespeichert wurde. Die Anzahl der gespeicherten Stelle in Binärdarstellung liegt durch das Double-Zahlenformat (52 Bit Mantisse, 11 Bit Exponent) fest. Da speichert Python nichts extra. Was daraus bei der Darstellung als Dezimalzahl wird, hängt etwas vom Wert ab. Bei Addition und Subtraktion sehr unterschiedlicher Zahlen schlägt diese Beschränkung der Stellenzahl voll zu, da der Exponent vor der Rechnung angeglichen werden muss.
Gustl B. schrieb: > OK, es wird also immer dazugespeichert mit wieviel Stellen diese Zahl > gespeichert wurde um sie dann mit der gleichen Anzahl an Stellen wieder > auszugeben? Nein. Festgelegt ist die Länge der Mantisse in Bits. Intern wird die Zahl bei Verwendungen eines normalen (in der Länge statischen!) Fließpunktzahlentyps immer mit der gleichen Genauigkeit, sprich Anzahl Stellen, sprich Anzahl Bits der Mantisse gespeichert. Irgendwo wird bei der Ausgabe gerundet, wenn z.B. die 0,1 als Double ausgegeben wird, sonst sähe man ganz viele Stellen hinter dem Komma. Die genauen Konvertierungsregeln kenne ich nicht.
Wolfgang schrieb: > Die Anzahl der gespeicherten Stelle in Binärdarstellung liegt durch das > Double-Zahlenformat (52 Bit Mantisse, 11 Bit Exponent) fest. Das meinte ich nicht. Ich meine, wenn ich in Python 0.1 speichere, dann wird das wieder als 0.1 ausgegeben, wenn ich 233727.71429843 speichere wird es als 233727.71429843 ausgegeben. Warum wird es nicht als 233727.71429843002 ausgegeben wenn es doch so intern im Speicher liegt? Da muss also irgendwie dazugespeichert werden mit welcher Länge (dezimal) es eingegeben wurde. Und DAS war mir nicht bekannt. Bei einer Subtraktion hätte ich erwartet, dass diese Länge dann erhalten bleibt, also wenn von einer Zahl mit 8 Nachkommastellen (dezimal) eine mit drei Nachkommastellen (dezimal) subtrahiert wird, dann dachte ich guckt das Python dach was die maximale Anzahl an Nachkommastellen ist und verwendet diese auch für das Ergebnis. Aber ist wohl nicht so. Schade eigentlich.
Gustl B. schrieb: > 233727.71429843 speichere wird es als > 233727.71429843 ausgegeben. Warum wird es nicht als 233727.71429843002 > ausgegeben wenn es doch so intern im Speicher liegt? Du vergleichst Äpfel mit Birnen. 233727.71429843 ist das Ergebnis Deiner Rechnung im Zehnersystem. 233727.71429843002 ist das Ergebnis der Wandlung von Minuend und Subtrahend in's Binärsystem, Bildung der Differenz und Rückwandlung in das Zehnersystem. Diese Zahlen müssen nicht identisch sein.
Peter M. schrieb: > Du vergleichst Äpfel mit Birnen. Gut möglich. Aber ein Rechner speichert doch nicht im Zahnersystem?! Also muss auch die Eingabe 233727.71429843 ins Binärsystem gewandelt und so gespeichert werden. Das ist dann 233727.71429843002. Aber ausgegeben wird mir wieder 233727.71429843 wenn ich damit nicht rechne. Wieso? Woher weiß der Rechner mit wievielen Dezimalstellen er das ausgeben soll? Ja, er bekommt diese Anzahl bei der Eingabe mit, aber um sie mir mit der gleichen Anzahl ausgeben zu können muss der sie auch irgendwo speichern. Wo geschieht das?
Gustl B. schrieb: > Ich meine, wenn ich in Python 0.1 speichere, dann wird das wieder als > 0.1 ausgegeben, wenn ich 233727.71429843 speichere wird es als > 233727.71429843 ausgegeben. Warum wird es nicht als 233727.71429843002 bei "0.1" werden nur die führenden unf angehängten "0" ausgeblendet: 233727.71429843002 Ausgabe = 233727.71429843 000000.01000000000 Ausgabe = 0.1
Gustl B. schrieb: > Woher weiß der > Rechner mit wievielen Dezimalstellen er das ausgeben soll? Ja, er > bekommt diese Anzahl bei der Eingabe mit, aber um sie mir mit der > gleichen Anzahl ausgeben zu können muss der sie auch irgendwo speichern. > Wo geschieht das? Das weiß er gar nicht. Er gibt sie vollständig aus. An irgendeiner Stelle bei Ausgabe einer binär gespeicherten Zahl im Zehnersystem rundet er, vermutlich die letzte Stelle im Zehnersystem. Sonst würdest Du statt 0,1 mit einer Stelle hinterm Komma ganz viele Neunen oder Nullen mit irgendeiner Ziffer am Schluss sehen.
Kein aber. Immer wenn du rechnest müssen die floats normalisiert werden das führt bei sehr unterschiedlich großen Zahlen zu Rundungsfehler. Das war übrigens schon immer so und eine FPU ändert daran gar nichts. Merke: wenn es genau sein muss ist float das falsche Format. Das ist so weit es Computer gibt und daran wird sich auch nichts ändern. Thomas
Hallo td, td schrieb: > bei "0.1" werden nur die führenden unf angehängten "0" ausgeblendet: Nein. Führende Nullen enthalten keine Zusatzinformationen. Wie im wirklichen Leben. :) Intern sind 0,1 entweder 0,09999999999999999999x oder 0,1000000000000000000x Die Anzahl der Ziffern hinterm Komma oben ist nur beispielhaft. Die Rundung macht daraus in beiden Fällen 0,1.
:
Bearbeitet durch User
Irgendwie reden wir aneinander vorbei. Also ich gebe in Python die Dezimalzahl 233727.71429843 ein. Und lasse mir den Inhalt dieser Variablen wieder ausgeben und bekomme wieder 233727.71429843. Intern wird aber 233727.71429843002 gespeichert. Warum wird mir nicht 233727.71429843002 ausgegeben? Woher weiß Python auf wieviele Dezimalstellen bei der Ausgabe gerundet werden soll? Genauso bei 0.1, da wird mir auch nicht 0,0999999999999999999... ausgegeben, sondern 0.1. Warum?
:
Bearbeitet durch User
Gustl B. schrieb: > Intern wird aber 233727.71429843002 gespeichert. Sie unterscheiden um ein Bit. Das ist einfach ein Rundungsfehler (oder genauer: Digitalisierungsfehler), der sich beim Rechnen ergibt. Kannst du gut mit folgendem C-Programm sehen:
1 | #include <stdio.h> |
2 | |
3 | int
|
4 | main(void) |
5 | {
|
6 | double a = 233728.06805843; |
7 | double b = 0.35376; |
8 | double x = 233727.71429843; |
9 | |
10 | printf("%A\n%A\n", a - b, x); |
11 | |
12 | return 0; |
13 | }
|
Ergebnis:
1 | 0X1.C87FDB6E21864P+17 |
2 | 0X1.C87FDB6E21863P+17 |
Wichtig: ohne Optimierung compilieren, damit der Compiler das nicht gleich selbst ausrechnet. Leider hat Python kein Format "A", daher kann man das in Python nicht direkt zeigen.
:
Bearbeitet durch Moderator
Gustl B. schrieb: > Warum wird es nicht als 233727.71429843002 > ausgegeben wenn es doch so intern im Speicher liegt? Es liegt nicht so im Speicher. Im Speicher stehen Binärzahlen.
Wolfgang schrieb: > Im Speicher stehen Binärzahlen. Das ist mir klar, aber diese Binärzahl entspricht der Dezimalzahl 233727.71429843002 und nicht der Dezimalzahl 233727.71429843. Warum wird mir trotzdem 233727.71429843 ausgegeben?
Hallo Gustl, die von Dir geschilderten Abweichungen sind das Resultat von mehreren Faktoren, ich versuch es mal ganz von vorne Stück für Stück... 1.) Double-Variablen werden als normalisierte Gleitkomazahlen (IEEE754 Double precision 64-bit) gespeichert. Nicht jede reele Zahl lässt sich als endliche Folge in einem Double darstellen, es gibt also bei der Umwandlung Rundungsfehler. In Deinem Zahlenbeispiel lässt sich weder der Minuend noch der Subtrahent verlustfrei wandeln. Dezimal 233727.71429843 wird beim Wandeln dann zu 2.33727714298429986229166388512E5 Dezimal 0.35376 wird beim Wandeln dann zu 3.53760000000000018882673202825E-1 Hier hast Du also die erste Fehlerquelle 2.) Gleitkommazahlen werden subtrahiert, indem zuerst die Exponenten angeglichen werden (dabei wird die Mantisse verschoben, bei Zahlen mit stark unterschiedlicher Größenordnung wie bei Dir fällt dann hinten was runter) und dann die Mantissen subtrahiert werden. Das Ergebnis wird dann wieder normalisiert (an dieser Stelle unkritisch) und abgespeichert. Hier ist also die zweite Fehlerquelle (Zahlen mit unterschiedlicher Größenordnung). 3.) Schließlich wird das Ergebnis beim Ausdruck dann durch irgendeinen Algorithmus gerundet. Im Normalfall geht man bei Double von ca. 17 bis 18 signifikanten Stellen aus, d.h. die Anzahl der Nachkommastellen hängt auch von der Größe der Zahl bzw. der Anzahl der Vorkommastellen ab. Hier wäre dann Fehlerquelle Nummer 3 (die Anzeige weicht von der internen Speicherung ab). Wenn Du wirklich verstehen willst, was da hinter den Kulissen abgeht, dann empfehle ich Dir, ein Grundwissen dazu aufzubauen. Hilfreiche Quellen: Infos zu Gleitkommazahlen und Normierung: https://de.wikipedia.org/wiki/Gleitkommazahl Online-Umrechner zwischen verschiedenen Formaten: http://www.binaryconvert.com/index.html
Gustl B. schrieb: > Intern wird aber 233727.71429843002 gespeichert. Warum wird mir nicht > 233727.71429843002 ausgegeben? Weil es weiß, dass die interne binäre Darstellung eine dezimal mit 17 Ziffern eingegebene Dezimalzahlen nicht auflösen kann, i.e. die letzen Ziffern bei der Umwandlung in Dezimaldarstellung, aus Sicht der Eingabe, sowieso Hausnummern sind.
Gustl B. schrieb: > Irgendwie reden wir aneinander vorbei. > > Also ich gebe in Python die Dezimalzahl 233727.71429843 ein. > > Und lasse mir den Inhalt dieser Variablen wieder ausgeben und bekomme > wieder 233727.71429843. > > Intern wird aber 233727.71429843002 gespeichert. Warum wird mir nicht Nein, das war das Ergebnis der Subtraktion. Gib' doch auch mal 233727.71429843002 ein und wieder aus! Wie Jörg W. schon zeigte, sind das zwei unterschiedliche Zahlen in Binärdarstellung. > 233727.71429843002 ausgegeben? Woher weiß Python auf wieviele > Dezimalstellen bei der Ausgabe gerundet werden soll? Gar nicht. Was für Dich aussieht, als ob auf ein unterschiedliche Anzahl von Stellen hinter dem Komma gerundet wird, ist das Ergebnis eines immer gleichen Verfahrens. > > Genauso bei 0.1, da wird mir auch nicht 0,0999999999999999999... > ausgegeben, sondern 0.1. Warum? Weil gerundet wird. Vermutlich wird die letzte Zehnerstelle abgeschnitten. Lautet die auf 5,6,7,8 oder 9 wird die Stelle davor um Eins erhöht, andernfalls wird die Zahl gleich gelassen. Dann wird die Zahl ohne diese letzte Zehnerstelle ausgegeben.
Wolfgang schrieb: > Weil es weiß, dass die interne binäre Darstellung eine dezimal mit 17 > Ziffern eingegebene Dezimalzahlen nicht auflösen kann, i.e. die letzen > Ziffern bei der Umwandlung in Dezimaldarstellung, aus Sicht der Eingabe, > sowieso Hausnummern sind. Aha! Aus Sicht der Eingabe. Das Python weiß also bei der Ausgabe noch mit wievielen Ziffern die Dezimalzahl eingegeben wurde. Wenn das tatsächlich so ist, dann erklärt es mir das verhalten. Sprich Python speichert zu jeder eingegebenen Zahl irgendwo dazu mit wievielen Dezimalstellen diese eingegeben wurde.
Gustl B. schrieb: > Aha! Aus Sicht der Eingabe. Das Python weiß also bei der Ausgabe noch > mit wievielen Ziffern die Dezimalzahl eingegeben wurde. > Wenn das tatsächlich so ist, dann erklärt es mir das verhalten. Sprich > Python speichert zu jeder eingegebenen Zahl irgendwo dazu mit wievielen > Dezimalstellen diese eingegeben wurde. Ich kann kein Python und behaupte trotzdem, dass das nicht passiert. Gib' einfach mal immer längere Zahlen ein! Irgendwann werden die letzten Stellen ignoriert.
Ja, werden sie, aber mir geht es hier um kürzere Zahlen. 0.1 wird eben als 0.1 ausgegeben. Wenn ich damit rechne werden sie aber mit maximaler Länge ausgegeben, sprich da wird die Information über die Länge bei der Eingabe irgendwie nicht mitgenommen. >>> a=0.1 >>> b=0.2 >>> c=a+b >>> c 0.30000000000000004 Edit: Anscheinend ist die Frage immernoch unklar, also: Wieso wird im Folgenden a als 0.1 ausgegeben, c aber nicht als 0.3 sondern als 0.30000000000000004? >>> a=0.1 >>> b=0.2 >>> c=a+b >>> a 0.1 >>> b 0.2 >>> c 0.30000000000000004
:
Bearbeitet durch User
Gustl B. schrieb: > Intern wird aber 233727.71429843002 Nein, es wird 233727.714298430015332996845245361328125 gespeichert. Gustl B. schrieb: > Ja, werden sie, aber mir geht es hier um kürzere Zahlen. 0.1 wird > eben > als 0.1 ausgegeben. > Wenn ich damit rechne werden sie aber mit maximaler Länge ausgegeben, > sprich da wird die Information über die Länge bei der Eingabe irgendwie > nicht mitgenommen. > >>>> a=0.1 >>>> b=0.2 >>>> c=a+b >>>> c > 0.30000000000000004 Die Stellen sind Notwendig, weil das Ergebnis der Rechnung 0.3000000000000000444089209850062616169452667236328125 ist, und nicht die genauere Näherung von 0.3: 0.299999999999999988897769753748434595763683319091796875. Wenn die Gleitkommazahl in einen String umgewandelt wird, und dann wieder in eine Gleitkommazahl, kommt so die gleiche Zahl dabei raus.
Klar, aber wieso werden dann a und b als 0.1 ausgegeben? >>> a=0.1 >>> a 0.1
Klar, aber wieso werden dann a und b als 0.1 ausgegeben? Im Speicher steht 0.10000000 41873461 : Das wird eben als 0.1 dargestellt Im Speicher steht 0.30000000 97123643 : Das wird dann als 0.30000001 dargestellt. Hier kannst Du es mit floats mal testen:https://www.h-schmidt.net/FloatConverter/IEEE754.html
Gustl B. schrieb: > Warum wird mir trotzdem 233727.71429843 ausgegeben? Habe ich zwei Beiträge über deiner Frage geschrieben. Liest du dir die Antworten auch durch?
Fliesskommawerte ohne Formatangaben auszugeben führt immer wieder zu Überraschungen. Fast egal in welcher Sprache, nur bei "bc" gibts keine.
1 | >>> 0.3-(0.1+0.2) |
2 | -5.551115123125783e-17 |
3 | |
4 | >>> "{0:.11f} {0:.16f} {0:.20f}".format(0.3-(0.1+0.2)) |
5 | '-0.00000000000 -0.0000000000000001 -0.00000000000000005551' |
:
Bearbeitet durch User
Gustl B. schrieb: > Aha! Aus Sicht der Eingabe. Das Python weiß also bei der Ausgabe noch > mit wievielen Ziffern die Dezimalzahl eingegeben wurde. Quatch - egal ob du 233727.71429843002 oder 233727.71429843 eingibst, resultiert das in der selben Binärdarstellung. Die Binärzahlen liegen auf dem Zahlenstrahl eben nicht beliebig dicht beieinander. Also ist es auch sinnlos, die Dezimalausgabe höher aufzulösen, als es dem Binärraster entspricht.
Bezogen auf das Programm: Keine Fliesskommazahl an Tk übergeben, die irgendwo zwischendrin in obskurer Weise formatiert wird, sondern einen String, der vorher definiert formatiert wird. Und bleib darin unter der Anzahl signifikanter Stellen, die das interne Fliesskommaformat hergibt.
:
Bearbeitet durch User
OK, verstanden. Es wird also zweimal gerundet, bei der Eingabe und bei der Ausgabe. Na gut, damit kann ich leben. Ich hätte erwartet, dass zumindest bei der Ausgabe der tatsächlich gespeicherte Wert ausgegeben wird und man explizit angeben muss wenn man den gerundet haben möchte.
Wolfgang schrieb: > Quatch - egal ob du 233727.71429843002 oder 233727.71429843 eingibst, > resultiert das in der selben Binärdarstellung. Macht es nicht, hatte ich oben gezeigt. Beide Zahlen unterscheiden sich um genau ein Bit.
Hallo Gustl B., Gustl B. schrieb: > OK, verstanden. Es wird also zweimal gerundet, bei der Eingabe und bei > der Ausgabe. Na gut, damit kann ich leben. Ich hätte erwartet, dass > zumindest bei der Ausgabe der tatsächlich gespeicherte Wert ausgegeben > wird und man explizit angeben muss wenn man den gerundet haben möchte. Ich habe hier mal ein Beispiel konstruiert mit selbst erfundenen "Mini-Floats". Die haben 8 Bit und können Zahlen von 0 bis knapp unter 1 speichern. Das oberste Bit entspricht einer 0,5, das zweitoberste 0,25 und das letzte entspricht 1/2^8. In 8 Bit passen bequem zwei Zehnerstellen rein, in diesem Fall also zwei Nachkommastellen. Auf die wird bei der Ausgabe gerundet. Die Eingabe von mehr Nachkommastellen ist witzlos, sie beeinflussen die Rechnung nicht. Das kann man aber nicht als Runden bezeichnen. Beachte den Übergang von 0,09 auf 0,1! Führenden Nullen (außer der ersten) und nachlaufende Nullen spielen keine Rolle. Durch Rundung entsteht aus 0,1015625 auf zwei Nachkommastellen 0,10. Die Null hinter der Eins hat keinen Informationsgehalt, deswegen wird sie nicht angezeigt, nicht etwa deswegen, weil sich die Programiersprache gemerkt hat, dass die Zahl nur mit einer Nachkommastelle eingegeben wurde!
:
Bearbeitet durch User
Danke! Ja das finde ich eben komisch, dass bei der Ausgabe gerundet wird.
Gustl B. schrieb: > Danke! Ja das finde ich eben komisch, dass bei der Ausgabe gerundet > wird. Rein analytisch gesehen hast Du Recht, weil ja nicht mit dem angezeigten, sondern einem anderen Wert gerechnet wird. Die gerundete Ausgabe ist halt Konvention und schafft Lesekomfort.
:
Bearbeitet durch User
>Ich hätte erwartet, dass zumindest bei der Ausgabe der tatsächlich >gespeicherte
Wert ausgegeben wird
Kannst Du knicken.
Beispiel: 2^-99 hat eine exakte Darstellung als (doppelgenaue) Float. In
nicht gerundeter Dezimaldarstellung sieht die Zahl aber so aus (mit
einem CAS ausgerechnet):
0.0000000000000000000000000000015777218104420236108234571305655724593464
12870218046009540557861328125
Das wäre der "tatsächlich gespeicherte Wert". Welchen Sinn sollte es
aber machen, eine Ausgaberoutine ("float to string conversion") so zu
programmieren, dass sie wirklich solche Monster ausspuckt? Oder wenn Du
trotzdem noch zweifelst: Was wolltest Du dann etwa bei 2^-300 erwarten?
Eine seitenfüllende Ausgabe?
Irgendwo hatte ich gelesen, dass vergangene Zivilisationen ein 60er-System hatten, mit der Primfaktorzerlegung 2*2*3*5. Im Gegensatz zum Binärsystem oder zum Dezimalsystem kann man damit vielmehr Bruchteile der Form 1/x "abbrechend" darstellen. Leider weiß ich nicht, ob die auch 60 verschiedene Zahlsymbole hatten. :)
Sollte man das nicht den Benutzer entscheiden lassen? Wenn der so kleine Zahlen verwendet, dann wird der schon einen Grund dafür haben und will die vielleicht auch so ausgegeben haben wie sie im Speicher stehen.
>... finde ich eben komisch, dass bei der Ausgabe gerundet wird.
Das passiert nicht aus Spaß, sondern aus Notwendigkeit: Man ist
gezwungen zu runden, weil das Binär- und das Dezimalsystem nicht
zueinander passen. Die Basis 10 des von uns Menschen benutzten
Dezimalsystems hat (außer der 2) halt auch noch die 5 als Primfaktor -
letztlich liegt darin die Ursache für all jene Merkwürdigkeiten, mit
denen Du Dich gerade auseinandersetzt.
>5 als Primfaktor letztlich liegt darin die Ursache für all jene Merkwürdigkeiten
Daran sind jetzt aber nicht die Menschen schuld.
>Wenn der so kleine Zahlen verwendet,
Das Problem ist nicht auf kleine oder große oder blaue oder rote
(kleiner Scherz...) Zahlen beschränkt, sondern absolut fundamentaler
Natur.
Deshalb gibt es keine perfekte Lösung, d. h. egal, wie ausgeklügelt ein
von Dir erdachter Rundungsalgorithmus auch sein mag, es wird immer
eine Input-Teilmenge geben, bei der er Bockmist produziert.
Es steht Dir natürlich frei, Dein eigenes Zahlenformat auf der Basis 10
(z. B. BCD) zu programmieren - da sind dann alle Dezimalzahlen (mit
genügend kurzer Mantisse) von Haus aus exakt darstellbar. Das Rechnen
mit Floats wäre aber auch da mit Rundungsfehlern behaftet - dagegen
kannst Du rein garnichts machen.
Tut mir leid, keine bessere Antwort für Dich zu haben.
Du kannst den Benutzer entscheiden lassen. Wie oben gezeigt kann man ja mit '{:.11f}'.format(Zahl) bestimmen wie viele du sehen willst. Der Standard auf der Konsole ist halt nicht alle sondern irgendwas was eben noch leicht lesbar ist. Ob man in Python die allgemeine Konsolen Ausgabe Präferenz ändern kann wie bei c++ Std::cout weiß ich nicht
Peter M. schrieb: > Irgendwo hatte ich gelesen, dass vergangene Zivilisationen ein > 60er-System hatten, mit der Primfaktorzerlegung 2*2*3*5. Im Gegensatz > zum Binärsystem oder zum Dezimalsystem kann man damit vielmehr > Bruchteile der Form 1/x "abbrechend" darstellen. > > Leider weiß ich nicht, ob die auch 60 verschiedene Zahlsymbole hatten. > :) Du meinst Babylon&Konsorten. Da gab es tatsächlich 60 verschiedene Symbole, die aber aus getrennten Zehner und Einerstelle bestanden: https://en.wikipedia.org/wiki/Babylonian_numerals
LostInMusic schrieb: > Das passiert nicht aus Spaß, sondern aus Notwendigkeit: Man ist > gezwungen zu runden, weil das Binär- und das Dezimalsystem nicht > zueinander passen. Die Basis 10 des von uns Menschen benutzten > Dezimalsystems hat (außer der 2) halt auch noch die 5 als Primfaktor - > letztlich liegt darin die Ursache für all jene Merkwürdigkeiten, mit > denen Du Dich gerade auseinandersetzt. Also klar, wenn ich eine Dezimalzahl in Register schreibe, dann kann es passieren, dass da abgeschnitten werden muss. Gut. Aber gilt auch der umgekehrte Fall? Gibt es Dualzahlen (Float) die sich nicht als endliche Dezimalzahl schreiben lassen? Das https://www.h-schmidt.net/FloatConverter/IEEE754.html schreibt mir auch für das kleinste gesetzte Bit einen endlichen Wert in Dezimalschreibweise hin.
Gustl B. schrieb: > umgekehrte Fall? Gibt es Dualzahlen (Float) die sich nicht als endliche > Dezimalzahl schreiben lassen? Nein, weil der Teiler 2 im Binärsystem in der Menge der Teiler des Dezimalsystems (2 und 5) enthalten ist.
Wunderbar, also kann Python doch problemlos jede Float als endliche Dezimalzahl ausgeben. Ohne zu runden.
Gustl B. schrieb: > Wunderbar, also kann Python doch problemlos jede Float als endliche > Dezimalzahl ausgeben. Ohne zu runden. Macht es ja auch. Nehmt doch endlich zur Kenntnis, dass das tatsächlich zwei verschiedene Zahlen sind. Ich hör' jetzt langsam auf, das hier zu predigen …
Gustl B. schrieb: > Wunderbar, also kann Python doch problemlos jede Float als endliche > Dezimalzahl ausgeben. Ohne zu runden. Ja, nur gilt das eben umgekehrt nicht. Analog zu "Dezimal Float" n.m x 10^exp (n=1..9) werden "Binary Float" als 1.m x 2^exp dargestellt. m ist dabei dabei aus 1/2, 1/4, 1/8, bis 1/2^n (n Anzahl Mantissenbits) Will man nun 0.7 dezimal darstellen, so ist dies 1/2 + 1/8 + ... genauer 1.01100110011... x 2^-1 Die letzten 4 Stellen der Mantisse wiederholen sich zyklisch, d.h. Bei endlicher Anzahl Bits kann man 0.7 als Binäre Floatingpointzahl gar nicht darstellen. FP "double" kann eben gut 15 Stellen darstellen so daß es nichts ausmacht, daß da eigentlich 0.69999999999999955... im Speicher steht, denn sinnvollerweise würde man nach auf weniger als 16 Stellen runden, vor der erste 5, so daß das gerundete Ergebnis 9.7 Der erste Konstruktuer eines Computers mit binärem FP Rechnenwerk war ein Bauing., der wuste, daß schon 6 Signifikante Stellen für viele *Rechenergebnisse mehr als ausreichend sind. Die anderen Maschinen zu Zuses Zeit verwendeten Dezimal-Arithmetik, die aufwendiger ist und mehr Speicherbits per Ziffer verbraucht, als Binär-Arithmetik. 15 Stellen Matisse sind schon 60 Bit + Exponent (min. 2 Stellen, 8 Bit) + Vorzeichen Mantisse/Exponent sind 70 Bit, statt 64. Satte 8-Relais mehr pro Speicherwort! *intern hat man bei double ja immer noch min 15 Stellen und rechnet auch mit dieser Genauigkeit. BTW, ich hab die Bits natürlich nicht mal schnell selber gerechnet: https://www.exploringbinary.com/floating-point-converter/
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.