2010-10-16 18 views
18

Gibt es eine einfache, pythische Art der Rundung auf die nächste ganze Zahl, ohne Gleitkommazahl zu verwenden? Ich möchte die folgenden aber mit Integer-Arithmetik tun:Runde mit ganzzahliger Division

skip = int(round(1.0 * total/surplus)) 

==============

@John: Gleitpunkt ist nicht reproduzierbar über Plattformen hinweg. Wenn Sie möchten, dass Ihr Code Tests über verschiedene Plattformen hinweg durchführt, dann müssen Sie Gleitkommazahlen vermeiden (oder fügen Sie Ihren Tests etwas Hacky-Espilon-Zeug hinzu und hoffen, dass es funktioniert). Das Obige mag einfach genug sein, dass es auf den meisten/allen Plattformen dasselbe sein würde, aber ich möchte diese Bestimmung lieber nicht machen, da es einfacher ist, Gleitkommazahlen ganz zu vermeiden. Wie ist das "nicht im Geiste von Python"?

+7

@John: Naja, Longs in Python können beliebig große Werte speichern, bei denen Gleitkommazahlen fest eingestellt sind - also gibt es Kosten in Reichweite, Komplexität und mögliche Fehler, die Gleitkommazahlen in Integer-Operationen einführen. Ich wünschte, die Leute würden aufhören, jede Frage mit dem albernen Schlagwort "Pythonic" zu bestreuen. –

+0

@GlennMaynard Wahr! Es ist nicht sehr pythisch. –

Antwort

28

Sie können dies ganz einfach:

(n + d // 2) // d, wo n ist die Dividende und d der Divisor.

Alternativen wie (((n << 1) // d) + 1) >> 1 oder die äquivalent (((n * 2) // d) + 1) // 2 können in der letzten CPythons langsamer sein, wo ein int wie die alten long umgesetzt wird.

Die einfache Methode führt 3 Variablenzugriffe, 1 Konstantlast und 3 Ganzzahloperationen aus. Die komplizierten Methoden machen 2 Variablenzugriffe, 3 konstante Lasten und 4 ganzzahlige Operationen. Ganzzahlige Operationen benötigen wahrscheinlich Zeit, die von der Größe der beteiligten Zahlen abhängt. Variable Zugriffe auf Funktion Locals beinhalten keine "Lookups".

Wenn Sie wirklich für die Geschwindigkeit verzweifeln, tun Benchmarks. Ansonsten, KISS.

+2

+1 Diese Methode ist sowohl lesbarer als der Bit-Shifting-Ansatz als auch schneller (in Zeittests) auf Py 2.7. – snapshoe

6
skip = (((total << 1) // surplus) + 1) >> 1 

Shifting Dinge ein Bit nach links multipliziert effektiv durch zwei, Abrunden Dinge ein Bit nach rechts dividiert durch zwei zu verschieben. Wenn man einen in der Mitte hinzufügt, wird abgerundet, wenn das Ergebnis über 0,5 Dezimalstellen liegt.

Es ist im Grunde das gleiche wie wenn man ... schrieb

skip = int((1.0*total/surplus) + 0.5) 

außer mit allem, was multplied von 2 und dann geteilt später von 2, die etwas, das Sie mit Integer-Arithmetik tun (da don Bit verschiebt brauche kein Fließkomma).

+0

Richtige Idee, aber ich denke, dass die Menge, die Sie zu "total" hinzufügen müssen, "Überschuss" entspricht. Ich würde die "+ 1" durch "+ Überschuss" in Ihrer aktuellen Formel ersetzen und das wäre wahrscheinlich in etwa richtig. –

+0

Eigentlich muss ich nur die 1 nach draußen bewegen. :) Es entspricht dem Hinzufügen von überschüssigem Inhalt, erfordert jedoch weniger Suchvorgänge. – Amber

+0

Ja, das ist eine andere Möglichkeit. –

-2

Dies sollte auch funktionieren:

def rint(n): 
    return (int(n+.5) if n > 0 else int(n-.5)) 
+0

Dies ist keine Antwort auf die Frage, da es sich um Gleitkommaarithmetik in 'n + .5' handelt. @rubik Ich habe nicht downvote, das war jemand anderes. ;-) –

+1

@ArneL .: Naja, egal, wenn es falsch ist, ist es richtig runterzuschauen :) – rubik

0

einfach die Rundungsregel kümmern, bevor Sie jemals teilen. Für das einfachste Rundhalb-up:

if total % surplus < surplus/2: 
    return total/surplus 
else: 
    return (total/surplus) + 1 

Tweak ein wenig, wenn Sie tun müssen, ein richtiges Rund to-even.

+1

Der Modulo und der Division Operator sind ziemlich teuer, dieser Code läuft 3 Divisionsoperationen (ein Modulo und 2 reguläre Divisionen), also Das ist nicht optimal, wenn der Code schnell sein muss. – FrederikNS

2

Noch eine andere lustige Art und Weise:

q, r = divmod(total, surplus) 
skip = q + int(bool(r)) 
+0

Beachten Sie, dass diese Lösung auf die nächstgrößere ganze Zahl aufrundet, die nicht unbedingt die nächste ganze Zahl ist. Siehe meine Antwort für eine feste Version _ (die ich zu einem Zeitpunkt gepostet habe, als ich noch nicht genug Reputation für Kommentare hatte.) _. – Daniel

4

Inspiriert von zhmyh's answer Antwort, die

q, r = divmod(total, surplus) 
skip = q + int(bool(r)) # rounds to next greater integer (always ceiling) 

ist, kam ich auf die folgende Lösung:

q, r = divmod(total, surplus) 
skip = q + int(2 * r >= surplus) # rounds to nearest integer (floor or ceiling) 

Da der OP gefragt zum Runden auf die nächste ganze Zahl, zh mhs Lösung ist in der Tat ein wenig falsch, weil es immer auf die nächste größere ganze Zahl rundet, während meine Lösung wie gefordert funktioniert.

(Wenn Sie das Gefühl, dass meine Antwort sollte besser ein bearbeiten oder kommentieren zhmh Antwort haben, lassen Sie mich dafür, dass meine vorgeschlagene bearbeiten weisen darauf hin, abgelehnt wurde, weil es besser ein Kommentar sein sollte, aber ich weiß nicht genug Ruf noch zur Kommentierung)

Falls Sie sich fragen, wie divmod definiert ist: nach seiner documentation

für ganze Zahlen sind, ist das Ergebnis das gleiche wie (a // b, a % b).

Wir bleiben daher bei der Ganzzahlarithmetik, wie vom OP gefordert.