2015-04-23 6 views
5

Я читаю из файла JSON с помощью jsoncpp. Когда я пишу обратно в файл, мои значения с плавающей точкой немного отключены. Для тестирования я решил разобрать файл на Json :: Value, а затем записать это значение обратно в файл. Я ожидаю, что он будет выглядеть одинаково, но вместо этого значения float разные.Jsoncpp неправильно записывает значения с плавающей запятой

Пример:

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

пишет:

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

Вы можете заметить, что 0,25 не изменится, даже если все остальные поплавки делали. Любая идея, что здесь происходит?

+0

Некоторые значения с плавающей запятой могут быть точно представлены в двоичном формате, а некоторые не могут. То, что вы видите, является ближайшим представлением ваших ценностей. –

+0

Номера с плавающей точкой не точны. Они являются лучшим представлением в ограниченной памяти. PS 0.25 - четверть - сумма, чтобы работать с бинарными ;-) –

+0

Спасибо за разъяснение. Есть ли вообще избежать этого? – SFBA26

ответ

2

Это фактически проблема внедрения синтаксического анализа чисел с плавающей запятой. Хотя числа с плавающей запятой могут точно представлять только некоторые десятичные числа (0,25 - одно из ~ 2^64), необходимо проанализировать строковое представление ближайшему двоичному представлению. При печати с плавающей запятой также необходимо распечатать (желательно кратчайшее) строковое представление, которое можно восстановить в двоичном представлении.

Я признаю, что я не исследовал JsonCPP, чтобы узнать, есть ли решение для этого. Но, как я автор RapidJSON, я пытался понять, как RapidJSON выполняет для этого:

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

И результат:

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

RapidJSON реализован как при разборе и печати алгоритмов внутри. Обычный синтаксический анализ точности будет иметь максимум 3 ошибки ULP, но с полным флагом разбора точности (kParseFullPrecisionFlag) он всегда может разобрать до ближайшего представления. В части печати реализован алгоритм Grisu2. Он всегда генерирует точный результат и более 99% времени будет самым коротким (оптимальным).

Собственно, используя strtod() и sprintf(..., "%.17g", ...) может решить эту проблему. Но в текущей стандартной библиотеке C/C++ они намного медленнее. Например, я сделал benchmark для печати double. Таким образом, в RapidJSON мы реализовали собственные оптимизированные решения только для заголовков.

+3

Благодарим за отзыв. К сожалению, я не писал оригинальный код, и сейчас уже слишком поздно переключаться на RapidJSON. Я попытался использовать to_string для преобразования float перед записью в JSON, но у меня возникла ошибка при чтении сохраненных значений с использованием .AsFloat(). Поэтому я считаю, что мой единственный вариант - преобразовать в строку при записи и чтении в виде строки, а затем преобразовать в float. – SFBA26

Смежные вопросы