Skip to content

Rechnen mit Python ...

Liebe Pythonians, was soll so etwas denn in der heutigen Zeit? (Zufallsfund)
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 :

*Das ist normal und in eigentlicher fast jeder Sprache so (es gibt auch Sprachen, die es besser machen). Hier mal das Ergebnis aus C/C++ (mit dem GNU-Compiler):

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 :

*Mein erster Computer, an dem ich einen Monitor anschliessen konnte, war ein TI-99/4A und der hat damals schon genauer gerechnet als moderne Programmiersprachen heute.

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 :

*Das ist kein Python-Problem, sondern systemisch bedingt:)
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 :

*Aus diesem Grund wies ich im Kommentar auf geschicktere Algorithmen, wie sie beispielsweise Perl einsetzt, oder auf Additionstabellen hin.

Christopher Grebs am :

*Hrm, ich hab weder den Witz, noch den Zufallsfund verstanden… Das ist das normale Verhalten vieler Programmiersprachen einfach aus dem Grund das es nicht anders geht. Entweder du schnappst dir das Decimal Modul oder rechnest selber richtig:

In [3]: (110000.0 - 103604.0) / 100.0
Out[3]: 63.96

ein einfacher Trick, der bereits in der Grundschule funktioniert hat :-)

onli am :

*Übrigens etwas, was Danisch bei seiner Kritik an Scala kritisiert. Ruby macht das wohl in manchen Fällen besser, indem auf bignum umgeschaltet würde.

Dirk Deimeke am :

*Letzten Endes hängt sehr viel von der Implementation der Arithmetik ab, die man - auf Kosten der Geschwindigkeit - durchaus auch intelligent lösen kann.

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 :

*Die meisten Sprachen sehen eine Kommazahl in der Gleichung, also rechnen sie mit IEE 754 und geben das Ergebnis auch so aus.

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 ;-)

Jochen Schnelle am :

*Hallo,

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 :

*Sehr interessant. Vielen Dank Jochen.

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 :

*Hallo,

so, einen noch:

Javascript, Firefox 6.0:

function rechnen() {
erg = 1100-1036.04;
window.alert(erg);
}

Ergebnis: 63.960000000000036

foobar am :

*So funktionierts aber wie gewünscht!!!

$ python -c "1100-1036.04"
63.96

Gruss
foobar

Marc am :

*Und hier nochmal in Ruby

$ ruby -e "puts 1100-1036.04"
63.96

losingYou am :

*Das ist ja witzig, Ruby 1.8 liefert ein richtiges Ergebnis, Ruby 1.9.1 ein falsches...

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

Jochen Schnelle am :

*Was mich jetzt ja mal wirklich interessieren würde: Rechnen Perl, Lua und Ruby wirklich genauer oder sind die nur "schlauer" beim Runden als Python, Erlang und Javascript?

Wenn ich nicht nicht völlig irre ist z.B. Lua, ebenso wie Python, in ANSI C99 implementiert.

onli am :

*Zumindest Ruby wechselt, wie oben geschrieben, bei bestimmten Situationen wohl auf eine andere Klasse und rechnet dann wirklich genauer. Welche Sprache die Grundlage ist, ist dann egal.

Dirk Deimeke am :

*Es reduziert sich auf die Frage, ob sie C zum Rechnen nutzen oder eigene Routinen verwenden.

dasdestillat am :

