编码和解码浮动在PHP与JSON没有失去精度 [英] Encoding and decoding floats in json with PHP without losing precision

查看:204
本文介绍了编码和解码浮动在PHP与JSON没有失去精度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将一个json字符串解码为PHP对象,然后将这个对象再次转换为json字符串,而不会损失json中的浮点数的精度。

如果运行在输出下面的示例是:

$ $ p $ code $ J
string $ {
integer1:10000,
integer2:100000999499498485845848584584584,
float1:1.121212,
float2:8.226347662837406e + 09
}'(length = 130)
JSON WITHOUT SPACES [1]:
string'{integer1:10000,integer2:100000999499498485845848584584584,float1:1.121212,float2:8.226347662837406e + 09}'(length = 112)
OBJECT:
object(MyObject)[1]
public'integer1'=> int 10000
public'integer2'=> float 1.000009994995E + 32
public'float1'=> float 1.121212
public'float2'=> float 8226347662.8374
JSON FROM OBJECT [2]:
string'{integer1:10000,integer2:1.000009994995e + 32,float1:1.121212,float2:8226347662.8374}' = 91)

我希望字符串[1]和[2]等于或至少不等于松散的精度。
我知道十进制数不能完全代表PHP浮点数,我知道你可以使用 json_decode($ json,true,512,JSON_BIGINT_AS_STRING)在PHP中将数字表示为字符串。但是使用这个选项,我可以从对象重新生成原始的json。

 <?php 

$ json =<< EOT
{
integer1:10000,
integer2:100000999499498485845848584584584,
float1:1.121212,
float2:8.226347662837406e + 09
}
EOT;

