Rechnen mit Python ...
Liebe Pythonians, was soll so etwas denn in der heutigen Zeit? (Zufallsfund)
Nachtrag: Perl ist natürlich (wie immer) schlechter
dirk@drusus ~ $ python Python 2.7.2 (default, Jun 29 2011, 15:07:32) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> 1100-1036.04 63.960000000000036
Nachtrag: Perl ist natürlich (wie immer) schlechter
$ perl -e "print 1100-1036.04" 63.96
Trackbacks
Dirks Logbuch am : Perl 6 rechnet anders ...
Vorschau anzeigen
Die Kommentare des Artikels Rechnen mit Python kann man vermutlich nur mit einer gehörigen Ladung Popcorn ertragen, vor allem der Teil, dass der Computer bestimmt, was mathematisch korrekt ist, hat Unterhaltungswert. Damals habe ich schon angemerkt, dass
Kommentare
Ansicht der Kommentare: Linear | Verschachtelt
Dee am :
printf("%4.16g - %4.16g = %4.16g\n", 1100.0, 1036.64, 1100-1036.64 );
1100 - 1036.64 = 63.3599999999999
Floating-Point-Zahlen sind in den letzten Stellen so gut wie immer ungenau, ganz einfach weil der Computer es nicht besser kann.
Aus dem Grund sind absolute Abfragen bei Vergleichen bei Double-Werten in mathematischen Anwendungen auch eher ungewöhnlich: https://secure.wikimedia.org/wikipedia/de/wiki/Rechengenauigkeit#Dezimalzahlen
Es gibt zu dem Thema auch verschiedene Links:
http://www.codinghorror.com/blog/2009/05/why-do-computers-suck-at-math.html
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
Und zahlreiche mehr.
Ich habe leider keinen Link gefunden, der dies für verschiedene Programmiersprache aufzeigt. Das wäre nett gewesen.
Dirk Deimeke am :
Mir ist auch klar, dass bei der Wandlung Dezimal-Binär-Dezimal etwas verloren geht, aber ich bin tatsächlich davon ausgegangen, dass das mittlerweile durch geschickte Algorithmen oder Rechentabellen gelöst ist.
Kai am :
Informatikstudenten müssen sich im ersten Semester eine Zeit lang intensiver mit der mit der Berechnung von Gleitkommazahlen beschäftigen.
Wie du zu deinem krummen Ergebnis kommst, kannst du mit der Berechnung hier nachvollziehen: http://de.wikipedia.org/wiki/IEEE_754#Berechnung_Dezimalzahl_.E2.86.92_IEEE754-Gleitkommazahl
Dirk Deimeke am :
Christopher Grebs am :
In [3]: (110000.0 - 103604.0) / 100.0
Out[3]: 63.96
ein einfacher Trick, der bereits in der Grundschule funktioniert hat
Dirk Deimeke am :
onli am :
Dirk Deimeke am :
In meinem Beispiel könnte die die Intelligenz die Zahlen beispielsweise intern als 1100 10^0 und 103604 10^-2 speichern. In einem ersten Schritt würde dann aus 1100^0 = 110000 * 10^-2, danach wird mit Ganzzahlarithmetik die Differenz gebildet und für die Ausgabe das Komma um zwei Stellen verschoben.
Was die meisten Programmiersprachen aber machen, ist direkt aus den Zahlen eine Binärdarstellung zu bauen und danach erst die Arithmetik darauf los zu lassen, das führt zu stark ungenauen Ergebnissen.
onli am :
Bei deiner Methode würde man vielleicht nicht unbedingt das Komma bei der Ausgabe verschieben, sondern (Ergebnis * 10^-2) speichern und schön dargestellt ausgeben
Dirk Deimeke am :
Jochen Schnelle am :
nochmal das Ergebnis, was zwei weitere Programmiersprachen liefern:
Erlang (V5.7.4):
1> 1100-1036.04.
63.960000000000036
Lua (V5.1.4):
> return 1100-1036.04
63.96
Dirk Deimeke am :
Mein alter Taschenrechner hat intern mit 13 Stellen nach dem Komma gerechnet, bei der Ausgabe und Speichern aber auf 10 Stellen nach dem Komma gerundet.
Mit diesem Verfahren wären alle falschen Ergebnisse richtig.
Jochen Schnelle am :
so, einen noch:
Javascript, Firefox 6.0:
function rechnen() {
erg = 1100-1036.04;
window.alert(erg);
}
Ergebnis: 63.960000000000036
Dirk Deimeke am :
foobar am :
$ python -c "1100-1036.04"
63.96
Gruss
foobar
Dirk Deimeke am :
Marc am :
$ ruby -e "puts 1100-1036.04"
63.96
Dirk Deimeke am :
losingYou am :
Ruby 1.8:
ruby1.8 -e "puts 1100-1036.04"
63.96
Ruby 1.9.1:
ruby1.9.1 -e "puts 1100-1036.04"
63.960000000000036
O.o
lg,
losingYou
Dirk Deimeke am :
Jochen Schnelle am :
Wenn ich nicht nicht völlig irre ist z.B. Lua, ebenso wie Python, in ANSI C99 implementiert.
onli am :
Dirk Deimeke am :
Dirk Deimeke am :
dasdestillat am :
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1100-1036.04
63.960000000000036
>>> (1100-1036.04).__str__()
'63.96'
Damit hätte ich jetzt nicht gerechnet
Ansonsten finde ich es etwas übertrieben, von "falschen" Ergebnissen zu sprechen. "Implementierungsdetail" trifft es wohl eher. Für solche Designentscheidungen gibt es idR mehr oder weniger gute Gründe; Programmiersprachen sind halt keine Taschenrechner
Dirk Deimeke am :
Taschenrechner rechnen noch ungenauer, zeigen es aber nicht an.
Gerade Programmiersprachen könnten es besser machen, zumindest im interaktiven Modus, in dem es nun wirklich nicht auf Geschwindigkeit ankommt.
Dieses Beispiel ist ein ganz einfaches, das durch geschickte Implemtierung genau ausgerechnet werden könnte.
posativ am :
Ich werfe hier mal in die Runde, dass der Erschaffer von Python, namentlich Guido van Rossum, _Mathematiker_ ist. Du kannst ja gerne einen Bug-Report lösen: bugs.python.org
ps: zum __str__():
In [15]: (1100-1036.04).__str__()
Out[16]: '63.96'
In [17]: (1100-1036.04).__repr__()
Out[17]: '63.960000000000036'
Dirk Deimeke am :
Mit einer andere Meinung oder damit, dass Python selbst einfache Aufgaben falsch rechnet?
posativ am :
Einzig eine ideale Turing-Maschine könnte in der Binärdarstellung von Zahlen korrekt rechnen (AFAIK). Bräuchte dafür allerdings unendlich lange.
Um mal wieder zur Realität zurückkommen. IEEE 754 ist ein verdammt gutes Verfahren zur Speicherung von Gleitkommazahlen, die ein Rechner per definitionem nicht verarbeiten kann. Ein Runden (was man als "richtig" empfindet) ist nur ästhetisch sinnvoll.
Dirk Deimeke am :
Das Argument, was Du anführst, erinnert ein bisschen an die Deutsche Bahn, die definierte, dass nur die Züge "zu spät" sind, die später als 5 Minuten nach der Abfahrtzeit (!) ankamen.
Wenn ich die Aussage nicht ändern kann, muss ich die Umgebung so biegen, dass die Aussage passt (oder auch nicht).
Das, was korrekt ist, gibt die Mathematik vor und nicht Konvertierungsverluste. Dass das Ergebnis vorhersagbar ist, ändert aber nichts am Inhalt.
Wie weiter oben beschrieben, hätte man in diesem Fall einfach die Differenz von zwei Integern bilden können.
posativ am :
Um nochmal kurz zum print-Statement zu überprüfen. Das scheint wirklich nicht gänzlich durchdacht zu sein. `print` ruft eigentlich _str_ auf, nicht __repr__
http://www.gossamer-threads.com/lists/python/dev/848610?do=post_view_threaded
Dirk Deimeke am :
posativ am :
https://secure.wikimedia.org/wikipedia/de/wiki/K%C3%B6rper_%28Algebra%29
Auch wenn ich sehe, dass du nicht mehr diskutieren willst. Ich ändere hier nicht das Universum, ich ändere einzig den Körper, indem ich rechne. Das ist mathematisch anerkannt und ohne wäre so ziemlich jede Informationstheorie nicht möglich.
Dirk Deimeke am :
Der Punkt ist nur, dass meine Eingabe im Dezimalsystem erfolgt und dass ich eine Ausgabe im Dezimalsystem erwarte (und auch bekomme). Wenn ich ein anderes Zahlsystem wollte, müsste ich das angeben, so sind die Regeln.
Was Du nun sagst, ist, dass es ein System gibt, in dem das alles korrekt ist. Das ist eine wahre Aussage. Ich habe allerdings nicht gesagt, dass ich das System wechseln wollte und damit ist das Ergebnis falsch.
Das meinte ich mit "Wechsel des Universums".
posativ am :
(und wie immer, es ist möglich mit dem Decimal-Modul).
Dirk Deimeke am :
Und jetzt kommen wir einen Schritt weiter. Wenn es ein Modul gibt, dass richtig rechnet, warum ist es nicht per Default eingeschaltet?
Genau das ist aber eine Philosophie-Frage.
posativ am :
Das Decimal-Modul wird wohl ein größeres Stück mehr Arbeitsspeicher und Rechenzeit verschlingen. Philosophie nicht unbedingt. Das ist eher der Unterschied, ob man mathematisch bei seinen Wurzeln bleibt oder sich dem Anwender anpasst.
Das mit: meine Eingabe ist dezimal, deshalb soll es als Dezimalzahl behandelt werden, kann ich nachvollziehen, birgt aber allgemein Schwierigkeiten. Fallbeispiel: Texteingabe. Welches encoding? Oder bei der Eingabe von Zahlen, wenn ich den Körper F^11, also 0-9A haben will, aber in einem Beispielfall das A gar nicht brauche, welches Basis soll nun genommen werden? Das ist eine Komplexität, die Python in jedem Falle verhindern will. Wer Decimals möchte (auch in der Shell mit startup python scripts), der hat es immerhin schon in der Standard-Library.
Dirk Deimeke am :
Jetzt beginnen wir eine gemeinsame Sprache zu sprechen.
Performancegründe spielen natürlich eine Rolle, allerdings ist meine Meinung, dass Genauigkeit vor Performance geht, es sei denn, ich entscheide mich aktiv gegen die Genauigkeit.
Das macht es zur Philosophie.
Es scheint bei Python die andere Meinung vor zu herrschen, ich muss mich aktiv gegen Performance entscheiden, wenn ich Genauigkeit möchte.
Texteingabe: Das Environment gibt das Encoding vor.
Zahleneingabe: 0x hat sich als Vorgabe für Hex eingebürgert, andere wären möglich.
Und noch mal der Hinweis auf 2011. Warum soll ich mich dem Computer anpassen müssen und nicht umgekehrt?
posativ am :
Außerdem weiß ich nicht, ob es irgendeinen Standard gibt, der unendliche Genauigkeit bei Dezimaleingaben beschreibt, womit ich nun eher dazu tendiere, diverse Workarounds zu nutzen, als das gesamte Zahlensystem noch einmal umzukrempeln.
Und warum sollte ich python als Taschenrechner nutzen, wenn es diese Funktionalität mit `python` (ohne .pythonrc) nicht erfüllen kann? Daran würde ich jetzt nicht unbedingt die Qualität einer Programmiersprache beurteilen. Auch im Jahre 2011 nicht
Anstelle von python, wäre `bc` die bessere Wahl. (Wobei python mit importieren des Math-Modul samt Decimal-Replacement in der .pythonrc dann die bessere Wahl ist.)
---
es gibt in Python neben Dezimaleingabe (float, int) einzig nur Eingaben im Binärsystem: 0b, 0x, 0 (oktal). Daher ist eine Eingabe mit einer anderen Basis als irgendwas mit ^2 wirklich wierig. Ich möchte im Gegensatz zu dir nämlich gerne im 11-er System arbeiten (Beispiel). Wie kann ich nun meine Zahl explizit als solche markieren ohne dass sie im Konflikt mit der Dezimaleingabe steht?
Dirk Deimeke am :
Vielleicht ist aber auch Python einfach die einfache aber falsche Lösung für das Problem. (Ich habe Python genommen, weil ich es gerade gestartet hatte).
Ich habe 1987 oder 1988 im Mathestudium Pascal SC benutzt, das damals schon weiter war als alle mir bekannten Programmiersprachen, Fortran eingeschlossen. Aber ein Programm zu schreiben, es zu compilieren und laufen zu lassen, um eine Differenz zu bilden, scheint mir overkill zu sein.
onli am :
Verschwindet das Problem, wenn die Eingabe binär wäre?
Deswegen rechnet man später im Studium auch aus, wie diese Fehler sich weitertragen und lernt Algorithmen in Numerik, die den Fehler möglichst gering halten.
Außerdem: Warum tust du so, als sei das gottgegeben? Dir ist bewusst, dass es Computer gab, die das Dezimalsystem zur Darstellung von Zahlen nutzten?
Wie reele Zahlen binär dargestellt werden sollen und welcher Fehler tolerierbar ist war sicher Stoff zahlreicher Diskussionen.
Woher kommt dein Schwenk auf unendliche Zahlen? Wir geben hier keine unendlichen Zahlen ein, und erwarten auch keine unendliche zurück.
Wie Sprachen mit Mathematik umgehen ist durchaus eine Designentscheidung. Man kann einfach binär rechnen und Fehler akzeptieren. Man kann Alternativen bereitstellen. Eine Sprache kann auch automatisch versuchen, solche oder ähnliche Fehler zu fangen. Du erwähnst selbst bc. Dirk kritisiert hier, dass Python scheinbar den einfachen Weg gegangen ist bzw Performance vor Genauigkeit setzt. Das muss man nicht so sehen wie er. Aber darauf mit Fundamentalkritik "Das ist richtig" so zu antworten wirkt auf mich nicht so, als wüsstest du wirklich worum es geht.
Dirk Deimeke am :
onli am :
Dirk Deimeke am :
burli am :
Dirk Deimeke am :
Adrian am :
Viele Grüße
Adrian
Dirk Deimeke am :