Python json.loads kürzt werte

Revolution

Commodore
Registriert
März 2008
Beiträge
4.964
Hallo zusammen,

Mir erschließt sich gerade nicht so richtig warum Python mir mit json.loads(jasondata) den wert 0.001000000000000000020816681711721685132943093776702880859375 auf 0.001 kürzt? Kann man das irgendwie verhindern?

Code:
if args.output == 'json':
                    #pd.set_option('precision', 10)
                    jasondata=(json.dumps(ydata, indent=4, sort_keys=True))
                    print(jasondata)
                    pythonObj = json.loads(jasondata)
                    print(pythonObj)
                    name = (pythonObj['records'][1]['value'])
                    print(name)
                    #print(jasondata['records'][1]['value'])
                    #print(jasondata)

Code:
root@testpi:/home/****/pyMeterBus/tools# python3 mbus-serial-request-data-multi-reply.py -a 0 /dev/serial0
{
    "access_no": 32,
    "identification": "23670225",
    "manufacturer": "INV",
    "medium": 7,
    "records": [
        {
            "unit": "none",
            "value": 23670225
        },
        {
            "unit": "m^3",
            "value": 0.001000000000000000020816681711721685132943093776702880859375
        }
    ]
}
{'access_no': 32, 'identification': '23670225', 'manufacturer': 'INV', 'medium': 7, 'records': [{'unit': 'none', 'value': 23670225}, {'unit': 'm^3', 'value': 0.001}]}
0.001
 
https://docs.python.org/3/tutorial/floatingpoint.html
Grob zusammengefasst: Floats haben meist keine exakte binäre Representation sondern können mit endlich vielen Bits nur approximiert werden. Vergleichbar ist das mit 1/3 das mit endlich vielen Stellen auch dezimal nicht exakt repräsentiert werden kann. Damit einem Python nicht ständig ewig viele Nachkommastellen ausspuckt, wenn man zB mit 0.1 arbeitet, werden die Ausgaben gerundet. Gerechnet wird dennoch so exakt wie möglich.

Edit: Ich weiß nicht wo der Wert im JSON herkommt, aber vermutlich sollte es eigentlich 0.001 sein und die anderen Stellen kommen genau daher, dass man 0.001 nicht exakt speichern kann. Ebenso weiß ich nicht, was genau du da misst (auf jeden Fall m^3) aber die erste Stelle nach 0.001 ist bei 10e-20. Ich unterstelle mal, dass du es nicht so genau brauchst :)
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: BeBur und nkler
Wenn du die genaue Zahl willst musst du den Wert als String einlesen und verarbeiten, z.B. mit einer Bibliothek für Rechnungen mit beliebiger Genauigkeit wie mpmath.

Die Zahl sieht aber danach aus, als wenn sie selber schon das Ergebnis eines Rundungsfehlers ist, nämlich 0,001 als 'double' gespeichert (also IEEE 754 Format mit doppelter Genauigkeit).
Denn Zahlen wie 0,1, 0,01, 0,001, etc. haben keine geschlossene binäre Darstellung werden daher stets nur näherungsweise gespeichert.

Laut random Online Umrechner: Wert von 0,001 gespeichert als IEE754 double precision entspricht 0,00100000000000000002081668171172 also so ziemlich genau das was du da vorliegen hast. Vermutlich wurde noch ein etwas anderes Format mit noch mehr "Genauigkeit" aka Nachkommastellen verwendet.

Sprich, der originale Wert war 0,001, alles andere sind Fließkommazahl-Rundungsfehler.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Dalek und blöderidiot
Warum Kubikmeter für solche Maße? Wären Parsec vielleicht auch eine Idee?

Protip, wenn die absoluten Werte zu klein sind, dann nimmt man die kleinere Einheit und nicht den kleineren Wert. Und wenn es mm^3 wären, dann hat man aber signifikant genauere Ergebnisse.
 
Ich benutze häufig den decimal Typ, wenn ich solche Rundungsfehler vermeiden will.
 
Zurück
Oben