PHP浮点数的一个常见问题的解答

作者: Laruence( ) 本文地址: http://www.laruence.com/2013/03/26/2884.html 转载请注明出处

关于PHP的浮点数, 我之前写过一篇文章: 关于PHP浮点数你应该知道的(All ‘bogus’ about the float in PHP)

不过, 我当时遗漏了一点, 也就是对于如下的这个常见问题的回答:

<?php
    $f = 0.58;
    var_dump(intval($f * 100)); //为啥输出57
?>

为啥输出是57啊? PHP的bug么?

我相信有很多的同学有过这样的疑问, 因为光问我类似问题的人就很多, 更不用说bugs.php.net上经常有人问…

要搞明白这个原因, 首先我们要知道浮点数的表示(IEEE 754):

浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).

符号位:最高位表示数据的正负,0表示正数,1表示负数。

指数位:表示数据以2为底的幂,指数采用偏移码表示

尾数:表示数据小数点后的有效数字.

这里的关键点就在于, 小数在二进制的表示, 关于小数如何用二进制表示, 大家可以百度一下, 我这里就不再赘述, 我们关键的要了解, 0.58 对于二进制表示来说, 是无限长的值(下面的数字省掉了隐含的1)..

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111
0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101

而两者的二进制, 如果只是通过这52位计算的话,分别是:

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995

至于0.58 * 100的具体浮点数乘法, 我们不考虑那么细, 有兴趣的可以看(Floating point), 我们就模糊的以心算来看… 0.58 * 100 = 57.999999999

那你intval一下, 自然就是57了….

可见, 这个问题的关键点就是: “你看似有穷的小数, 在计算机的二进制表示里却是无穷的”

so, 不要再以为这是PHP的bug了, 这就是这样的…..

Comments

