Seite 2 von 3
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 14:57
von Sirius3
@Qubit: Dezimalzahlen können nicht immer exakt durch binäre Floats dargestellt werden, 2.635 ist in Wirklichkeit
Und damit kleiner als 2.635 und wird deshalb abgerundet.
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 16:27
von DeaD_EyE
Kaufmännisches Runden geht auch:
Code: Alles auswählen
from decimal import Decimal, localcontext, ROUND_HALF_UP
def wechselgeld(betrag: str, münzen: list[Decimal]):
with localcontext(rounding=ROUND_HALF_UP):
betrag = round(Decimal(betrag), 2)
münzen = sorted(münzen, reverse=True)
ergebnis = {}
for münze in münzen:
menge = betrag // münze
betrag -= münze * menge
ergebnis[münze] = int(menge)
return ergebnis, betrag
münzen = [Decimal(s) / 100 for s in (1, 2, 5, 10, 20, 50, 100, 200)]
ergebnis, rest = wechselgeld("13.367", münzen)
# wir aufgerundet auf 13.37
summe = sum(value * amount for value, amount in ergebnis.items())
print("Münzen:", münzen)
print("Rest:", rest)
print("Wechselgeld:", ergebnis)
print("Summe:", summe, "Typ:", type(summe))
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 18:45
von Pitwheazle
Ach, ihr seid zu lieb!
Ich habe das jetzt mit @Sirius3 s Vorschlag mit dem Rechnen mit Cent umgesetzt ... und jetzt auch noch ergänzt, dass der Kunde nicht immer 1 Cent Stücke zur Hand hat sondern 2 Cent Stücke hinlegt.
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 20:54
von Qubit
Sirius3 hat geschrieben: Montag 29. Januar 2024, 14:57
@Qubit: Dezimalzahlen können nicht immer exakt durch binäre Floats dargestellt werden, 2.635 ist in Wirklichkeit
Und damit kleiner als 2.635 und wird deshalb abgerundet.
Jup, wobei ja auch diese Float-Darstelllung so nur max. auf 8 Stellen genau sein sollte.
Man könnte jetzt die "round"-Funktion diesbezüglich selbst heilen, wobei das aber eigentlich implementiert sein sollte, oder?
zB.
Code: Alles auswählen
round_float = lambda number, prec=0: round(round(10**prec*number,1))/10**prec
zahl = 2.635
print(round_float(zahl,2))
print(round_float(zahl,20))
print(round_float(zahl,200))
2.64
2.635
2.635
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 21:31
von narpfel
@Qubit: Das ist jetzt aber nicht richtig, weil 2.635 näher an 2.63 als an 2.64 ist.
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 21:50
von Qubit
narpfel hat geschrieben: Montag 29. Januar 2024, 21:31
@Qubit: Das ist jetzt aber nicht richtig, weil 2.635 näher an 2.63 als an 2.64 ist.
Was heisst näher?
Der Abstand ist in den reellen Zahlen R über eine (Standard-) Metrik definiert ("Euklidische Metrik"), und da gilt:
|2.635-2.64| = |2.635-2.63|=0.005
Was heisst falsch?
Es geht um die Rundungsvorgabe für die "round"-Funktion. Diese soll nach ""wissenschaftlicher Rundung" erfolgen.
Das tut sie aber nicht korrekt.
Schau dir am Besten noch mal insbesondere Regel 3 des hier irgendwo verlinken Wikipedia-Artikels an.
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 22:26
von Sirius3
@qubit: Deine Rundung ist aber falsch, wenn ich die Zahl 2.63499999999999978684 runden möchte, wird fälschlicherweise aufgerundet.
Du hast nur den einen nummerischen Fehler durch einen anderen ersetzt. Zum Glück ist klar definiert, wie gerundet werden sollte, und das ist nicht die Näherungsformel, die Du erfunden hast, weil dessen Fehler zu groß ist.
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 22:26
von narpfel
@Qubit: 2.635 ist keine reelle Zahl, sondern ein 64-Bit-IEEE-754-Wert. Und in dieser Darstellung ist 2.635 == 2.6349999999999997868371792719699442386627197265625, und das ist eben klar näher an 2.63 als an 2.64.
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 22:45
von Qubit
Sirius3 hat geschrieben: Montag 29. Januar 2024, 22:26
@qubit: Deine Rundung ist aber falsch, wenn ich die Zahl 2.63499999999999978684 runden möchte, wird fälschlicherweise aufgerundet.
Du hast nur den einen nummerischen Fehler durch einen anderen ersetzt. Zum Glück ist klar definiert, wie gerundet werden sollte, und das ist nicht die Näherungsformel, die Du erfunden hast, weil dessen Fehler zu groß ist.
Verstehe da jetzt leider nicht ganz was du meinst, welche Rundung genau?
Code: Alles auswählen
round_float = lambda number, prec=0: round(round(10**prec*number,1))/10**prec
zahl = 2.63499999999999978684
print(f"{round_float(zahl,19):.19f}")
print(f"{round(zahl,19):.19f}"
2.6349999999999997868
2.6349999999999997868
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 22:46
von Qubit
narpfel hat geschrieben: Montag 29. Januar 2024, 22:26
@Qubit: 2.635 ist
keine reelle Zahl, sondern ein 64-Bit-IEEE-754-Wert. Und in dieser Darstellung ist 2.635 == 2.6349999999999997868371792719699442386627197265625, und das ist eben klar näher an 2.63 als an 2.64.
Doch "2.635" ist eine reelle Zahl.
Was du meinst, sind "Maschinenzahlen", also die interne Darstellung dieser Zahl.
Es geht aber um die "wissenschaftliche Rundung" (innerhalb der darstellbaren Genauigkeit).
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 23:38
von nezzcarth
@Qubit: Das Verhalten, das du bemängelst, wird in der Doku von round explizit angesprochen und wie von Sirius3 und narpfel beschrieben erklärt (
https://docs.python.org/3/library/functions.html#round). Daher würde ich das nicht als Fehler sehen. Der Begriff "wissenschaftliches Runden" taucht in der Doku übrigens nicht auf; den hatte ich nur angeführt und man sollte sich daran vielleicht nicht aufhalten.

Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 23:39
von narpfel
@Qubit: In Python eben nicht. Da gibt es keine reellen Zahlen. Kann es auch gar nicht, weil man im allgemeinen Fall nicht wirklich damit rechnen kann. Unter anderem, weil fast alle reellen Zahlen nicht berechenbar sind (es kann also keine Turing-Maschine (und damit auch kein Python-Programm) geben, das die Zahl berechnet). Außerdem sind fast alle reellen Zahlen nichtmal definierbar.
Im Kontext von Programmiersprachen muss man damit immer mit Untermengen der reellen Zahlen rechnen. Und da ist 2.635 eben eine 64-Bit-IEEE-754-Zahl. Wenn man eine andere Untermenge (mit eventuell anderen arithmetischen Eigenschaften) haben will, gibt es z. B. `decimal.Decimal` für Dezimalzahlen mit beliebiger (endlicher) Präzision oder `fractions.Fraction` für rationale Zahlen.
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 23:44
von Qubit
nezzcarth hat geschrieben: Montag 29. Januar 2024, 23:38
@Qubit: Das Verhalten, das du bemängelst, wird in der Doku von round explizit angesprochen und wie von Sirius3 und narpfel beschrieben erklärt (
https://docs.python.org/3/library/functions.html#round). Daher würde ich das nicht als Fehler sehen. Der Begriff "wissenschaftliches Runden" taucht in der Doku übrigens nicht auf; den hatte ich nur angeführt, man sollte sich jedoch nicht dran aufhalten.
Naja, nur weil es "dokumentiert" ist, ist es noch nicht von Fehlerhaftigkeit befreit (die Anforderungen zählen).
Bei der Anforderung des "wissenschaftlichen Rundens" geht es eben nicht um "Maschinenzahlen" sondern "reelle Zahlen" der Anwender.
Oder anders gesagt: ich nutze bestimmt nicht Python, um mir gerade über die interne Darstellung von Zahlen Gedanken zu machen.

Es ist zwar Schade, dass die "round"-Funktion versagt, aber man sollte die Grenzen und Alternativen kennen.
Eben so ein Workaround von mir (schlecht) oder saubere Implementierungen wie "Decimal" (gut)..
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 23:53
von Dennis89
Hallo,
`round` würde versagen, wenn sich `round` anders verhalten würde als in der Doku beschrieben. Da aber beschrieben ist, was `round` kann und wie es sich in bestimmten Fällen verhält, ist es dann Anwendersache, die für seine Ansprüche richtige Funktion auszusuchen.
Grüße
Dennis
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 23:55
von Qubit
narpfel hat geschrieben: Montag 29. Januar 2024, 23:39
@Qubit: In Python eben nicht. Da gibt es keine reellen Zahlen. Kann es auch gar nicht, weil man im allgemeinen Fall nicht wirklich damit rechnen kann. Unter anderem, weil fast alle reellen Zahlen nicht berechenbar sind (es kann also keine Turing-Maschine (und damit auch kein Python-Programm) geben, das die Zahl berechnet). Außerdem sind fast alle reellen Zahlen nichtmal
definierbar.
Im Kontext von Programmiersprachen muss man damit
immer mit Untermengen der reellen Zahlen rechnen. Und da ist 2.635 eben eine 64-Bit-IEEE-754-Zahl. Wenn man eine andere Untermenge (mit eventuell anderen arithmetischen Eigenschaften) haben will, gibt es z. B. `decimal.Decimal` für Dezimalzahlen mit beliebiger (endlicher) Präzision oder `fractions.Fraction` für rationale Zahlen.
Sorry, ich weiss nicht genau, was du mir erklären willst?
Dass man mit Computern nicht richtig runden kann?
Das glaube ich zwar nicht, aber du vermischt hier für mich einiges was man auseinanderhalten sollte..
Re: Ein Rundungsproblem
Verfasst: Montag 29. Januar 2024, 23:59
von Qubit
Dennis89 hat geschrieben: Montag 29. Januar 2024, 23:53
`round` würde versagen, wenn sich `round` anders verhalten würde als in der Doku beschrieben. Da aber beschrieben ist, was `round` kann und wie es sich in bestimmten Fällen verhält, ist es dann Anwendersache, die für seine Ansprüche richtige Funktion auszusuchen.
Das kann man natürlich so sehen.
Besser wäre es aber, wenn die "round"-Funktion einfach richtig nach "wissenschaftlicher Rundung" funktionieren würde.
Wie gesagt, Dokumentation ist gut, richtige Funktion aber besser

Re: Ein Rundungsproblem
Verfasst: Dienstag 30. Januar 2024, 00:22
von narpfel
@Qubit: Wie willst du einen Datentyp für reelle Zahlen definieren, wenn fast alle reellen Zahlen nicht berechenbar sind? Also wirklich für reelle Zahlen und nicht für irgendeine Untermenge? Wenn das nicht geht, dann ja, man kann mit Computern nicht richtig runden. Unter anderem, weil man fast alle Zahlen nichtmal
darstellen kann.
Die reelle Zahl 2,635 ist nicht exakt als `float` darstellbar. Mit dem Datentyp `float` ist es also prinzipiell unmöglich, sie „korrekt“ (nach deiner Definition) zu runden. Weil 2,635 eben keine `float`-Zahl ist.
Ist `math.sqrt` auch inkorrekt?
Code: Alles auswählen
In [27]: math.sqrt(2) ** 2
Out[27]: 2.0000000000000004
In [28]: math.sqrt(2) ** 2 == 2.0
Out[28]: False
Re: Ein Rundungsproblem
Verfasst: Dienstag 30. Januar 2024, 00:39
von Qubit
narpfel hat geschrieben: Dienstag 30. Januar 2024, 00:22
@Qubit: Wie willst du einen Datentyp für reelle Zahlen definieren, wenn fast alle reellen Zahlen nicht berechenbar sind? Also wirklich für reelle Zahlen und nicht für irgendeine Untermenge? Wenn das nicht geht, dann ja, man kann mit Computern nicht richtig runden. Unter anderem, weil man fast alle Zahlen nichtmal
darstellen kann.
Die reelle Zahl 2,635 ist nicht exakt als `float` darstellbar. Mit dem Datentyp `float` ist es also prinzipiell unmöglich, sie „korrekt“ (nach deiner Definition) zu runden. Weil 2,635 eben keine `float`-Zahl ist.
Ist `math.sqrt` auch inkorrekt?
Code: Alles auswählen
In [27]: math.sqrt(2) ** 2
Out[27]: 2.0000000000000004
In [28]: math.sqrt(2) ** 2 == 2.0
Out[28]: False
Hm, ich bin ehrlich gesagt im Zweifel, ob du wirklich Ahnung hast, worüber du sprichst..
Ad
1) Natürlich lässt sich jede reelle Zahl beliebig genau (nach Vorgabe) berechnen.
Dazu kannst du z.B. Intervallschachtelungen oder sogen. "Dedekindsche Schnitte" verwenden..
2) Das "Float-Number-" Problem ist genau eines der "internen Präzision". Innerhalb dereren Genauigkeit kann man "reelle Zahlen" auch exakt darstellen.
3) Natürlich ist dein Beispiel des Quadrats der Wurzel inkorrekt, aber nicht innerhalb der Genauigkeit..
Re: Ein Rundungsproblem
Verfasst: Dienstag 30. Januar 2024, 11:24
von Qubit
narpfel hat geschrieben: Dienstag 30. Januar 2024, 00:22
@Qubit: Wie willst du einen Datentyp für reelle Zahlen definieren, wenn fast alle reellen Zahlen nicht berechenbar sind?
Sorry, ich glaube, wie sprechen auch einander vorbei..
Unstrittig ist, dass Computer mit Maschinenzahlen rechnen.
Und Maschinenzahlen sind keine Dezimalzahlen der Mathematik (zB. kein "Körper" wie rationale oder reelle Zahlen).
Aber diese Maschinenzahlen sind interne Darstellungen dieser Dezimalzahlen.
Und im Kontext eines Algorithmus (wie zB. der der "wissenschaftlichen Rundung"), lassen sich so zwei Maschinenzahlen (als interne Darstellung von Dezimalzahlen) innerhalb der Genauigkeit(!) ineinander überführen,
also hier zB:
Code: Alles auswählen
von
>>> f"{2.635:.20f}"
'2.63499999999999978684'
mit Rundungsalgorithmus
nach
>>> f"{2.64:.20f}"
'2.64000000000000012434'
Aber der (nicht ganz korrekte) Algorithmus der "round"-Funktion liefert hier als Erghebnis
Re: Ein Rundungsproblem
Verfasst: Dienstag 30. Januar 2024, 12:58
von __blackjack__
@Qubit: Du zeigst hier immer Dezimaldarstellungen der Zahlen, intern werden die aber Binär repräsentiert und auch die Operationen erfolgen auf dieser Binärdarstellung. Du müsstest also schon zeigen das auf Binärebene falsch gerundet wird und das es tatsächlich besser ginge, wobei dabei auch bedacht werden muss, dass viele Schritte nicht in Software gemacht werden, sondern in Hardware, und an *den* Algorithmen kann man nichts ändern, es sei denn man will den Vorteil von einer schnelle(re)n Verarbeitung in der Hardware verlieren. → Das will keiner.