*Python 2.7.2 (default, Jun 29 2011, 11:10:00)
[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 :

*Was richtig und was falsch ist, gibt die Mathematik vor. Das Ergebnis 63.96 ist richtig. Punkt.

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 :

*Das ist doch riesengroßer Mist! Nahezu jede Sprache hat IEEE 754 implementiert und was ein print-Statement ausgibt, ist noch lange nicht die interne Darstellung. In Python ist das eben plain, wenn du es richtig haben willlst, dann mach print "%.2f". Oder nutze eben richtige Mathematik mit Decimals. Niemand hat jemals behauptet, dass IEEE 754 mathematisch korrekt ist.

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 :

*Womit genau hast Du ein Problem?

Mit einer andere Meinung oder damit, dass Python selbst einfache Aufgaben falsch rechnet?

posativ am :

*Damit, dass Python nicht falsch rechnet! Du rechnest mit dem Dezimalsystem, Python mit dem Binärsystem. Alles, was Python dort rechnet, ist korrekt. Es widerspricht nur unserer Vorstellung im Umgang mit unendlichen Zahlen.

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 :

*Selbstverständlich rechnet Python falsch (wie viele andere Programmiersprachen auch). Aber, es wäre möglich richtig zu rechnen.

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 :

*Nein! In der Mathematik ist das richtig. Lineare Algebra, Körper F^2. Demnach wäre ja wohl 1+1 = 0 auch falsch. Generell jede Rechenoperation außerhalb des Körpers F^10.

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 :

*Na, da bin ich anderer Meinung, wie weiter oben beschrieben. Das Universum zu ändern, um eine Aussage richtig zu machen, ist möglich ...

posativ am :

*Nicht dass ich mir das jetzt ausgedacht habe. Erstes Semester Lineare Algebra, jeder Informatik-Studiengang:

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 :

*Hey, ich habe Mathematik bis zum Vordiplom und danach Informatik studiert, dass das mathematisch möglich ist, steht völlig ausser Frage.

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 :

*Dann ist die Frage aber, wieso du einen Computer als Dezimalsystem betrachtest. Ich finde es gut, dass er alles binär macht und wäre nicht damit zufrieden, wenn er mir das Gerundete widergibt. Denn damit würde er mich anlügen.

(und wie immer, es ist möglich mit dem Decimal-Modul).

Dirk Deimeke am :

*Ich betrachte einen Computer nicht als Dezimalsystem. Per Definition ist jede Zahl, die ich eingebe, wenn ich nicht explizit etwas anderes angebe, eine Dezimalzahl. Das ist schon alles.

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 :

*(im übrigen wette ich, dass Perl einfach per default rundet ;-) )

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 :

*Ich gehe davon aus, dass Perl rundet.

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 :

*Hex/Binär kann man jetzt nicht wirklich mit Dezimal vergleichen. Denn das ist ja gerade der Punkt. Hex/Bin kann der Computer problemlos darstellen, einzig die Dezimaleingabe bereitet Probleme und sollte eigentlich demnach gänzlich aus den Programmiersprachen verschwinden (mal sehr drastisch gesprochen).

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 :

*11er-System: Genau das muss Dir die Programmiersprache anbieten, dafür habe ich keine Lösung.

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 :

*Das ist so nicht richtig. Nur weil du zwischen den Körpern wechselst, ist nicht laut Mathematik in Dezimalsystem als Ergebnis plötzlich etwas anderes richtig. Tatsächlich rechnet Python falsch, unweigerlich, weil bei der Darstellung von reelen Zahlen eben Fehler auftreten können.

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 :

*Danke Malte, Du formulierst das verständlich, was meine Meinung ist. Vielleicht sollte ich Dich zu meinem Sekretär machen ;-)

onli am :

*Wahrscheinlich würd ich den Job sogar annehmen ;-)

burli am :

*Dafür kann Python mit beliebig großen Integern berechnen ;-)

Dirk Deimeke am :

*:-) Schön. Diese einfache Differenz liesse sich auch mit zwei Integern lösen ...

Adrian am :

*Danke für deinen Tipp. Hat mir heute mal den Tag gerettet. ;-)
Viele Grüße
Adrian

Kommentar schreiben

Gravatar, Favatar, Pavatar, Identica, Twitter, MyBlogLog Autoren-Bilder werden unterstützt.
BBCode-Formatierung erlaubt
Umschließende Sterne heben ein Wort hervor (*wort*), per _wort_ kann ein Wort unterstrichen werden.
Standard-Text Smilies wie :-) und ;-) werden zu Bildern konvertiert.
Die angegebene E-Mail-Adresse wird nicht dargestellt, sondern nur für eventuelle Benachrichtigungen verwendet.
:'(  :-)  :-|  :-O  :-(  8-)  :-D  :-P  ;-) 
Formular-Optionen