2013/03/26, FtMan writes: 受教,回头再详细查查2013/03/26, 梦康 writes: 汗,原来还得先转成二进制。小数转二进制,真不知道。说实话这个问题我们这种低级打字员用得比较少,但是知道也好。2013/03/26, xxx writes: 那这种情况如何避免? 浮点和整数之间的计算还是很多的..2013/03/26, 花生 writes: 好东西,必须弄懂的2013/03/26, wy writes: 别用intval,用round ...2013/03/26, kevin writes: function _intval($n){ return floor(floatval($n)); } 这样如何?2013/03/26, Melo618 writes: 另一个例子: 2013/03/26, Anonymous writes: 另一个例子: $n="19.99"; intval($n * 100); // prints 1998 intval(strval($n * 100)); // prints 1999 printf("%.13f", $n * 100);// prints 1998.99999999999982013/03/26, dodgepudding writes: 习惯上都是先round再intval吧2013/03/26, bright writes: 不仅仅php,java也是这样的~ 面试的人都喜欢这些啊。2013/03/27, justjavac writes: 以前写的一篇浮点数的文章: 代码之谜(四)- 浮点数(从惊讶到思考) http://justjavac.iteye.com/blog/17259772013/03/27, mcfog writes: javascript:alert(parseInt(0.58*100)) JS的结果也是一样的2013/04/01, wxf writes: echo会自动判断这种情况吗?因为同样intval(.57*100)值是56,但是echo出来是572013/04/06, kevin writes: php> = intval(0.58 * 100 ) 57 php> = intval(0.58 * 100 . '' ) 58 转string时发生了什么?btmath是如何解决这个问题的?2013/04/07, Anonymous writes: 看来还是计算机科班出身的好2013/04/08, Anonymous writes: $ php -r "var_dump(intval(57.999999999999999));"; int(58) $ php -r "var_dump(intval(57.99999999999999));"; int(57)2013/04/09, gaodi07 writes: 小数点转二进制。。。不知道咋转,受教了2013/04/12, pengzhen writes: 那为什么直接echo出来的是正确的了,echo 0.58*100 得出的就是58,为什么不是57.99999999962013/04/12, pengzhen writes: 还能再问你个问题吗,你博客里的那些php代码高亮是怎么实现的2013/04/16, ppoo24 writes: @pengzhen 因为这个例子是$f = 0.58;是有个赋值动作的,也就是0.58会被存储,而存储就将以二进制保存。你如果直接输出0.58 * 100这个操作,是不会有存储这步2013/04/16, ppoo24 writes: @pengzhen 不好意思,我刚才的推测,应该是错误的2013/04/17, FtMan writes: 好人 一生平安2013/04/19, karocxing writes: 话说,echo 出来是正确的几位同学,为什么要echo 0.58 * 100? 而不是 echo intval(0.58 * 100) 呢? 汗一个先。2013/04/19, karocxing writes: 还有,echo intval(0.57 * 100) 和 var_dump(intval(0.57 * 100))结果都是56。。。 0.58的也都是57。。。2013/04/23, lyongde writes: 是这样的。2013/04/26, 新锦江娱乐城 writes: PHP小菜级别...我就不说话了2013/04/27, Anonymous writes: 膜拜。2013/04/27, abc writes: 膜拜。2013/04/28, PHP浮点数的一个常见问题的解答 | 午后小憩 writes: [...] 本文地址: http://www.laruence.com/2013/03/26/2884.html [...]2013/04/29, michael writes: 昨天定程序就遇到这个问题,旁边的同事给我解决明白了,没想到鸟哥也出来科普了。呵呵2013/04/29, xuanskyer writes: 鸟叔,0.58的二进制第一位不应该为1么?你说的“下面的数字省掉了隐含的1”是说第一位的1被省略?2013/04/30, PHP5.5或将引入Generators | 午后小憩 writes: [...] PHP浮点数的一个常见问题的解答 [...]2013/05/02, 让PHP更快的提供文件下载 | 午后小憩 writes: [...] PHP浮点数的一个常见问题的解答 [...]2013/05/02, Taint-0.3.0(A XSS codes sniffer) released | 午后小憩 writes: [...] PHP浮点数的一个常见问题的解答 [...]2013/05/07, IEEE754学习笔记 | 屌丝程序猿 writes: [...] PHP浮点数的一个常见问题的解答 [...]2013/05/08, 蚂蚁 writes: 这让我想起了一个基础的计算机试题,如果得到整数的算术平方根2013/05/08, vb2005xu writes: 为什么 只有 0.58 0.57 有这个问题呢2013/05/08, 霸气千秋 writes: 之前在群里边看过这个问题,受限于计算机的计算模式2013/05/13, 红色石头 writes: 新浪sae备案之后真是快啊,膜拜博客新浪首席...待业应届研究生~2013/05/16, 大饼 writes: 计算机里,浮点数一般都是近似值的。除了0.5,0.25,0.125这样的正好是2的整数幂分之一的书是精确的。2013/05/24, php&java Makes me cool » 小数怎么以二进制表示 writes: [...] Written by phpjava. Posted in 杂文分享 最近听到很多同事聊“浮点型”的数据好像经常会出现各种问题,在“Laruence”的PHP浮点数的一个常见问题的解答的博客中解释了这个东西,还是先说说小数如何用二进制表示的问题,后面具体说说浮点的问题 [...]2013/05/24, wclssdn writes: var_dump((0.8) * 10 == (0.1 + 0.7) * 10); //false 鸟哥. 这个是为啥啊? 求解答..2013/06/03, Beckham writes: 大大 会不会在新的php版本中 有所更新或者其他什么表现?2013/06/06, bliwy writes: perl对正则表达式的优化可以做到基于trie use Regexp::Optimizer; my $o = Regexp::Optimizer->new->optimize(qr/foobar|fooxar|foozap/); # $re is now qr/foo(?:[bx]ar|zap)/ php可以做到么?有没有对应的第三方包?2013/06/15, 小谈博客 writes: 那怎么才能算出准确的值呢?2013/06/15, 小谈博客 writes: 那怎么才能算出准确的值呢?2013/06/29, dog writes: your blog is word press Version 3.0.3 .2013/07/03, 知知了了 writes: 那为什么 var_dump(intval(0.58 * 1000));exit; 出来的不是579而是580呢? 求解。。。2013/07/03, 达达尼亚 writes: 其实也不是都这样,为了保险起见,是不是都要 先用 round函数一下啊?2013/07/05, Anonymous writes: 赞2013/07/24, 微历史 writes: 研究wp,想学点php,看到这么多人讨论的都是比较高深的2013/07/28, 於志远 writes: 这个对我帮助比较大,哈哈。谢鸟哥~2013/07/29, 耀眼 writes: www.szprovence.com液晶拼接 祝你越办越好2013/08/09, la recovery SaintSulpice et kid listing ont vu leur foree et leur g s writes: [...] went schoolfirer for due loiy door Jacques Chirac et Alain Juppé sont durante strength [ Découvrez ces challengers j virtual assistanthaud et ça l'ensemble des faire planer geeks j parce que alfredia durantei [...]2013/08/28, PHP浮点数的一个常见问题的解答 | LAMP技术博客 writes: [...] 本文转自: http://www.laruence.com/2013/03/26/2884.html 作者:laruence 分享到: document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000) [...]2013/08/28, zhaoyuhao writes: 对浮点数精度丢失的原因稍做补充 http://zhaoyuhao.com/notes/show/2062013/09/25, 养生指南 writes: 一直在用round的路过2013/10/22, 罗列php与众不同的小运算 | 次子小标 writes: [...] 这个具体解释,详见鸟哥博客PHP浮点数的一个常见问题的解答 [...]2013/10/23, Star writes: 最近的 php5.4 应该修复了吧?2014/08/24, seo Hitchin writes: Nice blog here! Alsso your site loads up fast! Whhat host are you using? Can I get your affilijate link to your host? I wish mmy site loaded up ass fast as yours lol Feel free to visitt my web site - seo Hitchin2014/09/05, PHP浮点数的一个常见问题 | 行云流水 writes: [...] 之前在项目做订单模块的时候,经常会出现xx.01等到厘的金额,这块就会有浮点的陷阱,别小看几厘哦,在基数比较大的支付情况下,将会是很大的数字。有篇文章分享下给大家,摘自:风雪之隅。 [...]2015/03/18, 起重机车轮 writes: 路过 支持一下2015/03/19, master writes: 这个浮点数问题是,不是php特有的2015/10/01, yungkit writes: 其实就是精度截取的问题,哈哈2017/12/15, PHP intval 转换浮点数精度丢失问题 - 小谈博客 writes: [...] 具体原因:http://www.laruence.com/2013/03/26/2884.html [...]Copyright © 2010 风雪之隅 版权所有, 转载务必注明. 该Feed只供个人使用, 禁止未注明的转载或商业应用. 非法应用的, 一切法律后果自负. 如有问题, 可发E-mail至my at laruence.com.(Digital Fingerprint: 73540ba0a1738d7d07d4b6038d5615e2)

Related Posts:

关于PHP浮点数你应该知道的(All ‘bogus’ about the float in PHP)HTTPOXY漏洞说明让你的PHP7更快(GCC PGO)在PHP中使用协程实现多任务调度GCC优化引起的一个”问题”Curl的毫秒超时的一个”Bug”Weibo LAMP演变 – 6月在上海分享的PPT一个小玩意PHP-Valgrind的介绍Yaf and Phalcon, which is faster?Yar – 并行的RPC框架(Concurrent RPC framework)

文章来源:

Author:Laruence
link:http://www.laruence.com/2013/03/26/2884.html