PHP7.1 кодирование json() Плавающий выпуск


Это не вопрос, так как это скорее вопрос осознания. Я обновил приложение, которое использует json_encode() до PHP7.1.1, и я увидел проблему с плавающими числами, которые иногда расширяются до 17 цифр. Согласно документации, PHP 7.1.x начал использовать serialize_precision вместо точности при кодировании двойных значений. Я предполагаю, что это вызвало примерное значение

472.185

Стать

472.18500000000006

После этого значения прошел через json_encode(). С момента моего открытия я вернулся к PHP 7.0.16, и у меня больше нет проблемы с json_encode(). Я также попытался обновиться до PHP 7.1.2, прежде чем вернуться к PHP 7.0.16.

Обоснование этого вопроса проистекает из PHP - точности чисел с плавающей запятой, однако в конечном итоге все это связано с переходом от точности к использованию serialize_precision в json_encode().

Если кто-нибудь знает решение этой проблемы, я был бы более чем счастлив чтобы послушать рассуждения/исправить.

Выдержка из многомерного массива (ранее):

[staticYaxisInfo] => Array
                    (
                        [17] => stdClass Object
                            (
                                [variable_id] => 17
                                [static] => 1
                                [min] => 0
                                [max] => 472.185
                                [locked_static] => 1
                            )

                    )

И после прохождения json_encode()...

"staticYaxisInfo":
            {
                "17":
                {
                    "variable_id": "17",
                    "static": "1",
                    "min": 0,
                    "max": 472.18500000000006,
                    "locked_static": "1"
                }
            },
Author: Machavity, 2017-03-23

5 answers

Это немного сводило меня с ума, пока я, наконец, не нашел эту ошибку, которая указывает вам на этот RFC, в котором говорится

В настоящее время json_encode() использует EG(точность), значение которого равно 14. Это означает, что для отображения (печати) номера используется не более 14 цифр. Двойной стандарт IEEE 754 поддерживает более высокую точность и serialize()/var_export() использует PG(serialize_precision), значение которого по умолчанию равно 17, чтобы быть более точным. Поскольку json_encode() использует EG(точность), json_encode() удаляет нижние цифры дробь разделяет и уничтожает исходное значение, даже если значение с плавающей точкой PHP может содержать более точное значение с плавающей точкой.

И (курсив мой)

В этом RFC предлагается ввести новую настройку, например (точность)=-1 и PG(serialize_precision)=-1, которая использует режим zend_dtoa() 0, в котором используется лучший алгоритм округления чисел с плавающей запятой (-1 используется для обозначения режима 0).

Короче говоря, есть новый способ заставить PHP 7.1 json_encode использовать новую и улучшенную точность двигатель. В php.ini вам нужно изменить serialize_precision на

serialize_precision = -1

Вы можете убедиться, что он работает с этой командной строкой

php -r '$price = ["price" => round("45.99", 2)]; echo json_encode($price);'

Вы должны получить

{"price":45.99}
 45
Author: Machavity, 2017-03-27 20:51:57

Как разработчик плагинов, я не имею общего доступа к настройкам php.ini сервера. Итак, основываясь на ответе Machavity, я написал этот небольшой фрагмент кода, который вы можете использовать в своем PHP-скрипте. Просто поместите его поверх скрипта, и json_encode продолжит работать как обычно.

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'serialize_precision', -1 );
}
 7
Author: alev, 2017-09-24 13:06:37

У меня была та же проблема, но только serialize_precision = -1 не решило проблему. Мне пришлось сделать еще один шаг, чтобы обновить значение точности с 14 до 17 (как оно было установлено в моем файле PHP7.0 ini). По-видимому, изменение значения этого числа изменяет значение вычисляемого значения с плавающей точкой.

 3
Author: Alin Pop, 2017-12-22 08:49:23

Другие решения для меня не сработали. Вот что мне пришлось добавить в начале выполнения кода:

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'precision', 17 );
    ini_set( 'serialize_precision', -1 );
}
 3
Author: mikepsinn, 2018-01-23 21:51:07

Я кодировал денежные ценности и имел такие вещи, как 330.46 кодирование в 330.4600000000000363797880709171295166015625. Если вы не хотите или не можете изменять настройки PHP и заранее знаете структуру данных, есть очень простое решение, которое сработало для меня. Просто приведите его к строке (оба следующих действия делают одно и то же):

$data['discount'] = (string) $data['discount'];
$data['discount'] = '' . $data['discount'];

Для моего варианта использования это было быстрое и эффективное решение. Просто обратите внимание, что это означает, что когда вы декодируете его обратно из JSON, это будет строка, так как она будет завернута в двойные кавычки.

 0
Author: texelate, 2018-06-20 09:08:23