Ganzzahlige Teilung von Float-Objekten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Ich speichere ganze Zahlen zwischen -2**15 und 2**15 in Python-Float-Objekten,
und fuehre nur Teilungen aus, wenn diese auch mathematisch aufgehen.
Kann bei einer solchen Division ein Rungdungsfehler auftreten,
ist das compiler-abhaengig, oder ist das ganz unmoeglich?

Beispiel:

Code: Alles auswählen

a = float(169)
b = float(13)
assert 1000000*(a/b) == 13000000
Muss a/b immer genau 13 sein, oder koennte a/b==12.9999999999999 vorkommen?
Zuletzt geändert von Goswin am Donnerstag 15. Mai 2008, 10:11, insgesamt 1-mal geändert.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Ich habe ein Verständnisproblem: 2**-15 ist doch keine ganzzahliges Objekt? Und wenn mit Du float-Objekten mit Nachkommaanteil rechnest kann es immer zu Rundungsfehlern kommen. Das ist weniger vom Compiler abhängig mit dem Python auf Deiner Maschine kompiliert wurde, sondern eher von Deiner Maschine selbst (wir gehen mal davon aus, daß der Compiler "gescheit" angepasst war). siehe http://docs.python.org/tut/node16.html

Gruß,
Christian
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Vlt. ist -2**15 gemeint. Warum arbeitest du nicht mit Integern?
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Das mit dem 2**-15 anstelle von -2**15 war ein Tippfehler; tut mir leid.

Ich arbeite mit Float-Objekten, um bei ungünstiger Eingabe ein Overflow zu vermeiden. 90% oder mehr meiner Anwendungsfälle erzeugen Zahlen, die zwischen -2**15 und 2**15 liegen, und in allen diesen Fällen (mit immerhin Millionen von Rechnungen) möchte ich Rundungsfehler vermeiden. Im restlichen 10% der Fälle würde bei einer Integer-Speicherung ein Overflow entstehen, und dort wäre bei Float-Objekten ein Rundungsfehler letztlich unvermeidbar (Long-Integer-Objekte kommen bei Millionen von Rechnungen sowieso nicht in Frage, das wird echt langsam). Und wenn ich jedesmal explizit nachfrage, ob die Zahlen auch klein genug sind, dann ist der Code entschieden nicht mehr benutzerfreundlich.
BlackJack

Die Erklärung habe ich nicht so ganz verstanden!? Wenn nur ein paar von den Zahlen grösser als der Wertebereich von `int` sind, müssen doch auch nur *diese* Zahlen als `long` repräsentiert werden!? Der grosse Rest kann als `int` gespeichert werden.

Ansonsten sind `float` bei grossen Zahlen ungenau, weil die niederwertigen Stellen der originalen Zahl einfach als 0 angenommen werden, wenn nicht mehr alles in die Mantisse passt.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hast du schon mal ausprobiert, ob die Integer-Geschwindigkeit ausreicht? Am Ende auf Floats umstellen wäre eine recht einfache Aufgabe.

Bedingt durch die Rechnerarchitektur lassen sich Rundungsfehler bei Floats natürlich nicht vermeiden. Es kommt eben auf die Genauigkeit an, die du brauchst.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Wäre eine eigene Integer-Klasse evtl schneller?

Code: Alles auswählen

class MyLong:
    def __init__(self, mant, exp):
        self.mant = mant
        self.exp = exp
    def __div__(self, other):
        return MyLong(self.mant/other.mant, self.exp-other.exp)
    def __str__(self):
        return "%de%d" % (self.mant, self.exp)
    def __repr__(self):
        return str(self)
BlackJack

Etwas mit Mantisse und Exponent ist keine Ganzzahl sondern eine Fliesskommazahl. Und das selbst in Python zu implementieren ist sicher langsamer als die Implementierung in der Prozessorhardware.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

BlackJack hat geschrieben:Etwas mit Mantisse und Exponent ist keine Ganzzahl sondern eine Fliesskommazahl. Und das selbst in Python zu implementieren ist sicher langsamer als die Implementierung in der Prozessorhardware.
long/long ist aber bestimmt nicht in Prozessorhardware gegossen.

Abgesehen davon, dass meine Klasse von verbuggt ist, hätte ich keine Rundungsfehler.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Darii hat geschrieben:
BlackJack hat geschrieben:Etwas mit Mantisse und Exponent ist keine Ganzzahl sondern eine Fliesskommazahl. Und das selbst in Python zu implementieren ist sicher langsamer als die Implementierung in der Prozessorhardware.
long/long ist aber bestimmt nicht in Prozessorhardware gegossen.
Nein, aber es kann in C durch primitive Datentypen dargestellt werden, welche letztendlich nur Container im Speicher sind und dementsprechend schnell.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Es sieht fast so aus, als ob Rundungsfehler entstehen würden:

