Fließkomma rechnen mit Micropython

Aus Micropython Referenz
Zur Navigation springen Zur Suche springen

Das Rechnen mit Fließkommazahlen bringt immer Ungenauigkeiten mit sich. Insbesondere in Micropython ist das zu beachten. Micropython arbeitet nach Außen mit nur einer 8stelligen Mantisse, also 1 Vorkomma- und maximal 7 Nachkommastellen. Intern sind es etwas mehr (vermutlich 11), deshalb fallen die Fließkomma-Stoplerfallen auch nicht immer gleich auf. In der REPL bekommt man meist die richtigen Ergebnisse. Wenn man aber viele Fließkommaoperationen nacheinander ausführt treten dann doch die üblichen Probleme auf.

Ein kleiner Test[Bearbeiten | Quelltext bearbeiten]

>>> 0.0008499998+0.00000000009
0.0008499999
>>> 0.0008499998+0.00000000008
0.0008499998
>>> 0.0008499998+0.00000000007
0.0008499998
>>> 

>>> b=0.0008499999
>>> b
0.0008499998
>>> _
0.0008499998
>>> _+0.00000000002    # 2 an 12.Stelle
0.0008499998
>>> _+0.0000000002     # 2 an 11.Stelle
0.0008500001
>>> 

Der erste Fehler tritt schon bei der Zuweisung in der ersten Zeile auf.
Die 2 in der 12. Stelle (11. Nachkommastelle) wird ignoriert.
Wenn die die 2 aber an der 11. Stelle (10. Nachkommastelle) steht wird sie berücksichtigt. Hier aber gleich mit einem Rundungsfehler.

Additions-/Rundungsfehler[Bearbeiten | Quelltext bearbeiten]

Gerade bin ich auf einen Effekt bei der Addition von vielen Fließkommazahlen mit einem M5Stack gestoßen:

Additionsfehler[Bearbeiten | Quelltext bearbeiten]

>>> step = 0.00005
>>> x = 0.0
>>> for i in range(1000):
    x += step
    print(x)

# Ergebnisse:
5e-05
0.0001
0.00015
0.0002
0.00025
0.0003
0.00035
0.0004
0.00045
0.0005
0.00055
0.0005999999
0.0006499999
0.0006999999
0.0007499999
0.0007999998
0.0008499998
0.0008999997
0.0009499997
0.0009999997
0.00105
0.0011
0.00115
0.0012
0.00125
...
0.0024
0.00245
0.0025
0.002549999
0.002599999
0.002649999
...
0.003749997
0.003799997
0.003849998
0.003899997
0.003949997
...
0.007049992
0.007099992
0.007149992
0.007199991
0.007249992
0.007299991
0.007349991
0.007399991

Multipikationsfehler[Bearbeiten | Quelltext bearbeiten]

Mit einer Multiplikation statt Addition ist das Ergebnis befriedigender:

>>> for i in range(1000):
    x = step*i
    print(x)

# Ergebnisse:

0.0
5e-05
0.0001
0.00015
0.0002
0.00025
0.0003
0.00035
0.0004
0.00045
0.0005
0.00055
0.0006
0.00065
0.0007
0.00075
0.0008
0.0008499999
0.0009
0.00095
0.0009999999
0.00105
0.0011
0.00115
0.0012
0.00125
...
0.0052
0.00525
0.0053
0.005349999
0.0054
0.00545
0.0055
...
0.0056
0.00565
0.005699999
0.00575
0.0058
...
0.006
0.00605
0.006099999
0.00615
0.0062
...
0.0065
0.00655
0.006599999
0.00665
0.0067
0.00675
0.006799999
0.006849999
0.0069
0.00695
0.007
0.007049999
0.0071
0.007150001
0.0072
0.00725
0.0073
0.007349999
0.0074
0.00745
...
0.00775
0.0078
0.007849999
0.0079
0.00795
0.008
0.008049999
0.0081
0.00815
0.008200001
0.008249999
0.0083
0.008349999
0.0084
0.008450001
0.008499999
0.008549999
0.008599999
0.00865
0.0087
0.00875
...
0.00895
0.009
0.009049999
0.009099999
0.009149999
0.0092
0.00925
...
0.00945
0.0095
0.009549999
0.009599999
0.00965
0.0097
...
0.0409
0.04095
0.04099999
0.04105
0.0411
...
0.0442
0.04425
0.04429999
0.04435
0.0444
...
0.0453
0.04535
0.04539999
0.04545
0.0455
0.04555
0.04559999
0.04565
0.0457
...
0.0488
0.04885
0.04889999
0.04895
0.049
0.04905
0.0491
0.04914999
0.0492
0.04925
0.0493
0.04934999
0.0494
0.04945
...
0.04975
0.0498
0.04985
0.0499
0.04995
>>> 

Verbesserung durch Integer[Bearbeiten | Quelltext bearbeiten]

Mit dem Umweg über Integer:

# float_test_001.py
#
# Addiert Fließkommazahlen mit verschiedenen Methoden
#

faktor = 1e5
int_step = int(step*faktor)

step = 0.00005
x_add = 0.0
x_mul = 0.0
x_int = 0

for i in range(300):
    x_add += step
    x_mul = step*(i+1)
    x_int = ((x_int * faktor) + int_step) / faktor
    print(f'{i:5}: {x_add:<12} {x_mul:<12} {x_int:<12}')

Der Fehler entsteht durch die Summierung:

# in der Schleife:
101: 0.0051      
102: 0.00514999

# als einzelne Berechnung in der REPL:
>>> 0.0051+0.00005
0.00515
>>>    

Lösung mit Stringformatierung[Bearbeiten | Quelltext bearbeiten]

Noch 'ne Möglichkeit:

>>> a = f'{0.0009999999:.5f}'
>>> a
'0.00100'
>>> float(a)
0.001
>>>