2016-06-15 8 views
2

Ich versuche, Geld Zahlen in Dezimal auf die nächsten 0,05 Runden. Gerade jetzt, ich tue dies:Runde Python Dezimal zu nächsten 0,05

def round_down(amount): 
    amount *= 100 
    amount = (amount - amount % 5)/Decimal(100) 
    return Decimal(amount) 

def round_up(amount): 
    amount = int(math.ceil(float(100 * amount)/5)) * 5/Decimal(100) 
    return Decimal(amount) 

Gibt es eine Möglichkeit, dies eleganter tun kann, ohne mit Schwimmern Umgang mit Python Dezimalzahlen (mit Quantisierungsraster vielleicht)?

+0

Warum nicht die Runde in Python verwenden? –

+0

@SeekAddo. Wie würde ich auf den nächsten 0,05 runden? Außerdem muss ich die Rundungsrichtung kontrollieren (dh ich brauche sowohl Aufrunden/Abrunden). – gurch101

+0

def round_num (Betrag): print (rund (Betrag, 2)) round_num (23.045954) gibt Ihnen 23.05, lassen Sie mich fragen, wie möchten Sie die Runde wie @ gurch101 sein –

Antwort

5

Mit Schwimmer verwenden Sie einfach round(x * 2, 1)/2. Dies gibt jedoch keine Kontrolle über die Rundungsrichtung.

Decimal.quantize Verwenden Sie auch die vollständige Kontrolle über die Art und Richtung der Rundung (Python 3.5.1) erhalten:

>>> from decimal import Decimal, ROUND_UP 

>>> x = Decimal("3.426") 
>>> (x * 2).quantize(Decimal('.1'), rounding=ROUND_UP)/2 
Decimal('3.45') 

>>> x = Decimal("3.456") 
>>> (x * 2).quantize(Decimal('.1'), rounding=ROUND_UP)/2 
Decimal('3.5') 
+0

klug. Vielen Dank! – gurch101

0

Erste Notiz dieses Problem (unerwartete Abrunden) nur manchmal tritt auf, wenn die Die Ziffer unmittelbar hinter der Ziffer, auf die Sie gerundet werden, hat eine 5.

heißt

>>> round(1.0005,3) 
1.0 
>>> round(2.0005,3) 
2.001 
>>> round(3.0005,3) 
3.001 
>>> round(4.0005,3) 
4.0 
>>> round(1.005,2) 
1.0 
>>> round(5.005,2) 
5.0 
>>> round(6.005,2) 
6.0 
>>> round(7.005,2) 
7.0 
>>> round(3.005,2) 
3.0 
>>> round(8.005,2) 
8.01 

Aber es gibt eine einfache Lösung, die ich gefunden habe, dass die Arbeit immer scheint, und die sich nicht auf die import von zusätzlichen Bibliotheken. Die Lösung besteht darin, eine hinzuzufügen, wobei X die Länge der Zahlenzeichenfolge ist, die Sie verwenden möchten round on plus 1.

>>> round(0.075,2) 

0.07 

>>> round(0.075+10**(-2*6),2) 

0.08 

Aha! So auf dieser Basis können wir eine handliche Wrapper-Funktion machen, die eigenständigen und benötigt keine zusätzlichen import Anrufe ...

def roundTraditional(val,digits): 
    return round(val+10**(-len(str(val))-1)) 

Grundsätzlich fügt dieser Wert garantiert, dass er kleiner ist als der am wenigsten gegeben Ziffer der Zeichenfolge versuchen, round zu verwenden. Durch das Hinzufügen dieser kleinen Menge wird das Verhalten der round in den meisten Fällen beibehalten, während nun sichergestellt wird, dass die Ziffer, die der abgerundeten Zahl 5 entspricht, rundet und wenn sie 4 ist, rundet sie ab.

Der Ansatz von 10**(-len(val)-1) mit Absicht war, wie es die größte kleine Zahl, die Sie den Übergang zu zwingen hinzufügen können, aber auch, dass der Wert, den Sie nie ändert sich die Rundung auch wenn das Dezimalsystem . fehlt hinzufügen zu gewährleisten. Ich könnte nur 10**(-len(val)) mit einem bedingten if (val>1) verwenden, um 1 zu subtrahieren mehr ... aber es ist einfacher, nur die 1 subtrahieren als das wird nicht viel den anwendbaren Bereich von Dezimalzahlen ändern diese Problemumgehung kann richtig umgehen. Dieser Ansatz wird fehlschlagen, wenn Ihre Werte die Grenzen des Typs erreichen, dies wird fehlschlagen, aber für fast den gesamten Bereich der gültigen Dezimalwerte sollte es funktionieren.

Sie können auch die decimal-Bibliothek verwenden, um dies zu erreichen, aber der von mir vorgeschlagene Wrapper ist einfacher und kann in einigen Fällen bevorzugt werden.


Edit: Dank Blckknght für den Hinweis auf, dass der 5 fringe Fall here nur für bestimmte Werte auftritt.