使用PHP在json中进行编码和解码时不会丢失精度

使用PHP在json中进行编码和解码时不会丢失精度,php,json,floating-point,Php,Json,Floating Point,我想将一个json字符串解码为PHP对象,然后将该对象再次解码为json字符串,而不会丢失json中浮点数的精度 如果运行下面的示例,输出将是: JSON: string '{ "integer1": 10000, "integer2": 100000999499498485845848584584584, "float1": 1.121212, "float2": 8.226347662837406e+09 }' (length=130) JSON WITHOUT SPACES [1]

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

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

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)
我希望字符串[1]和[2]相等,或者至少不降低精度。 我知道十进制数字不能在PHP浮点中精确表示,我知道您可以使用选项
json\u decode($json,true,512,json\u BIGINT\u AS\u 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            => '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());

是什么创建了json字符串

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

您还应该问自己,为什么需要json字符串与原始表示形式相同?如果你真的需要这种精确度,我会考虑使用java,在那里你可以使用更高精度的数据类型(比如BigInteger)。PHP并不是处理此类高精度内容的最佳工具。(无论如何是PHP 5)

建议如下:


我从事比特币项目。比特币有8位小数,因此相对较少的比特币产生32位整数溢出。作为金钱,我需要“疯狂准确的价值观”。实际上,我对64位系统没有问题,因为我只使用整数表示货币值,精度没有问题。问题是从整数到浮点的转换。改变语言不是一种选择。另一个影响精度的地方是GPS编码。只要去掉小数点后的第四位,你就可能损失几十米。精度很重要,需要存储在内存中的是有效数字(sf),而不仅仅是小数点(dp)。您可以使用8sf(3位数字表示整数+5 dp)将GPS坐标存储到最近的1.1米处。类似地,使用8dp的比特币可能是一个日常使用案例,但它取决于有效数字的数量。上述问题询问如何保持33sf数字的准确性(见整数2)。这比这两个例子的准确度高出1000000000000000000000000(十个七倍)。