Wieder mal Runden von Float

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Hase
User
Beiträge: 101
Registriert: Donnerstag 1. Oktober 2009, 15:17
Wohnort: Bremer Speckgürtel

Hallo,
wie bekomme ich es hin, dass der Grenzfall

Code: Alles auswählen

round(4.05,1)
zu 4.1 und nicht zu 4.0 gerundet wird. Mit round() bin ich sonst ganz zufrieden, sonst tut es genau das, was es soll. Ich habe mir auch "Floating Point Arithmetic: Issues and Limitations" duchgelesen. Jetzt weiß ich zwar, warum es nicht immer klappt. Es steht aber leider nicht da, wie ich das Problem umschiffen kann.

Das Ganze gehört zu einem ziemlich komplexen Programm und ich möchte vermeiden, größere Teile komplett neu zu schreiben. Ich suche also eine einfache, sichere und wirkungsvolle Lösung.

Danke

I.H.


Python 2.7

The behavior of round() for floats can be surprising
Boa
User
Beiträge: 190
Registriert: Sonntag 25. Januar 2009, 12:34

Eine Möglichkeit wäre eine Ungenauigkeit miteinzubeziehen und den Fall X.X5 separat zu behandeln. Vermutlich gibt es eine einfachere Lösung :)

Code: Alles auswählen

from decimal import Decimal
dec = Decimal(4.05)+Decimal(0.000001) # Ungenauigkeit von einer Promille berücksichtigen
to_round = int(dec*Decimal(100))           # Zu Ganzzahl konvertieren
if to_round % 10 == 5:                           # um zu sehen ob sie mit dem Spezialfall 5 endet
    rounded = round((to_round + Decimal(5)) / Decimal(100), 1)   #Addiere 0,05 um sicher zu gehen, dass aufgerundet wird
else:
    rounded =  round(dec)                        # Ansonsten normal runden
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@Boa: Du beziehst keine Ungenauigkeit mit ein, sondern fügst nur noch weitere hinzu. Unter Umständen werden die Berechnungen sogar fehlerhaft.
Hase hat geschrieben:Ich habe mir auch "Floating Point Arithmetic: Issues and Limitations" duchgelesen. Jetzt weiß ich zwar, warum es nicht immer klappt. Es steht aber leider nicht da, wie ich das Problem umschiffen kann.
Doch, das steht da. Es wird auf das decimal-Modul hingewiesen, mit dem du solche Probleme umgehen kannst.
Hase hat geschrieben:Das Ganze gehört zu einem ziemlich komplexen Programm und ich möchte vermeiden, größere Teile komplett neu zu schreiben. Ich suche also eine einfache, sichere und wirkungsvolle Lösung.
Du wirst nichts neu schreiben müssen, das Durchgehen deines gesamten Codes wird allerdings nötig sein. Wenn du die von dir gewünschten Garantien haben willst, dann musst du alle Vorkommnisse von ``float``s durch ``decimal.Decimal`` ersetzen. Sonst werden auch alle Zwischenschritte fehlerhaft berechnet. Ich hoffe, dass du gute und ausreichend viele Unit-Tests hast, sonst kann das ekelhaft werden ;-)
Das Leben ist wie ein Tennisball.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Na, wenn man schon mal `decimal` einbringt, kann man es auch direkt zum Runden benutzen und damit mehr Kontrolle zu haben _wie_ gerundet wird.

Vorausgesetzt du willst bei 5 immer aufrunden:

Code: Alles auswählen

In [14]: import decimal

In [15]: d = decimal.Decimal('4.05')

In [16]: d.quantize(decimal.Decimal('0.1'), rounding=decimal.ROUND_HALF_UP)
Out[16]: Decimal('4.1')
Antworten