2015-04-23 13 views
5

Ich lese aus einer JSON-Datei mit Jsoncpp. Wenn ich in die Datei zurückschreibe, sind meine Gleitkommawerte etwas aus. Aus Gründen des Testens entschied ich, die Datei zu einem Json :: Value zu analysieren und diesen Wert dann wieder in die Datei zu schreiben. Ich würde erwarten, dass es gleich aussieht, aber stattdessen sind die Float-Werte unterschiedlich.Jsoncpp Float Werte falsch schreiben

Beispiel:

"Parameters": 
{ 
"MinXValue": 0.1, 
"MaxXValue": 0.15, 
"MinYValue": 0.25, 
"MaxYValue": 1.1, 
"MinObjectSizeValue": 1 
} 

schreibt:

"Parameters": 
{ 
"MinXValue": 0.10000000000000001, 
"MaxXValue": 0.14999999999999999, 
"MinYValue": 0.25, 
"MaxYValue": 1.1000000238418579, 
"MinObjectSizeValue": 1 
} 

Sie fest, dass 0,25 nicht ändern, auch wenn alle anderen Schwimmer tat. Irgendeine Idee, was hier vor sich geht?

+0

Einige Gleitkommawerte können im Binärformat genau dargestellt werden, andere nicht. Was Sie sehen, ist die engste Darstellung Ihrer Werte. –

+0

Fließkommazahlen sind nicht präzise. Sie sind die beste Darstellung in der begrenzten Erinnerung. PS 0.25 ist ein Viertel - Summat, das mit binärem Arbeiten zu tun hat ;-) –

+0

Danke für die Klarstellung. Gibt es das überhaupt, um das zu vermeiden? – SFBA26

Antwort

2

Es ist tatsächlich ein Problem der Gleitkommazahl Parsing/Druck-Implementierung. Obwohl Gleitkommazahlen nur einige Dezimalzahlen genau darstellen können (0,25 ist eine von ~ 2^64), ist es notwendig, eine Zeichenfolgendarstellung zur nächsten Binärdarstellung zu analysieren. Beim Drucken von Gleitkomma ist es auch notwendig, die (vorzugsweise kürzeste) Zeichenfolgendarstellung zu drucken, die in die Binärdarstellung wiederhergestellt werden kann.

Ich gebe zu, dass ich JsonCPP nicht untersucht habe, um zu sehen, ob es dafür eine Lösung gibt. Aber als ich den Autor RapidJSON bin, habe ich versucht, zu sehen, wie RapidJSON für diese ausführt:

const char json[] = 
    "{" 
    "\"MinXValue\": 0.1," 
    "\"MaxXValue\": 0.15," 
    "\"MinYValue\": 0.25," 
    "\"MaxYValue\": 1.1," 
    "\"MinObjectSizeValue\": 1" 
    "}"; 

using namespace rapidjson; 

Document d; 
d.Parse(json); 

StringBuffer sb; 
PrettyWriter<StringBuffer> writer(sb); 
d.Accept(writer); 

std::cout << sb.GetString(); 

Und das Ergebnis:

{ 
    "MinXValue": 0.1, 
    "MaxXValue": 0.15, 
    "MinYValue": 0.25, 
    "MaxYValue": 1.1, 
    "MinObjectSizeValue": 1 
} 

RapidJSON beide Parsing und Druck Algorithmen intern umgesetzt. Normale Präzisionsanalyse wird maximal 3 ULP-Fehler haben, aber mit dem Flag für die vollständige Analyse (kParseFullPrecisionFlag) kann es immer zur nächsten Repräsentation analysieren. Der Druckteil implementiert den Grisu2-Algorithmus. Es erzeugt immer ein genaues Ergebnis und mehr als 99% der Zeit, um am kürzesten (optimal) zu sein.

Eigentlich kann strtod() und sprintf(..., "%.17g", ...) dieses Problem auch lösen. In der aktuellen C/C++ - Standardbibliothek sind sie jedoch wesentlich langsamer. Zum Beispiel habe ich eine benchmark zum Drucken double getan. Deshalb haben wir in RapidJSON eigene optimierte Header-Only-Lösungen implementiert.

+3

Vielen Dank für Ihre Antwort. Leider habe ich den Originalcode nicht geschrieben und es ist zu spät, um jetzt zu RapidJSON zu wechseln. Ich habe versucht, to_string zu verwenden, um die Floats vor dem Schreiben in JSON zu konvertieren, aber beim Lesen der gespeicherten Werte mit .AsFloat() ist ein Fehler aufgetreten. Also denke ich, meine einzige Option ist, beim Schreiben und Lesen als String in eine Zeichenkette zu konvertieren und dann in float zu konvertieren. – SFBA26