ganz genaue Zahlen

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.
Antworten
bughunter
User
Beiträge: 19
Registriert: Montag 28. Juli 2008, 13:51

Hallo liebe Forumsleser,

ich muss mit Python eine "list" mit "float" Elementen in eine Datei schreiben, allerdings habe ich das Problem ich muss am besten die Zahlen unverändert rausschreiben können. Nun bin ich über die Standardformatierung der str()-Funktion auf die Nase gefallen und habe mittels

Code: Alles auswählen

str("%16.16lf"%zahl)
das ganze etwas genauer hinbekommen. Nun bereiten mir Werte Kopfschmerzen die wohl noch genauer sein müssen. Ich hatte vorher schöne exponentiale Zahlen wie 1.23423e-022 usw. die fallen dann natürlich unter den Tisch und Werte wie -0.00000000 lassen erahnen das was fehlt.

Hat jemand eine Idee wie ich die Zahlen möglichst genau in einen String wandeln kann???
(die float liste kommt eigenlich aus C raus aber ich kann dort nichts verändern, ich muss mich mit der liste und den float Werten rumschlagen :wink: )

Meine Idee war "%g" (Edit: hoppla ich meinte %e) und alles wird gut, nur weiß ich nicht ob dies nun richtig ist und den float (hat ja double precision) korrekt umwandelt. "%x" sah komisch aus, müsste Hexadezimal nicht am besten sein.

Das Problem bereitet mir echt Kopfzerbrechen.

Vielen Dank im Voraus für Eure Ideen.

Viele Grüße
bughunter

P.S.: ich bin auf Python 2.4.1 angewiesen :!:
Zuletzt geändert von bughunter am Dienstag 13. Oktober 2009, 14:39, insgesamt 2-mal geändert.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

So richtig verstanden habe ich jetzt nicht, wo GENAU dein Problem ist.
Vielleicht ist es das, was du suchst: http://docs.python.org/3.1/library/decimal.html


Edit: Jetzt hab ich verstanden, worum es geht. Der Link oben hilft nicht.
Ich glaube, das ist es, was du willst:

Code: Alles auswählen

>>> a = 3.0/7**20
>>> a
3.7597628682590546e-17
>>> print "%.50e" %a
3.75976286825905464077223469810399383600908046476619e-17
bughunter
User
Beiträge: 19
Registriert: Montag 28. Juli 2008, 13:51

Hallo numerix,

ich bekomme eine Liste die etwa so aussieht:

liste(

float 1
float 2
float 3

float 4
float 5
float 6

float 7
float 8
float 9
)

diese float-Werte möchte ich gleich in einen string umwandeln und als Zeichenkette weiterverwenden. Die Werte landen später in einer Textdatei. Decimal ist gut gemeint, ich dachte nur ich brauche die garnicht, aber falls sich hier eine Lösungsmöglichkeit bietet bin ich ganz Ohr.

Viele Grüße
bughunter
bughunter
User
Beiträge: 19
Registriert: Montag 28. Juli 2008, 13:51

Hallo numerix,

fast wie ein chat hier :wink: Danke!
Ich weiß nicht, wie genau der float im Python ist, double liege bei 15-16 Stellen Genauigkeit (sei 64 bit breit), laufe ich hier Gefahr durch die Formatierung Datenmüll anzufügen?!

Etwa sowas bekomme ich mit "%16.16lf" heraus, ist aber wie gesagt ungünstig:

Code: Alles auswählen

 0.9977260491478370 0.0334070537177052 0.0585380185329267
-0.0257925263667679 0.9916544868481860 -0.1263175534020790
-0.0622693860213720 0.1245204701074090 0.9902611655966129 
1008.8248444808501000 -729.8262172489220300 428.0336341556750300
Schon interessant was bei mir mit Python 2.5.2 herauskommt:

Code: Alles auswählen

