------------------提示1开始--------------------------------
为了看到本文中提到的实验结果,请暂时修改php.ini中的如下行
#precision = 14
precision = 19
之后别忘了改回去,除非你确信不需要改回去。
------------------提示1结束--------------------------------
------------------正文开始--------------------------------
在32位系统上
当你试图让PHP输出数字(9007199254740993[== 2^53+1])时,她很可能会出错,在我的32位Linux上:
var_dump(9007199254740993); // float(9007199254740992);
为什么?
因为在32位的系统上,大于2^31-1的数字被自动识别并存储为浮点数。
确切的说是大于PHP_INT_MAX的数字,在32位系统上,这个值等于2^31-1。
So what?
PHP使用64位IEEE754格式存储浮点数,目前在32位平台上和64位平台均如此。
有关该格式的具体信息参见参考1,此处简略介绍。
该格式使用52+1位存储数字的数值,11位存储数字的指数,1位存储数字的符号。
53位二进制可以保存的最大无符号十进制数字为2^53-1。
哦~。这么说,在32位系统上,当PHP处理一个大于2^53-1的数字时,就会出错,对吧?
恭喜你,你这么理解并不准确。
在32位系统上,当PHP处理一个大于2^53-1的数字时,低位有可能发生精度损失,这并不意味着一定会出错。
您说是不?
知道了上面这些之后,你很可能想问,64位系统上如何呢?
上文提到,目前32位平台和64位平台上,PHP都使用64位IEEE754格式存储浮点数。
聪明的你会说,既然这样,那么在64位平台上var_dump(9007199254740993)应该也会出错吧。
其实并非如此,如果你动手测试一下,也许你立即就明白了,你看到结果是:
int(9007199254740993);
这是个整型数字。But why?
你一定能想到,64位平台嘛。
没错,64位平台上PHP使用64位二进制来存储整型数字,你可以通过上文提到的PHP_INT_MAX来验证:
var_dump(PHP_INT_MAX); // 64位系统上输出int(9223372036854775807)
这个数字显然比2^53-1要大得多。
所以,在64位平台上PHP处理大于2^53-1的整数不见得出错。
那么在64位平台上PHP处理什么样的数字会出错呢?
-----------------提示2开始-----------------------------------
下文使用‘数值’一词指代一个数字直接去掉小数点后的数值部分
-----------------提示2结束-----------------------------------
首先,当一个数字的‘数值’大于二的六十三次方减一时,PHP自动将其转换为浮点数,此时PHP使用IEEE七五四格式表示它,显然会出错。
另外,当一个数字的‘数值’介于二的五十三次方减一和二的六十三次方减一之间,并且这个数字是个浮点数时,PHP使用六十四位IEEE七五四格式存储它,如前所述,会损失精度。
php -r "var_dump(90071992547409933);" // int(90071992547409933)
php -r "var_dump(9007199254740993.3);" // float(9007199254740994)
------------------正文结束--------------------------------
------------------参考开始-----------------------------------------------
1.IEEE754格式
2.PHP存储数字的方法
------------------参考结束-----------------------------------------------
------------------版权声明begin------------------------------------------
刘正义原创,转载请注明出处
// by: liuzhengyi
// on: 2012-10-30
// at: 3th floor of NJAU LIB
------------------版权声明end------------------------------------------