Code: Alles auswählen

print (100000000000000000000000*(float(169)/float(13))).__repr__()
gibt nur 1.2999999999999999e+24 aus.

Anderseits lässt sich das Zeugs auch nicht anständig runden:

Code: Alles auswählen

import math as m
print (100000000000000000000000*(m.floor(float(169)/float(13)+0.5))).__repr__()
gibt ebenfalls nur 1.2999999999999999e+24 aus.

Ich arbeite derzeit mit floats (funktioniert fein), muss aber darauf achten,
dass sich meine Rundungsfehler im Extremfall nicht hochschaukeln.
Wahrscheinlich ist es am besten, nach jedem größeren Schritt abzurunden. Aber das scheint auch fehlzuschlagen.

@Darii:
Ich importiere Numeric und arbeite mit einem Array von Floats. Aus der Sicht von Numeric ist die Behandlung von Longs und allgemeinen anderen Klassen anscheinend gleichermaßen langsam. Das Runden von Array-Einträgen scheint auch nicht direkt zu sein
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Das hat nichts mit dem Runden beim Rechnen zu tun, sondern damit, dass 1.3e24 als Float nicht exakt dargestellt werden kann:

Code: Alles auswählen

>>> 1.3e24
1.2999999999999999e+24
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Ich verstehe nicht, wieso Du mit floats arbeiten mußt. Und warum Numeric?

Code: Alles auswählen

In [15]: a = 169 * 100000000000000000000000

In [16]: a
Out[16]: 16900000000000000000000000L

In [17]: b = 13 * 100000000000000000000000

In [18]: a/b
Out[18]: 13L
...
In [22]: a = numpy.array([1, 100, 10000, 1000000, 100000000, 16900000000000000000000000])

In [23]: a/a
Out[23]: array([1, 1, 1, 1, 1, 1], dtype=object)

In [24]: a/13
Out[24]: array([0, 7, 769, 76923, 7692307, 1300000000000000000000000], dtype=object)
BlackJack

Ansonsten käme `numpy` auch noch mit 64-Bit-Integern zurecht. Falls das als Wertebereich ausreicht.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@Rebecca:
Sehr gute Bemerkung, danke! In der Tat erhalten wir bei

Code: Alles auswählen

print (10000000000000000000000*(float(169)/float(13))).__repr__()
die Ausgabe 1.3e+23.

Tja, bedeutet das wohl, dass bei einer float-Division vielleicht doch keine unnötigen Rundungsfehler entstehen? Schön wäre es ja.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Goswin hat geschrieben:Tja, bedeutet das wohl, dass bei einer float-Division vielleicht doch keine unnötigen Rundungsfehler entstehen? Schön wäre es ja.
Vielleicht, vielleicht nicht. Das hängt von Deinen Zahlen ab. Siehe mein erster link, ganz oben.
BlackJack

@Goswin: Wie CM schon sagte hängt das von den Zahlen ab. Mal als Beispiel:

Code: Alles auswählen

In [24]: 9999999999999999.0
Out[24]: 10000000000000000.0
Die Zahl ist noch ein ganzes Stück von der darstellbaren Obergrenze weg, aber schon nicht mehr verlustfrei darstellbar. Andererseits ist der Wert auch ein gutes Stück kleiner als der Wertebereich eines 64-Bit-Integers!
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Ich verstehe Goswins Frage so: Gilt fuer drei reelle Zahlen a, b, c mit a + b = c folgendes: float(a) + float(b) = float(c)?

(und entsprechend fuer andere Grundrechenarten)
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Kann man alle drei Zahlen exakt in eine Zahl mit Basis 2 überführen: Ja. Sonst: Nein.

edit: Nochmal nachgefragt: Goswin, was möchtest Du eigentlich machen? Gibt es vielleicht eine Lösung, die wir nicht diskutieren, weil wir gar nicht das eigentliche Problem kennen?
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Stimmt, einfaches Gegenbeispiel:

Code: Alles auswählen

>>> 0.2 + 0.1
0.30000000000000004
>>> 0.3
0.29999999999999999
Sonst braeuchte man das oberste Gebot, Floats nie auf Gleichheit zu pruefen, auch nicht (so oft).
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Antworten