Python 2.5.2 (r252:60911, Feb 21 2008, 13:11:45) [MSC v.1310 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.

    ****************************************************************
    Personal firewall software may warn about the connection IDLE
    makes to its subprocess using this computers internal loopback
    interface.  This connection is not visible on any external
    interface and no data is sent to or received from the Internet.
    ****************************************************************
    
IDLE 1.2.2      
>>> a=3.0/7**20
>>> a
3.7597628682590546e-017
>>> str(a)
'3.75976286826e-017'
>>> str("%.50e"%a)
'3.75976286825905460000000000000000000000000000000000e-017'
>>> print "%.50e"%a
3.75976286825905460000000000000000000000000000000000e-017
>>> 

Viele Grüße
bughunter
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Was erwartest du denn? Alle ausgegebenen Zahlen sind doch auf mindestens 15 Stellen angegeben. Wie du ja selber schon festgestellt hast, werden auch nicht mehr garantiert. Allle verbleibenden Stellen können dann einfach mit Nullen aufgefüllt werden. Vielleicht erzählst du einfach mal, was du bei Zahlen wie 1008.8248444808501000 oder 0.9916544868481860 erwartest.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Vielleicht liegts an Windows. Meine Linux-Versionen (getestet mit 2.5.4 und 2.6.2) geben folgendes aus:

Code: Alles auswählen

>>> print "%.50e" % (3.0/7**20)
3.75976286825905464077223469810399383600908046476619e-17
Bottle: Micro Web Framework + Development Blog
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ja, aber dann sollte man es mit der Genauigkeit nicht mehr so ernst nehmen:

Code: Alles auswählen

>>> print "%.50e" % (1.0/3.0)
3.33333333333333314829616256247390992939472198486328e-01
Das Leben ist wie ein Tennisball.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Defnull hat geschrieben:Vielleicht liegts an Windows.
Es scheint auch etwas mit der (C)Python-Implementation zu tun zu haben.
Bei mir liefern alle (auf meinem Rechner vorhandenen) CPython-Versionen (2.5, 2.6, 3.0, 3.1) die Darstellung

Code: Alles auswählen

>>> print("%.50e" %(3.0/7**20))
3.75976286825905464077223469810399383600908046476619e-17
Jython hingegen (2.2 und 2.5) liefert

Code: Alles auswählen

>>> print("%.50e" %(3.0/7**20))
3.75976286825905500000000000000000000000000000000000e-17
Exakt wäre im übrigen

Code: Alles auswählen

>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 50
>>> print Decimal(3)/Decimal(7**20)
3.7597628682590546995108942577230359476147421084074E-17
bughunter
User
Beiträge: 19
Registriert: Montag 28. Juli 2008, 13:51

Ich benötige möglichst genaue Werte, da ich Transformationsmatrizen verarbeite. Ist eine Zahl ungenau wirkt sich das auf die Lage eines Volumenkörpers im Raum aus. Was mich wunderte, ist, dass ich teilweise e-Zahlen hatte die im Exponent hohe Genauigkeiten stehen haben - so hier:

Code: Alles auswählen

#per str(floatzahl) erzeugt:
-5.29395592034e-023 -3.44611707218e-024 1.0 -1.77809157735e-016 7.94093388051e-023 
Edit: auf Linux sind es auch mehr Stellen :lol: nur muss ich WinDAUs benutzen

Viele Grüße
bughunter
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das liegt an der internen Darstellung der Zahlen. Die Genauigkeit gilt für die Mantisse, der Exponent wird separat gespeichert. Vielleicht solltest du dir dazu den Wikipedia-Artikel durchlesen.
bughunter hat geschrieben:Edit: auf Linux sind es auch mehr Stellen Laughing nur muss ich WinDAUs benutzen
Diese haben allerdings keine Aussagekraft.
Das Leben ist wie ein Tennisball.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

EyDu hat geschrieben:Diese haben allerdings keine Aussagekraft.
Genau.
@bughunter: Sieh dir mein Beispiel oben doch nochmal an.

Die Frage wäre auch, wie genau die float-Werte sind, die du bekommst.
Wenn du die Berechnung in Python durchführen könntest (und es auf die Rechengeschwindigkeit nicht ankommt), dann wäre das Decimal-Modul doch das richtige. Da kannst du eine beliebige (tatsächliche!) Genauigkeit festlegen.
bughunter
User
Beiträge: 19
Registriert: Montag 28. Juli 2008, 13:51

@numerix:

Ich frage die Werte über eine C/C++ Erweiterung im Python ab, es müssen wohl double Werte im C sein die etwa mittels:

Code: Alles auswählen

PyObject* PyFloat_FromDouble(double v)
in eine "list" geschossen werden. So kann ich nur die floats nehmen und will diese möglichst unverändert rausschreiben.

:twisted: und wenn auch BINÄR :wink: Zahlen wären aber hübscher

Nochmals Dank für Eure regen Beiträge!

Viele Grüße
bughunter
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Warum verwendest du überhaupt eine verlustbehaftete Zahlenrepresentation, wenn die so genau sein müssen?
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Python floats sind doubles. Aber wenn die Präzision derart wichtig ist sind wie schon gesagt auch doubles nichts für dich.
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
bughunter
User
Beiträge: 19
Registriert: Montag 28. Juli 2008, 13:51

Genau, das sind im Prinzip doubles, heißen nur float (mit double precision).
Wie gesagt ich kann nur das nehmen was ich bekomme, ich weiß das es im C auch nicht genauer zugeht, allerdings birgt eine Typenkonvertierung immer die Gefahr des Datenverlusts und das wollte ich ausschließen.

Numerix und EyDu haben mir gute hinweise gegeben, die "%.50e" ist eine gute Lösung. Das Mantisse und Exponent getrennt sind erklärt dann auch meine ergebnisse mit "%16.16lf". Ist eine sehr genaue Zahl z.B.: 1.23432e-024 dabei, dann habe ich Datenverlust.

Ich werde vorsichtshalber wohl erstmal mit str() nachsehen ob es eine e-Zahl ist so spare ich mir die Größenprüfung und im Falle keiner e-Zahl
benutze ich "%16.16lf" das sollte genau genug sein, oder was haltet Ihr davon?

@defnull: Ich hätte auch lieber Strings bekommen :wink: anstatt Zahlen anfassen zu müssen die ich ohnehin als Text rausschreibe.

Viele Grüße
bughunter
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Es gibt noch einen anderen Weg:
Das math-Modul bietet die Möglichkeit, Mantisse und Exponent eines float Wertes zu ermitteln und auch umgekehrt aus beiden wieder den float zusammenzusetzen. Damit könnte es gelingen, die erhaltenen float (= double) Werte ohne Verlust zu speichern und auch wieder einzulesen (man sollte noch ein paar mehr Beispiele testen, um sicher zu gehen).

Code: Alles auswählen

>>> from math import frexp, ldexp
>>> a = 3.0/7**20
>>> a
3.7597628682590546e-17
>>> m,e = frexp(a)
>>> m
0.67729866609971623
>>> e
-54
>>> s = "%.17f" %m
>>> s
'0.67729866609971623'
>>> n = float(s)
>>> n
0.67729866609971623
>>> b = ldexp(n,e)
>>> a==b
True
bughunter
User
Beiträge: 19
Registriert: Montag 28. Juli 2008, 13:51

:idea: AH :idea: , das ist noch eine interessante Option, Danke!
Aber warum eigentlich "%.17f" und nicht "%.16" woher weiß man das?
Im Interaktiven Modus kümmert sich glaub ich "repr()" darum das Objekt anzuzeigen, ich müsste quasi die Stellenanzahl noch ermitteln oder?


Viele Grüße
bughunter
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

Das hängt damit zu zusammen, das 54 Nachkommastellen im Binärsystem etwa 17 Nachkommastellen im Dezimalsystem entsprechen. Zum Verständnis kannst du dir die Norm IEEE 754 anschauen.

Grüße
Gerrit
Antworten