Zahlen kaufmännisch Runden

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.
Märzhase
User
Beiträge: 2
Registriert: Sonntag 31. Oktober 2010, 19:05

Hi,

ich mache gerade erste Schritte in Python 3 und habe mir vorher Python 2 ein wenig angeschaut. Bei meinen ersten Programmen bin ich jetzt auf das berühmte Rundungsproblem gestoßen: Python 3 rundet halbe Zahlen ab. Ich verstehe, dass das in den meisten Programmen exaktere Ergebnisse bringt, aber ich benötige nun die Möglichkeit klassisch zu runden. Soweit ich mich informiert habe, könnte ich dazu das decimal-Paket verwenden. Ich habe damit ein bisschen rumgespielt, und offensichtlich ist quantize aus diesem Paket das was ich verwenden könnte. Das ganze erscheint mir allerdings unnötig kompliziert und irgendwie etwas overkill für meine Anwendung. Gibt es noch eine andere (unkompliziertere) Möglichkeit halbe Zahlen aufzurunden?
Benutzeravatar
DrFaust
User
Beiträge: 21
Registriert: Freitag 15. Oktober 2010, 23:10

Löst die Funktion round() dein Problem nicht? Die sollte auch ohne jedwede imports verfügbar sein.
BlackJack

@DrFaust: Diese Funktion *ist* das Problem. Die rundet bei Python 3.x den Wert 0.5 auf 0 und nicht auf 1 wie Märzhase das gerne hätte.

Code: Alles auswählen

Python 2.6rc1 (r26rc1:66416, Sep 15 2008, 15:38:46)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> round(0.5)
1.0
vs.

Code: Alles auswählen

Python 3.1.1 (r311:74480, Oct  6 2009, 20:04:02)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> round(0.5)
0
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

So vielleicht? (Hab den komplizierten Integer-Quatsch mal entfernt)

Code: Alles auswählen

r = lambda x : int(x+.5) if x>0 else int(x-.5)
Bin mir allerdings nicht sicher, ob das für negative Werte den käufmännischen Regeln entsprechend rundet.

Edit:
Runden auf die n-te Nachkommastelle:

Code: Alles auswählen

r = lambda x, n=0 : x/abs(x) * int(abs(x) * 10**n + .5) / 10**n
Märzhase
User
Beiträge: 2
Registriert: Sonntag 31. Oktober 2010, 19:05

wunderbar, das scheint perfekt zu funktionieren, danke!
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

BlackJack hat geschrieben:@DrFaust: Diese Funktion *ist* das Problem. Die rundet bei Python 3.x den Wert 0.5 auf 0 und nicht auf 1 wie Märzhase das gerne hätte.
Das ist ja ein echter Knaller!

Code: Alles auswählen

Python 3.1.2 (r312:79147, Apr 15 2010, 15:35:48) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> round(0.5)
0
>>> round(1.5)
2
>>> round(2.5)
2
Damit ist round() doch überhaupt nicht mehr zu gebrauchen.
Benutzeravatar
DrFaust
User
Beiträge: 21
Registriert: Freitag 15. Oktober 2010, 23:10

@numerix: Ich wollte es nicht glauben, als du das hier gepostet hast. Aber, es ist bei mir genau so. Was bitte haben sie sich dabei gedacht. Was immer sie genommen haben, ich will auch... ;-)
schorsch
User
Beiträge: 18
Registriert: Montag 26. November 2007, 18:39

Aus der Doku:
if two multiples are equally close, rounding is done toward the even choice (so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2).
Hintergrund des ganzen: http://de.wikipedia.org/wiki/Rundung#Ma ... he_Rundung
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

War mir auch neu. Eben nachgelesen. Bei Python 3.x gilt mathematisches unverzerrtes Runden ("round-to-even"), davor bei Python 2.x war es "round-away-from-zero", was AFAIK das selbe wie kaufmännisches Runden ist. Siehe http://de.wikipedia.org/wiki/Rundung für die Unterschiede (die englische Variante der Seite hat noch mehr Algorithmen).

Will man exakt mit Kommazahlen rechnen, ist das decimal-Modul richtig. Fließkommazahlen funktionieren prinzipbedingt nicht. Python erlaubt es bei Dezimalzahlen, die Rundungsmethode zu definieren: ROUND_05UP, ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP - entweder global oder pro Rechenoperation.

Stefan
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Eigentlich hätte ich ja von Gerold einen Beitrag dazu erwartet; als Experte für Kassensysteme muss er sich da doch auskennen :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

