Forum: PC-Programmierung Python rechnet falsch, Bug?


von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

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.

von HildeK (Gast)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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...

von mal_fix (Gast)


Lesenswert?


von Gustl B. (-gb-)


Lesenswert?

OK, verstanden. Aber wieso bekomme ich dann keinen Fehler? Oder wieso 
wird nicht eine längere Zahl verwendet (mehr Bits)?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Peter M. (r2d3)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Operator S. (smkr)


Lesenswert?

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
von Gustl B. (-gb-)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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).

von Gustl B. (-gb-)


Lesenswert?

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?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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
von Gustl B. (-gb-)


Lesenswert?

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.

von Peter M. (r2d3)


Lesenswert?

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
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

OK, es wird also immer dazugespeichert mit wieviel Stellen diese Zahl 
gespeichert wurde um sie dann mit der gleichen Anzahl an Stellen wieder 
auszugeben?

von Wolfgang (Gast)


Lesenswert?

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.

von Peter M. (r2d3)


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

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.

von Peter M. (r2d3)


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

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?

von td (Gast)


Lesenswert?

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

von td (Gast)


Lesenswert?

td schrieb:
> 000000.01000000000  Ausgabe =      0.1


Ich meine natürlich

000000.10000000000  Ausgabe =      0.1

von Peter M. (r2d3)


Lesenswert?

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.

von Thomas (Gast)


Lesenswert?

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

von Peter M. (r2d3)


Lesenswert?

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
von Gustl B. (-gb-)


Lesenswert?

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
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
von Wolfgang (Gast)


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

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?

von Ralf S. (Gast)


Lesenswert?

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

von Wolfgang (Gast)


Lesenswert?

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.

von Peter M. (r2d3)


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

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.

von Peter M. (r2d3)


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

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
von Jemand (Gast)


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

Klar, aber wieso werden dann a und b als 0.1 ausgegeben?

>>> a=0.1
>>> a
0.1

von Pegelregler (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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
von Wolfgang (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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
von Gustl B. (-gb-)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Peter M. (r2d3)


Angehängte Dateien:

Lesenswert?

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
von Gustl B. (-gb-)


Lesenswert?

Danke! Ja das finde ich eben komisch, dass bei der Ausgabe gerundet 
wird.

von Peter M. (r2d3)


Lesenswert?

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
von LostInMusic (Gast)


Lesenswert?

>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?

von Peter M. (r2d3)


Lesenswert?

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. 
:)

von Gustl B. (-gb-)


Lesenswert?

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.

von LostInMusic (Gast)


Lesenswert?

>... 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.

von Pegelregler (Gast)


Angehängte Dateien:

Lesenswert?

>5 als Primfaktor letztlich liegt darin die Ursache für all jene Merkwürdigkeiten
Daran sind jetzt aber nicht die Menschen schuld.

von LostInMusic (Gast)


Lesenswert?

>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.

von Lenny D. (le-do)


Lesenswert?

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

von Georg A. (georga)


Lesenswert?

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

von Gustl B. (-gb-)


Lesenswert?

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.

von Peter M. (r2d3)


Lesenswert?

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.

von Gustl B. (-gb-)


Lesenswert?

Wunderbar, also kann Python doch problemlos jede Float als endliche 
Dezimalzahl ausgeben. Ohne zu runden.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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 …

von Carl D. (jcw2)


Lesenswert?

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
Noch kein Account? Hier anmelden.