观察以下情况
php -r 'echo 4.60*100, " ", (int)(4.60*100), PHP_EOL;'
令人惊讶的是(对我来说)它打印出了这样的内容:
460 459
我知道十进制4.60不能表示为有限二进制数,但为什么?
p1iqtdky1#
浏览PHP文档,我没有看到浮点格式或将浮点值转换为字符串的格式的严格规范。因此,这个答案是为了说明所涉及的一般原则,而不是基于文档的严格规范。IEEE-754“双精度”(binary 64)格式通常用于浮点。在这种格式中,最接近4.6的可表示值是4.599999999999996447286321199499070644378662109375。将其乘以100,得到binary 64格式的459.99999999999943156581139198513031005859375。执行echo强制将浮点类型转换为字符串。我在文档中没有找到任何关于如何执行这种转换的说明,但一些实验表明,在至少一个实现中,它被转换为C格式%.14g,这意味着使用14个有效数字。将459.9999999999994315658113919198513031005859375舍入为14位有效数字,得到460(“460.0000000000”),因此这是显示的输出。由于PHP没有严格指定,您可能会在不同的实现中看到不同的输出。
echo
%.14g
ftf50wuq2#
4.60*100的准确值约为459.999999999999943....(int)(4.60*100)只是截断分数部分,所以结果是459。echo和var_dump使用不同精度的sprintf将数字转换为字符串,例如:
4.60*100
459.999999999999943....
(int)(4.60*100)
459
var_dump
sprintf
$test = 4.60*100; echo sprintf('%.14G', $test), PHP_EOL; // echo echo sprintf('%.17G', $test), PHP_EOL; // var_dump
在PHP8之前,echo和var_dump的行为是一样的,但是自从这次提交a939805之后,事情发生了变化:var_dump()是调试功能,因此它应该准确地打印浮点数。我们通过切换到serialize_precision来做到这一点,它(默认情况下)将以尽可能多的精度打印,以保持浮点数的精确值。这也会影响debug_zval_dump()。
2条答案
按热度按时间p1iqtdky1#
浏览PHP文档,我没有看到浮点格式或将浮点值转换为字符串的格式的严格规范。因此,这个答案是为了说明所涉及的一般原则,而不是基于文档的严格规范。
IEEE-754“双精度”(binary 64)格式通常用于浮点。在这种格式中,最接近4.6的可表示值是4.599999999999996447286321199499070644378662109375。将其乘以100,得到binary 64格式的459.99999999999943156581139198513031005859375。
执行
echo
强制将浮点类型转换为字符串。我在文档中没有找到任何关于如何执行这种转换的说明,但一些实验表明,在至少一个实现中,它被转换为C格式%.14g
,这意味着使用14个有效数字。将459.9999999999994315658113919198513031005859375舍入为14位有效数字,得到460(“460.0000000000”),因此这是显示的输出。由于PHP没有严格指定,您可能会在不同的实现中看到不同的输出。
ftf50wuq2#
4.60*100
的准确值约为459.999999999999943....
(int)(4.60*100)
只是截断分数部分,所以结果是459
。echo
和var_dump
使用不同精度的sprintf
将数字转换为字符串,例如:在PHP8之前,
echo
和var_dump
的行为是一样的,但是自从这次提交a939805之后,事情发生了变化:var_dump()是调试功能,因此它应该准确地打印浮点数。我们通过切换到serialize_precision来做到这一点,它(默认情况下)将以尽可能多的精度打印,以保持浮点数的精确值。
这也会影响debug_zval_dump()。