To nie jest pytanie, ponieważ należy bardziej uważać. Zaktualizowałem aplikację, która używa json_encode()
PHP7.1.1 i zauważyłem problem ze zmienianiem liczb zmiennoprzecinkowych, aby czasami wydłużały 17 cyfr. Zgodnie z dokumentacją, PHP 7.1.x zaczęło używać serialize_precision
zamiast precyzji przy kodowaniu podwójnych wartości. Domyślam się, że spowodowało to przykładową wartość
472,185
zostać
472.18500000000006
po przejściu tej wartości json_encode()
. Od czasu mojego odkrycia wróciłem do PHP 7.0.16 i nie mam już problemu z json_encode()
. Próbowałem też zaktualizować do PHP 7.1.2 przed powrotem do PHP 7.0.16.
Powód tego pytania pochodzi z PHP - Floating Number Precision , jednak ostateczny powód tego wszystkiego wynika ze zmiany precyzji na użycie serialize_precision w json_encode()
.
Jeśli ktoś zna rozwiązanie tego problemu, byłbym więcej niż szczęśliwy, mogąc wysłuchać uzasadnienia / poprawki.
Wyciąg z tablicy wielowymiarowej (przed):
[staticYaxisInfo] => Array
(
[17] => stdClass Object
(
[variable_id] => 17
[static] => 1
[min] => 0
[max] => 472.185
[locked_static] => 1
)
)
a po przejściu json_encode()
...
"staticYaxisInfo":
{
"17":
{
"variable_id": "17",
"static": "1",
"min": 0,
"max": 472.18500000000006,
"locked_static": "1"
}
},
ini_set('serialize_precision', 14); ini_set('precision', 14);
prawdopodobnie spowodowałoby to serializację, tak jak kiedyś, jednak jeśli naprawdę polegasz na określonej precyzji na swoich pływakach, robisz coś źle.Odpowiedzi:
To doprowadziło mnie do szału, aż w końcu znalazłem ten błąd, który wskazuje na ten RFC, który mówi
I (podkreślenie moje)
Krótko mówiąc, istnieje nowy sposób wykorzystania w PHP 7.1
json_encode
nowego i ulepszonego silnika precyzyjnego. W php.ini musisz zmienićserialize_precision
naserialize_precision = -1
Możesz sprawdzić, czy działa z tym wierszem poleceń
php -r '$price = ["price" => round("45.99", 2)]; echo json_encode($price);'
Powinieneś wziąć
{"price":45.99}
źródło
G(precision)=-1
iPG(serialize_precision)=-1
może być również używany w PHP 5.4serialize_precision = -1
. Z -1, ten kod jestecho json_encode([528.56 * 100]);
drukowany[52855.99999999999]
json_encode
problemJako programista wtyczek nie mam ogólnego dostępu do ustawień php.ini serwera. Tak więc, w oparciu o odpowiedź Machavity, napisałem ten mały fragment kodu, którego możesz użyć w swoim skrypcie PHP. Po prostu umieść go na górze skryptu, a json_encode będzie działał jak zwykle.
if (version_compare(phpversion(), '7.1', '>=')) { ini_set( 'serialize_precision', -1 ); }
W niektórych przypadkach konieczne jest ustawienie jeszcze jednej zmiennej. Dodam to jako drugie rozwiązanie, ponieważ nie jestem pewien, czy drugie rozwiązanie działa dobrze we wszystkich przypadkach, w których pierwsze rozwiązanie sprawdziło się.
if (version_compare(phpversion(), '7.1', '>=')) { ini_set( 'precision', 17 ); ini_set( 'serialize_precision', -1 ); }
źródło
Kodowałem wartości pieniężne i miałem takie rzeczy, jak
330.46
kodowanie330.4600000000000363797880709171295166015625
. Jeśli nie chcesz lub nie możesz zmienić ustawień PHP i znasz strukturę danych z góry, istnieje bardzo proste rozwiązanie, które działało dla mnie. Po prostu rzuć to na ciąg (oba poniższe robią to samo):$data['discount'] = (string) $data['discount']; $data['discount'] = '' . $data['discount'];
W moim przypadku było to szybkie i skuteczne rozwiązanie. Zwróć uwagę, że oznacza to, że kiedy dekodujesz go z powrotem z JSON, będzie to ciąg znaków, ponieważ będzie zawinięty w podwójne cudzysłowy.
źródło
Rozwiązałem to, ustawiając zarówno precyzję, jak i serialize_precision na tę samą wartość (10):
ini_set('precision', 10); ini_set('serialize_precision', 10);
Możesz również ustawić to w swoim php.ini
źródło
Miałem ten sam problem, ale tylko serialize_precision = -1 nie rozwiązał problemu. Musiałem zrobić jeszcze jeden krok, aby zaktualizować wartość precyzji z 14 do 17 (tak jak została ustawiona w moim pliku ini PHP7.0). Najwyraźniej zmiana wartości tej liczby powoduje zmianę wartości obliczonej liczby zmiennoprzecinkowej.
źródło
Inne rozwiązania u mnie nie działały. Oto, co musiałem dodać na początku wykonywania kodu:
if (version_compare(phpversion(), '7.1', '>=')) { ini_set( 'precision', 17 ); ini_set( 'serialize_precision', -1 ); }
źródło
Jak dla mnie problem był gdy przekazano JSON_NUMERIC_CHECK jako drugi argument json_encode (), który rzutował wszystkie liczby typu na int (nie tylko integer)
źródło
Zapisz go jako ciąg z dokładną precyzją, której potrzebujesz, używając
number_format
, a następniejson_encode
użyjJSON_NUMERIC_CHECK
opcji:$foo = array('max' => number_format(472.185, 3, '.', '')); print_r(json_encode($foo, JSON_NUMERIC_CHECK));
Dostajesz:
{"max": 472.185}
Zauważ, że spowoduje to, że WSZYSTKIE ciągi liczbowe w obiekcie źródłowym zostaną zakodowane jako liczby w wynikowym formacie JSON.
źródło
$val1 = 5.5; $val2 = (1.055 - 1) * 100; $val3 = (float)(string) ((1.055 - 1) * 100); var_dump(json_encode(['val1' => $val1, 'val2' => $val2, 'val3' => $val3]));
{ "val1": 5.5, "val2": 5.499999999999994, "val3": 5.5 }
źródło
Wygląda na to, że problem występuje, gdy
serialize
iserialize_precision
są ustawione na różne wartości. W moim przypadku odpowiednio 14 i 17. Ustawienie ich obu na 14 rozwiązało problem, podobnie jak ustawienieserialize_precision
na -1.Wartość domyślna
serialize_precision
została zmieniona na -1 w PHP 7.1.0, co oznacza, że "zostanie użyty rozszerzony algorytm zaokrąglania takich liczb". Ale jeśli nadal występuje ten problem, może to być spowodowane tym, że masz plik konfiguracyjny PHP z poprzedniej wersji. (Może zachowałeś plik konfiguracyjny podczas aktualizacji?)Inną rzeczą do rozważenia jest to, czy w twoim przypadku ma sens używanie wartości zmiennoprzecinkowych. Używanie wartości ciągów zawierających liczby w celu zapewnienia, że w formacie JSON zawsze zostanie zachowana odpowiednia liczba miejsc dziesiętnych, może mieć sens lub nie.
źródło
Możesz zmienić [max] => 472.185 z float na ciąg ([max] => '472.185') przed json_encode (). Ponieważ json i tak jest łańcuchem, konwersja wartości zmiennoprzecinkowych na ciągi przed json_encode () zachowa żądaną wartość.
źródło