Warum komisches Ergebnis bei Python?

3 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Das ist kein Fehler von Python, sondern liegt in der Natur von Computern. Vereinfacht erklärt: Der Computer arbeitet nicht mit Dezimalzahlen (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), sondern mit Binärzahlen (nur 0 und 1). Ganze Zahlen lassen sich recht einfach in solch einer darstellen und für den Computer ist es weit einfacher, mit zwei verschiedenen Zuständen zu arbeiten, als mit zehn. Die Zahl 12 wäre in Binär (Basis 2) somit 00001100 (dargestellt in 8-bit, also einem Byte).

Bei Fließkommazahlen sieht das Ganze aber etwas anders aus: Die Darstellung solcher ist in binär nicht ganz so einfach. Auch in unserem Dezimalsystem müssen wir für einige Fließkommazahlen auf Brüche ausweichen - solche existieren im Binärsystem jedoch nicht. Es ist daher nur mittels einigen Standards, wie IEEE 754, möglich, eine grobe Schätzung der Fließkommazahl zu speichern. Die Zahl wird daher nicht exakt gespeichert, und wenn du dann mit dieser weiter arbeitest und dir das Ergebnis später ausgeben lässt, wirst du feststellen, dass es zu diesen Rundungsfehlern kam.

Das Ganze kannst du hier, anhand des von mir genannten Standards IEEE 754, übrigens sehr gut nachvollziehen: https://www.h-schmidt.net/FloatConverter/IEEE754de.html
Die Seite dient als Umrechner zwischen Fließkommazahlen in "unserem" Dezimalsystem und Binärzahlen in IEEE754 mit einfacher Genauigkeit (32-bit, single).
In das Feld "You entered" einfach mal 0.1 eingeben. In dem Feld direkt darunter ("Value actually stored in float") siehst du dann nicht mehr 0.1, sondern 0.100000001490116119384765625. Ähnlich bei 0.2 als Eingabe.

Daran lässt sich nichts ändern und das lässt sich nicht beheben. Damit muss man arbeiten und diese Rundungsfehler einplanen. Man kann die Rundungsfehler nur etwas kleiner machen, indem man einen double anstelle von float verwendet. Hier würde die Zahl dann in 64-bit, nicht 32-bit, gespeichert, wodurch entsprechend mehr Platz für die Nachkommastellen ist.

Woher ich das weiß:Berufserfahrung – Inhaber einer App-Agentur & 15+ Jahre Programmiererfahrung

Der Computer stellt gebrochene Werte als Gleitkommazahlen dar. Ein solcher Datentyp kann viele Werte nicht exakt, sondern nur angenähert darstellen. Genauer gesagt lassen sich nur endlich viele rationale Zahlen darstellen. Da der Computer binär rechnet, sind die Zweierpotenzen (... 0.25, 0.5, 1, 2, 4, 8, ...) exakt darstellbar, aber viele Zwischenwerte wie 0.3 nicht.

Wenn das exakte Ergebnis nicht zu den darstellbaren Werten gehört, ist ein Rundungsfehler unvermeidbar, und das Ergebnis weicht ganz geringfügig vom wahren Wert ab.

Darum ist es in der Regel ein Fehler, die Ergebnisse von Gleitkommaberechnungen mit == oder != (gleich; ungleich) zu vergleichen, z. B.

double a;
......
if (a == 0.3) { ... }

Auch dann, wenn hier eigentlich 0.3 herauskommen würde, kann das Ergebnis leicht davon abweichen, so dass das Programm nicht richtig arbeitet. Man muss in solchen Fällen einen Toleranzbereich festlegen:

if (a > 0.2999 && a < 0.3001) ...

Um ein Ergebnis dem Benutzer auszugeben, rundet man es am besten sinnvoll durch Begrenzung der Zahl der Dezimalstellen, die auszugeben sind.

Woher ich das weiß:Studium / Ausbildung
Von Experte whgoffline bestätigt

Das liegt in der Natur von Fließkommazahlen. Die Zahlen sind nicht exakt darstellbar. Du kannst den Wert lediglich gerundet ausgeben.

Woher ich das weiß:Studium / Ausbildung – Datenverarbeitungs-Kfm, Hobby- und Profi-Programmierer
Seyd3 
Fragesteller
 23.04.2021, 08:44

Vielen Dank für die Antwort.

0
DonkeyShot  23.04.2021, 08:55
@Seyd3

Ich weiß nicht, nach welchem Standard Python die Zahlen intern speichert. Nach dem IEEE 754 Standard für Double-Werte (8 Bytes) wird eine Mantisse (52 Bits), ein Exponent (11 Bits) und ein Vorzeichen-Bit gespeichert. Der Wert der Zahl berechnet sich dann folgendermaßen:

(1 + (Mantisse / 4.503.599.627.370.496)) * 2^ ( Exponent-1023)

Der Wert 0,1 wird gespeichert als

0,1000000000000000055511151231257827021181583404541015625

(Mantisse = 2.702.159.776.422.298, Exponent = 1019)

1