web-dev-qa-db-fra.com

PHP7.1 json_encode () Float Issue

Ce n'est pas une question car c'est plus une conscience. J'ai mis à jour une application qui utilise json_encode() en PHP 7.1.1 et je constatais un problème de modification des flotteurs, qui pouvait parfois être étendu à 17 chiffres. Selon la documentation, PHP 7.1.x a commencé à utiliser serialize_precision au lieu de précision lors du codage de valeurs doubles. Je suppose que cela a causé une valeur d'exemple de 

472.185 

devenir 

472.18500000000006 

après que cette valeur soit passée par json_encode(). Depuis ma découverte, je suis revenu à PHP 7.0.16 et je n'ai plus le problème avec json_encode(). J'ai également essayé de mettre à jour vers PHP 7.1.2 avant de revenir à PHP 7.0.16.

Le raisonnement sous-jacent à cette question découle de PHP - Floating Number Precision , cependant, la raison en est le passage de la précision à la sérialisation_precision dans json_encode().

Si quelqu'un connaît une solution à ce problème, je serais ravi d'écouter le raisonnement/la solution.

Extrait de tableau multidimensionnel (avant): 

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

                    )

et après avoir traversé json_encode()...

"staticYaxisInfo":
            {
                "17":
                {
                    "variable_id": "17",
                    "static": "1",
                    "min": 0,
                    "max": 472.18500000000006,
                    "locked_static": "1"
                }
            },
47
Gwi7d31

Cela m’a un peu rendu fou jusqu’à ce que j’ai enfin trouvé ce bug qui vous indique ce RFC qui dit

Actuellement, json_encode() utilise EG (précision), qui est réglé sur 14. Cela signifie que 14 chiffres au maximum sont utilisés pour afficher (imprimer) le numéro. IEEE 754 double prend en charge une précision plus élevée et serialize()var_export() utilise PG (serialize_precision) qui est défini sur 17 par défaut pour être plus précis. Étant donné que json_encode() utilise EG (précision), json_encode() supprime les chiffres inférieurs des fractions et détruit la valeur d'origine même si la valeur float de PHP peut contenir une valeur float plus précise. 

Et (c'est moi qui souligne)

Cette RFC propose d’introduire un nouveau paramètre EG (précision) = - 1 et _/PG (serialize_precision) = - 1 qui utilise le mode 0 de zend_dtoa () qui utilise un meilleur algorithme pour arrondir les nombres à virgule flottante (-1 sert à indiquer 0 mode)

En bref, il existe un nouveau moyen de faire en sorte que PHP 7.1 json_encode utilise le nouveau moteur de précision amélioré. Dans php.ini, vous devez remplacer serialize_precision par

serialize_precision = -1

Vous pouvez vérifier que cela fonctionne avec cette ligne de commande

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

Tu devrais obtenir 

{"price":45.99}
61
Machavity

En tant que développeur de plug-in, je n'ai pas d'accès général aux paramètres php.ini d'un serveur. Donc, basé sur la réponse de Machavity, j'ai écrit ce petit morceau de code que vous pouvez utiliser dans votre script PHP. Il suffit de le placer en haut du script et json_encode continuera à fonctionner normalement.

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'serialize_precision', -1 );
}
15
alev

Les autres solutions ne fonctionnaient pas pour moi. Voici ce que je devais ajouter au début de l'exécution de mon code:

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'precision', 17 );
    ini_set( 'serialize_precision', -1 );
}
3
Mike P. Sinn

J'ai eu le même problème mais seul serialize_precision = -1 n'a pas résolu le problème. Je devais faire encore une étape, pour mettre à jour la valeur de précision de 14 à 17 (comme elle était définie sur mon fichier in7 PHP7.0). Apparemment, changer la valeur de ce nombre change la valeur du float calculé.

3
Alin Pop

J'ai résolu ce problème en définissant à la fois la précision et serialize_precision (10):

ini_set('precision', 10);
ini_set('serialize_precision', 10);

Vous pouvez également définir cela dans votre php.ini

0
whatever_sa

Quant à moi, le problème était quand JSON_NUMERIC_CHECK en tant que second argument de json_encode () était passé, ce qui transtypait tout type de nombres en int (pas seulement un entier).

0
Acuna

J'encodais des valeurs monétaires et j'avais des choses comme 330.46 encoding to 330.4600000000000363797880709171295166015625. Si vous ne souhaitez pas ou ne pouvez pas modifier les paramètres PHP et si vous connaissez la structure des données à l'avance, il existe une solution très simple qui a fonctionné pour moi. Il suffit de le lancer dans une chaîne (les deux suivants font la même chose):

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

Pour mon cas d'utilisation, c'était une solution rapide et efficace. Notez simplement que cela signifie que lorsque vous le décodez à partir de JSON, ce sera une chaîne car il sera entouré de guillemets.

0
texelate

Vous pouvez changer le [max] => 472.185 d'un float en une chaîne ([max] => '472.185') avant json_encode () Comme json est quand même une chaîne, convertir vos valeurs float en chaînes avant json_encode () conservera la valeur souhaitée.

0
Everett Staley