// json_last_error_msg(PHP 5> = 5.5.0)
if(!function_exists('json_last_error_msg')){
function json_last_error_msg(){
static $ errors = array(
JSON_ERROR_NONE => null,
JSON_ERROR_DEPTH =>'超过最大堆栈深度',
JSON_ERROR_STATE_MISMATCH =>'下溢或模式不匹配',
JSON_ERROR_CTRL_CHAR =>'找到意外的控制字符',
JSON_ERROR_SYNTAX =>'语法错误,格式错误的JSON',
JSON_ERROR_UTF8 =>'格式错误的UTF-8字符,可能错误编码'
);
$ error = json_last_error();
返回array_key_exists($ error,$ errors)? $ errors [$ error]:未知错误({$ error});


$ b $ class MyObject
{
/ ** @var int | float * /
public $ integer1;
/ ** @var int | float * /
public $ integer2;
/ ** @var int | float * /
public $ float1;
/ ** @var int | float * /
public $ float2;

public function __construct($ json)
{
$ this-> fromJson($ json);


公共函数fromJson($ json)
{
$ result = json_decode($ json); $ j $ _
if(json_last_error()!= JSON_ERROR_NONE){
die('json_decode error:'.json_last_error_msg());
};

$ this-> integer1 = $ result-> integer1;
$ this-> integer2 = $ result-> integer2;
$ this-> float1 = $ result-> float1;
$ this-> float2 = $ result-> float2;


public function toJson()
{
$ json = json_encode($ this); $ j $ _
if(json_last_error()!= JSON_ERROR_NONE){
die('json_decode error:'.json_last_error_msg());
};

return $ json;
}
}

echo'JSON:';
var_dump($ json);

echo'JSON WITHOUT SPACES [1]:';
var_dump(preg_replace('/ \ s + /','',$ json));

$ object = new MyObject($ json);

echo'OBJECT:';
var_dump($ object);

echo'JSON FROM OBJECT [2]:';
var_dump($ object-> toJson());

我想我可以添加额外的元数据到对象,并指出如果字符串属性它是一个数字原来的json字符串,但然后我将不得不解析由我自己的json,而不是使用json_decode函数。

我已经阅读了很多关于这个问题的帖子,但所有他们只说如何将JSON转换为对象(使用字符串),而不是如何将该对象再次转换为原始JSON作为json中的数字。



如果你想玩样品: http://sandbox.onlinephpfunctions.com/code/8d934874c93c4574d886dbbf645a6763ba1ba131


解决方案

什么创建了你的json字符串?

虽然理论上正确,但正常的PHP程序员并不需要一定程度的疯狂。这些值在32位(甚至是64位)PHP内存中不能表示为浮点或整数。顺便说一下,您发布的在线链接使用32位PHP。当PHP对json进行解码时,当在内存中表示值时,它会尽可能地精确。你有没有注意到你的integer2溢出被解码为浮点数,所以失去了精度,但没有溢出?由于它永远无法在内存中表示这些非常准确的值,所以当您再次对对象进行编码时,您会得到一个逻辑上相似但是不同的json字符串。

您还应该问问自己,为什么你需要json字符串与你原来的表示相同?如果你真的需要这样的准确性,我会考虑使用JAVA,你可以使用更高精度的数据类型(比如BigInteger)。对于像这样的高精度的东西,PHP并不是最好的工具。 (PHP 5反正)。

建议阅读:

$ ul

  • 浮点数

  • PHP整数

  • PHP Floats


  • I want to decode a json string to PHP object and then the object back again to json string without losing precision for floats numbers in json.

    If you run the sample below the output would be:

    JSON:
    string '{
     "integer1": 10000,
     "integer2": 100000999499498485845848584584584,
     "float1": 1.121212,
     "float2": 8.226347662837406e+09
    }' (length=130)
    JSON WITHOUT SPACES [1]:
    string '{"integer1":10000,"integer2":100000999499498485845848584584584,"float1":1.121212,"float2":8.226347662837406e+09}' (length=112)
    OBJECT:
    object(MyObject)[1]
      public 'integer1' => int 10000
      public 'integer2' => float 1.000009994995E+32
      public 'float1' => float 1.121212
      public 'float2' => float 8226347662.8374
    JSON FROM OBJECT [2]:
    string '{"integer1":10000,"integer2":1.000009994995e+32,"float1":1.121212,"float2":8226347662.8374}' (length=91)
    

    I want string [1] and [2] to be equal or at least not to loose precision. I know that decimal numbers can´t be represented exactly in PHP floats and I know that you can use the option json_decode($json, true, 512, JSON_BIGINT_AS_STRING) to represent number as string in PHP. But using that option I can re-generate the original json from the object.

    <?php
    
    $json = <<<EOT
    {
     "integer1": 10000,
     "integer2": 100000999499498485845848584584584,
     "float1": 1.121212,
     "float2": 8.226347662837406e+09
    }
    EOT;
    
    // json_last_error_msg (PHP 5 >= 5.5.0)
    if (!function_exists('json_last_error_msg')) {
        function json_last_error_msg() {
            static $errors = array(
                JSON_ERROR_NONE             => null,
                JSON_ERROR_DEPTH            => 'Maximum stack depth exceeded',
                JSON_ERROR_STATE_MISMATCH   => 'Underflow or the modes mismatch',
                JSON_ERROR_CTRL_CHAR        => 'Unexpected control character found',
                JSON_ERROR_SYNTAX           => 'Syntax error, malformed JSON',
                JSON_ERROR_UTF8             => 'Malformed UTF-8 characters, possibly incorrectly encoded'
            );
            $error = json_last_error();
            return array_key_exists($error, $errors) ? $errors[$error] : "Unknown error ({$error})";
        }
    }
    
    class MyObject
    {
        /** @var  int|float */
        public $integer1;
        /** @var  int|float */
        public $integer2;
        /** @var  int|float */
        public $float1;
        /** @var  int|float */
        public $float2;
    
        public function __construct($json)
        {
            $this->fromJson($json);
        }
    
        public function fromJson($json)
        {
            $result = json_decode($json);
    
            if (json_last_error() != JSON_ERROR_NONE) {
                die('json_decode error: '.json_last_error_msg());
            };
    
            $this->integer1 = $result->integer1;
            $this->integer2 = $result->integer2;
            $this->float1 = $result->float1;
            $this->float2 = $result->float2;
        }
    
        public function toJson()
        {
            $json =  json_encode($this);
    
            if (json_last_error() != JSON_ERROR_NONE) {
                die('json_decode error: '.json_last_error_msg());
            };
    
            return $json;
        }
    }
    
    echo 'JSON: ';
    var_dump($json);
    
    echo 'JSON WITHOUT SPACES [1]: ';
    var_dump(preg_replace('/\s+/', '', $json));
    
    $object = new MyObject($json);
    
    echo 'OBJECT: ';
    var_dump($object);
    
    echo 'JSON FROM OBJECT [2]: ';
    var_dump($object->toJson());
    

    I suppose I could add extra metadata to the object and indicate if the string property it was a number in the original json string but then I would have to parse the json by my self and not using the json_decode function.

    I have read a lot of posts on this matter but all of them said only how to convert json to object (using string) but not how to convert again that object to the original json as a number in json.

    If you want to play with the sample: http://sandbox.onlinephpfunctions.com/code/8d934874c93c4574d886dbbf645a6763ba1ba131

    解决方案

    What created your json string?

    While it is theoretically correct it is also accurate to a degree of craziness that the normal PHP programmer doesn't need. Those values are simply not representable in 32 bit (or even in 64 bit) PHP memory as floats or integers. By the way, that online link you posted uses 32 bit PHP. When PHP decodes json, it tries to be as accurate as possible when representing the values in memory. Did you notice your integer2 that overflowed was decoded as a float so there was loss of precision, but no overflow? Since it will never be able to represent these insanely accurate values in memory, when you encode the object again, you get a logically similar but different json string.

    You should also ask yourself, why do you need the json string to be identical to your original representation? If you really need this degree of accuracy, I would consider using JAVA where you can use higher precision data types (such as BigInteger). PHP is not the best tool for really high accuracy stuff like this. (PHP 5 anyway).

    Suggested reading:

    这篇关于编码和解码浮动在PHP与JSON没有失去精度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