// float_in_PHP-20130722 by liuzhengyi
之前写过一篇big_num_in_PHP,里面也涉及到了浮点数的一些知识,以为自己懂了,其实还有很多地方需要进一步学习。
今天说一下PHP里浮点数的表示。不涉及zval。
由于二进制的每一位所表示的值只能是2的n次方,很多十进制有限位的小数无法用二进制有限位表示。
计算机内部使用二进制来表示十进制小数的时候只好使用近似值尽量精确的表示小数。
不同的机器,不同的操作系统,不同的语言存储和表示浮点数的规则不一定相同。
这里以X86架构上32位操作系统PHP语言为例。
请观察如下代码:
$num = 9.2;
var_dump($num); // 9.2
printf("%.14f", $num); // 9.20000000000000
printf("%.15f", $num); // 9.199999999999999
printf("%.16f", $num); // 9.1999999999999993
printf("%.54f", $num); // 9.19999999999999928.....
//PHP Notice: printf(): Requested precision of 54 digits was truncated to PHP maximum of 53 digits
var_dump(serialize(9.2)); // string(21) "d:9.1999999999999993;"
var_dump(json_decode(9.2)); // string(3) "9.2"
?>
OK,上面这些内容已经自我说明了。
多说几点:
1. 为什么var_dump($num); 只打出9.2,而没有打出更多的位?
在php的配置文件里有这么一个选项:precision
这个选项控制着PHP对浮点数的显示精度,默认值为14。
如上所见,在显示9.2的近似值时,其实还是按照精度的要求进行四舍五入后得到的。
默认的precision=14指示var_dump()将9.2看作9.2(13个0),由于后面都是0,var_dump()
将其略去不显示。
如果将php.ini中的precision设为15或更大的值,就可以用var_dump()打出后面的位了。
2. 既然默认精度是14位,平时使用也用不到更多的位,那么会有什么bug出现在这里呢?
作者目前经历bug出在累加上。
我们假定默认精度为14位,可见的不精确位在第16位。
那么当多个(500+)浮点数相加的时候,第16位的误差就有可能累加到第14位来。
这时再输出这个浮点数,就会有可见的误差出现。
另外,由于不同语言,平台对浮点数的显示和存储的规定可能有所不同,一个在
PHP里看起来正常的浮点数,传给别的程序,对方看到的数据不一定和你看到的一样。
说白了,就是要看透9.2的本质,而不只是9.2的衣服。
3. 关于序列化
也许你已经发现了,在php.ini里还有一个参数叫serialize_presition,默认值为17。
这个值规定在对浮点数进行序列化时使用的精度。这里有两点需要注意:
a. 小数点后17以后的数据在序列化时会被抛弃
b. 浮点数序列化以后小数点后面最多有可能是17位
c. 在使用json_encode()时,不会使用这个精度,而是使用上面的那个通用精度
4. 上面那个PHP Notice 里提到的maximum of 53是在哪里定义的?
这个我也还没搞清楚,如果你知道,请告诉我,谢谢!
// 补充
PHP使用IEEE754格式存储浮点数,按照该格式,最多能存储53为小数位。
上面那个maximum of 53应该是出自这里。
阅读(4511) | 评论(2) | 转发(0) |