Fließkomma rechnen mit Micropython: Unterschied zwischen den Versionen

Aus Micropython Referenz
Zur Navigation springen Zur Suche springen
Peter (Diskussion | Beiträge)
Die Seite wurde neu angelegt: „Das Rechnen mit Fließkommazahlen bringt immer Ungenauigkeiten mit sich. Gerade bin ich auf einen Effekt bei der Addition von vielen Fließkommazahlen mit einem M5Stack gestoßen:<br> <pre> >>> 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.000899999…“
 
Peter (Diskussion | Beiträge)
 
(12 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
Das Rechnen mit Fließkommazahlen bringt immer Ungenauigkeiten mit sich. Gerade bin ich auf einen Effekt bei der Addition von vielen Fließkommazahlen mit einem M5Stack gestoßen:<br>
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==
<pre>
>>> 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
>>>
</pre>
Der erste Fehler tritt schon bei der Zuweisung in der ersten Zeile auf.<br>
Die 2 in der 12. Stelle (11. Nachkommastelle) wird ignoriert.<br>
Wenn die die 2 aber an der 11. Stelle (10. Nachkommastelle) steht wird sie berücksichtigt. Hier aber gleich mit einem Rundungsfehler.
 
== Additions-/Rundungsfehler==
Gerade bin ich auf einen Effekt bei der Addition von vielen Fließkommazahlen mit einem M5Stack gestoßen:<br>
=== Additionsfehler===
<pre>
<pre>
>>> step = 0.00005
>>> step = 0.00005
Zeile 46: Zeile 75:
0.003899997
0.003899997
0.003949997
0.003949997
...
0.007049992
0.007049992
0.007099992
0.007099992
Zeile 54: Zeile 84:
0.007349991
0.007349991
0.007399991
0.007399991
</pre>
=== Multipikationsfehler===
Mit einer Multiplikation statt Addition ist das Ergebnis befriedigender:
<pre>
>>> 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
>>>
</pre>
=== Verbesserung durch Integer===
Mit dem Umweg über Integer:
<pre>
# 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}')
</pre>
Der Fehler entsteht durch die Summierung:
<pre>
# in der Schleife:
101: 0.0051     
102: 0.00514999
# als einzelne Berechnung in der REPL:
>>> 0.0051+0.00005
0.00515
>>>   
</pre>
=== Lösung mit Stringformatierung===
Noch 'ne Möglichkeit:
<pre>
>>> a = f'{0.0009999999:.5f}'
>>> a
'0.00100'
>>> float(a)
0.001
>>>
</pre>
</pre>

Aktuelle Version vom 18. März 2025, 12:33 Uhr

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