Nur eine Frage, da ich py3 nicht installiert habe.
Werden diese Rundungsregeln auch angewendet, während der Formatierung von Zeichenketten. Als wenn man z.Bsp %.3f oder so was oder format() verwendet?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Code: Alles auswählen

>>> "%.0f"%0.5
'0'
>>> "%.0f"%1.5
'2'
>>> "%.0f"%2.5
'2'
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

sma hat geschrieben:Will man exakt mit Kommazahlen rechnen, ist das decimal-Modul richtig.
Ärgerlich daran ist aber doch, dass man sich damit auch die Nachteile des decimal-Moduls miteinkauft, bloß weil man mal was runden will (als da wären aus meiner Sicht: umständlicher in der Handhabung als floats und vor allem die schlechte Performance).

Ich hielte es für eine gute Lösung, wenn man round einen zusätzlichen Schlüsselwortparameter spendieren würde, so dass man die Rundungsregel ähnlich (aber ggf. weniger differenziert) einstellen kann wie beim decimal-Modul.
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

Uhh. Ich würde sagen verschlimmbessert.
don dirko
User
Beiträge: 3
Registriert: Mittwoch 11. Juli 2018, 15:50

Doppelt runden hält besser :shock:
Ich verzweifelte schier daran, eine Meterzahl in eine korrekt gerundete Kilometerzahl zu runden. So ergab zum Beispiel print round(422999 / 1000) immer den abgerundeten Wert 422, mathematisch korrekt müsste aber ab 422500 Meter auf 423 aufgerundet werden. Die Lösung war dann die doppelte Rundung:

print int(round(round(422999) / 1000))
Ergebnis: 423

Die int-Funktion dient nur der Optik (Null nach Komma abschneiden) und ändert nichts am Ergebnis.
Vielleicht findet ja noch ein schlauer Kopf die Erklärung dafür, ich weiß nur, dass es in Python 2.7 funktioniert.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

wenn Du Python 2.7 benutzt, dann ist die Division / ein Ganzzahldivision. Von daher solltest Du Fließkommadivision als default einstellen. Runden sollte man erst bei der Ausgabe:

Code: Alles auswählen

from __future__ import division
print "{:.0f}".format(422999 / 1000)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@don dirko: Also ich weiss nicht was Du meinst. Hier Python 2.7:

Code: Alles auswählen

In [5]: print round(422999 / 1000)
422.0

In [6]: print round(round(422999 / 1000))
422.0
Und hier 3.5:

Code: Alles auswählen

In [2]: print(round(422999 / 1000))
423

In [3]: print(round(round(422999 / 1000)))
423
In keinem der beiden Fälle bringt doppeltes `round()` irgendwas. Wäre auch sehr verwunderlich.

Der Unterschied zwischen den beiden ist hauptsächlich der hier:
Python 2:

Code: Alles auswählen

In [7]: 422999 / 1000
Out[7]: 422
Python 3:

Code: Alles auswählen

In [4]: 422999 / 1000
Out[4]: 422.999
In Python 2 ergibt die Division von zwei `int`-Werten einen `int`-Wert, in Python 3 dagegen einen `float`-Wert.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@__blackjack__: wichtig ist die Klammernsetzung. `round(422999)` wandelt erst die Zahl in float, die dann korrekt geteilt werden kann. Auch so ein Punkt, der sich zwischen Python 2 und 3 grundsätzlich geändert hat.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ups, glatt übersehen das `round()` als `float()` missbraucht wurde. :oops:
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
c.burkes
User
Beiträge: 58
Registriert: Montag 4. Februar 2019, 11:38

Moin =)


Reanimation eines alten Threads .... check xD

... das von ...
sma hat geschrieben: Montag 1. November 2010, 10:30 unverzerrtes Runden ("round-to-even"),
... beschriebene betrifft auch die Umwandlung von einem float-Wert in einen int-Wert? Sprich, da wird einfach auf Grund des Prinzipes des Programmes alles nach dem Komma weggecuttet, was auch immer da steht? Es besteht ansonsten keine mathematische Notwendigkeit oder der Gleichen?

Bin gerade, wie Märzhase, im Rahmen einer Uebung darueber gestolpert und erinnere mich dunkel, dazu mal nen Satz gelesen zu haben. Leider war das nur ne Erwaehnung und keine Erklaerung ...


lg, c.b
Antworten