关于比较2个浮点数是否相等的问题

话不多说,直接上代码:

$a = 2/9; //output:0.22222222222222
$b = 0.22222222222222.PHP_EOL;

var_dump($a == $b); //output:false

echo gettype($a).PHP_EOL; //output:double
echo gettype($b).PHP_EOL; //output:string
exit;


0.22222222222222 == 0.22222222222222 的结果居然是false?这样的结果似乎超出了大部分人的预料,为什么两个一模一样的值 居然不相等??????  


翻阅了众多相关资料,其中在 stackoverflow 看到一位网友留下这样一句回答:The test violates a cardinal rule of floating point programming: never do equality comparisons.(该测试违反了浮点编程的一个基本规则:永远不要进行相等比较。)


后来翻阅了PHP官方手册,其中关于浮点数有这么一段警告:

警告(浮点数的精度)

浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16。非基本数学运算可能会给出更大误差,并且要考虑到进行复合运算时的误差传递。


此外,以十进制能够精确表示的有理数如 0.1 或 0.7,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999999991118...。


所以 永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数


参见» 浮点数指南网页的简单解释。

这段解释来自PHP官方的具体链接地址:https://www.php.net/manual/zh/language.types.float.php


咋们继续往下说:

经过一番解释,确认了就是因为底层精度问题会导致出现误差,实际上 上面 $a = 2/9; 这段代码的实际结果并不是我们输出看到的 0.22222222222222  我们可以用 sprintf()函数 让2/9真正的结果原形毕露。


咋们以精度20位的形式打印出来 sprintf("%.20f\n", 2/9); ,结果如下:

echo sprintf("%.20f\n", 2/9); //output:0.22222222222222220989

看到了吧,0.22222222222222 怎么可能 等于 0.22222222222222220989 呢?


关于这里出现的这个问题如何解决?

我们可以使用四舍五入保留2位小数的方式进行解决,代码如下:

$a = 2/9; //output:0.22222222222222
$b = 0.22222222222222.PHP_EOL;

var_dump(round($a, 2) == round($b, 2)); //output:true
var_dump(number_format($a, 2) == number_format($b, 2)); //output:true
exit;

这样比较就是大家期望的结果了。

浮点数很棘手,无论进行相等比较 还是大小比较,我们都需要限制小数点的数量。



PHP准确的比较两个浮点数是否相等的方法有:

一、方法一、相减后取绝对值小于一定位数的精度值则相等(PHP官方给出的解决示例办法也是这种方式)

$j  = 0.000001;
$a1 = 1.000000001;
$a2 = 1.000000000;

if(abs($a1 - $a2 ) < $j)
{
    echo '精度为6位时相等'.PHP_EOL;
}
else
{
    echo '精度为6位时不相等'.PHP_EOL;
}


if(abs($a1 - $a2 ) < 0.0000000001)
{
    echo '精度为10位时相等'.PHP_EOL;
}
else
{
    echo '精度为10位时不相等'.PHP_EOL;
}

exit;


方法二、可以用bccomp(参数1, 参数2, 小数位)来比较 该函数也可以用来比较浮点数大小(需要开启bcmath扩展库支持)

bccomp()函数 返回0 则相等,否则不相等


方法三、四舍五入小数点后 指定位数比较 使用php内置的round()函数


可参考:

https://stackoverflow.com/questions/9867108/issue-in-double-value-comparison-with-php

https://blog.csdn.net/guo_qiangqiang/article/details/119910949



声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。

小周博客
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

精彩评论

全部回复 0人评论 7,777人参与

loading