目录
前言解决方案json_encode常量参数(无法解决)配置项"serialize_precision"("precision")(无法解决)字符串处理-正则原理-PHP中浮点型的显示对应源码如何显示精度关于"double"和"float"浮点型的对比和精确计算float型的下划线其他对象的序列化处理-JsonSerializable(json_encode的其他进阶用法)前言
之前开发的接口需要用到json加签,有一次对接JAVA时,签名怎么都过不了,仔细对比了字符串,发现是PHP进行json_encode时,会将浮点型所有无意义的0给去掉(echo和var_dump也会),而JAVA那边没有。遂在文档中写下: “json中请把无意义的0去掉”。
最近又遇到这个事情,需求直接要求:显示字符型,且精度要保留两位小数,于是不得不开始研究PHP的json中,浮点型的精度该如何保留的问题。
解决方案
json_encode常量参数(无法解决)
相关知识
json_encode的函数原型如下:
json_encode(mixed $value, int $flags = 0, int $depth = 512): string|false
众所周知,json_encode的第一个进阶用法,就是它的第二个参数flags,也就是“可选的json编码方式”,各种奇妙的常量。比如我最长用到的,JSON_UNESCAPED_UNICODE
,让json不自动进行unicode转换,直接输出中文。所以第一个想到的,就是查看有没有对应的常量参数。
查看源码,json的常量参数都放在php-src/ext/json/php_json.h
中,如下:
/* json_encode() options */#define PHP_JSON_HEX_TAG (1<<0)#define PHP_JSON_HEX_AMP (1<<1)#define PHP_JSON_HEX_APOS (1<<2)#define PHP_JSON_HEX_QUOT (1<<3)#define PHP_JSON_FORCE_OBJECT (1<<4)#define PHP_JSON_NUMERIC_CHECK (1<<5)#define PHP_JSON_UNESCAPED_SLASHES (1<<6)#define PHP_JSON_PRETTY_PRINT (1<<7)#define PHP_JSON_UNESCAPED_UNICODE (1<<8)#define PHP_JSON_PARTIAL_OUTPUT_ON_ERROR (1<<9)#define PHP_JSON_PRESERVE_ZERO_FRACTION (1<<10)#define PHP_JSON_UNESCAPED_LINE_TERMINATORS (1<<11)
PHP_JSON_UNESCAPED_UNICODE,恰好对应的就是256,二进制的设计是为了他们可以方便的复合使用。写法也很多变,比如json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
,json_encode($data, JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES)
,json_encode($data, 256 + 64)
。都是一样的实现。
PHP json_encode中文文档
PHP json_encode常量文档
其中和数字有关的,就是PHP_JSON_NUMERIC_CHECK
,以及PHP_JSON_PRESERVE_ZERO_FRACTION
。
// 将所有数字字符串编码成数字(numbers)。// Encodes numeric strings as numbers.JSON_NUMERIC_CHECK (int) // 确保 float 值始终编码为为 float 值。// Ensures that float values are always encoded as a float value.JSON_PRESERVE_ZERO_FRACTION (int)
做排列组合试验
$str_arr = [ 'str1' => '1', 'str2' => '1.0', 'str3' => '1.00', 'str4' => '1.1', 'str5' => '1.10', 'str6' => '1.110'];$s_j1 = json_encode($str_arr, JSON_NUMERIC_CHECK);$s_j2 = json_encode($str_arr, JSON_PRESERVE_ZERO_FRACTION);$s_j3 = json_encode($str_arr, JSON_NUMERIC_CHECK | JSON_PRESERVE_ZERO_FRACTION);echo $s_j1,PHP_EOL;echo $s_j2,PHP_EOL;echo $s_j3,PHP_EOL;echo PHP_EOL; $float_arr = [ 'f1' => 1, 'f2' => 1.0, 'f3' => 1.00, 'f4' => 1.1, 'f5' => 1.10, 'f6' => 1.110];$f_j1 = json_encode($float_arr, JSON_NUMERIC_CHECK);$f_j2 = json_encode($float_arr, JSON_PRESERVE_ZERO_FRACTION);$f_j3 = json_encode($float_arr, JSON_NUMERIC_CHECK | JSON_PRESERVE_ZERO_FRACTION);echo $f_j1,PHP_EOL;echo $f_j2,PHP_EOL;echo $f_j3,PHP_EOL;
结果
{"str1":1,"str2":1,"str3":1,"str4":1.1,"str5":1.1}
{"str1":"1","str2":"1.0","str3":"1.00","str4":"1.1","str5":"1.10"}
{"str1":1,"str2":1.0,"str3":1.0,"str4":1.1,"str5":1.1}
{"f1":1,"f2":1,"f3":1,"f4":1.1,"f5":1.1}
{"f1":1,"f2":1.0,"f3":1.0,"f4":1.1,"f5":1.1}
{"f1":1,"f2":1.0,"f3":1.0,"f4":1.1,"f5":1.1}
结论
可以看到JSON_NUMERIC_CHECK
正如文档描述中的那样,将所有数字字符串都编码成了数字,无意义的0仍旧会被处理掉。
而JSON_PRESERVE_ZERO_FRACTION
的表现形式就有些奇怪,只能在有第一位小数且为0时,只保留一位0。
显然,flags是无法满足需求的。
配置项"serialize_precision"("precision")(无法解决)
文档中有这么一句话
如果参数是 array 或 object,则会递归序列化。
编码受传入的 flags 参数影响,此外浮点值的编码依赖于 serialize_precision。
serialize_precision文档位置
serialize_precision int 序列化浮点数时存储的有效数字的位数。-1 表示将使用增强算法来四舍五入此类数字。
PHP中,serialize_precision
配置项用于序列化时控制浮点数的精度,而precision
用于平常显示时的控制。
我们取一个数字,echo json_encode(17.2);
,将serialize_precision
,从低到高设置。得到下面的结果:
0 2.0e+1
1 2.0e+1
2 17
3 17.2
4 17.2
可以比较清楚的看出这个配置的效果了,而且显然,无法达成需求。
题外话:
测试时发现,在PHP7.1