Hey ho,
kann mir jemand erklären, warum sich die ganzzahlige Division so verhält wie im folgenden Codebeispiel dargestellt?
[codebox=pycon file=Unbenannt.txt]
>>> 30 // 1 # hier wie erwartet
30
>>> 3 // 0.1 # warum?
29
[/code]
Mit freundlichen Grüßen
kemc
Ganzzahlige Division von Fließkommazahlen
@Liffi: Keine überzeugende Erklärung weil:
Das scheint trotz Gleitkommabeschränkungen recht glatt 30 zu ergeben.
Code: Alles auswählen
In [8]: '%.60f' % (3 / 0.1)
Out[8]: '30.000000000000000000000000000000000000000000000000000000000000'
Code: Alles auswählen
>>> '%.60f' % (0.1)
'0.100000000000000005551115123125782702118158340454101562500000'
'//' scheint sich demnach auch wie divmod zu verhalten:So, it seems that Python's floating-point division is internally done with high enough precision to know that 1 / 0.05 (that's the float literal 0.05, not the exact decimal fraction 0.05), is actually less than 20, but the float type in itself is incapable of representing the difference.
Code: Alles auswählen
>>> divmod(3,0.1)
(29.0, 0.09999999999999984)
Man könnte es mit dem fractions-Modul lösen (wenn ich nichts übersehen habe):
Oder gleich als Funktion:
Es sollte nur klar sein, dass hier ein entsprechender Overhead erzeugt wird (für beide Operanden eine String-Umwandlung + Erzeugung des Fraction-Objekts). Nur für den Fall, dass das eine Rolle spielt...
Code: Alles auswählen
from fractions import Fraction as F
print(3 // F('0.1'))
Code: Alles auswählen
from fractions import Fraction
def floordiv(a, b):
return Fraction(str(a)) // Fraction(str(b))
def main():
print('Without fractions:', 3 // 0.1)
print('With fractions:', floordiv(3, 0.1))
if __name__ == '__main__':
main()
Zuletzt geändert von snafu am Dienstag 24. Januar 2017, 10:25, insgesamt 2-mal geändert.
Hab gerade gesehen das Hy für `Fraction` Literale hat.
Code: Alles auswählen
=> 1/2
Fraction(1, 2)
=> (// 3 0.1)
29.0
=> (// 3 1/10)
30
Hier nochmal floordiv etwas optimiert:
Damit entfällt der relativ aufwändige Umweg über das fractions-Modul für Divisionen, die bereits auf "normalem" Wege das korrekte Ergebnis liefern.
Code: Alles auswählen
from fractions import Fraction
def floordiv(a, b):
result = a // b
if result * b != a:
result = Fraction(str(a)) // Fraction(str(b))
return result
Vielleicht noch als Zusatz: Mit dem decimal-Modul kann man dein Problem ganz ähnlich lösen. Es verhielt sich bei meinen Tests (in IPython mit timeit-Kommando gemessen) deutlich schneller als das fractions-Modul. Den passenden Code dazu habe ich so geschrieben:
Code: Alles auswählen
from decimal import Decimal
def floordiv(a, b):
result = a // b
if result * b != a:
# Dealing with at least one "problematic" float
result = float(Decimal(str(a)) // Decimal(str(b)))
return result
Wobei das noch stark verbesserungsdürftig ist, denn z.B. bei 10 // 3 passiert ja eigentlich nichts problematisches. Ich war wohl sehr auf das Beispiel fixiert und hatte trotz der Benennung nicht bedacht, dass "result * b != a" auch bei normal darstellbaren Floats und natürlich auch bei Integern vorkommen kann. Der Decimal-Code wird also deutlich öfter durchlaufen als nötig und die Annahme, dass mindestens ein Float enthalten ist, trifft auch nicht immer zu.
Vielleicht geht es noch anders, aber ich habe das jetzt mit divmod() gelöst. Ich gucke einfach, ob der Rest eine Ganzzahl ist.
Vielleicht geht es noch anders, aber ich habe das jetzt mit divmod() gelöst. Ich gucke einfach, ob der Rest eine Ganzzahl ist.
Code: Alles auswählen
from decimal import Decimal
def floordiv(a, b):
div, mod = divmod(a, b)
if mod != int(mod):
# Dealing with at least one "problematic" float
div = float(Decimal(str(a)) // Decimal(str(b)))
return div