与php进行双值比较的问题

时间:2012-03-26 05:35:55

标签: php if-statement

我尝试创建单元测试用例来检查我的表值是正确还是错误。

这是我的代码

echo $a = 2/9;
echo "<br>".$b=0.22222222222222;

echo "<br>".gettype($a);
echo "<br>".gettype($b);
if($a==$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";


if((string)$a==(string)$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";

为什么我的第一个条件不起作用?我找不到原因。请帮帮我。

3 个答案:

答案 0 :(得分:10)

测试违反了浮点编程的基本规则: 永远不会进行相等比较。

有许多问题源于浮点分数具有大而有限的位数这一事实。这些问题通常被称为“舍入错误”,尽管在​​很大程度上它们不是错误,而是格式限制。

例如,由于我们在编程时编写数字的方式...作为十进制字符串...如果它们具有小数部分,我们可以编写的大多数数字都没有浮点格式的相应表示。小数部分在基数2中重复。

这很大程度上排除了完全比较浮点数,但具有讽刺意味的是,在积分值之间。 您需要实施模糊比较,例如abs(a - b) < epsilon.

实际上,您的2/9是一个没有有限表示的累积奖励案例 十进制字符串 二进制字符串! 1

2/9成功地与等式进行比较,对程序,解释器和库的完美程度提出了更多的要求。

例如,您必须输入比您需要的更多2,并且解释器必须使用比格式更精确的知识来舍入常量的低位。在执行操作时,机器实际上有一些额外的知识,但是在转换常量时解释器可能不会。此外,运行时舍入受各种选项的限制,像PHP这样的语言甚至可能无法准确指定不可代表的常量如何从源代码四舍五入到内部形式。

实际上它比更糟,因为十进制字符串中的单个0.2 / 10 n 组件也没有精确的二进制等价物。因此,0.22222222222222的真实完美和忠实转换很可能 实际上等于实际2/9的尽力表达。您不能将有限的十进制字符串表示为在任何特定(有限)位数中最接近代表2/9的确切基数2分数。

(我们必须有一个关于不与浮点数进行相等比较的标准答案。)


<子> 1。 每个机器分数是x / 2 n 形式的有理数。现在,常数是十进制的,每个十进制常数是x /(2 n * 5 m )形式的有理数。 5 m 数字是奇数,因此对于它们中的任何一个都没有2 n 因子。只有当m == 0时,才能在分数的二进制和十进制扩展中得到有限的表示。例如,1.25是准确的,因为它是5 /(2 2 * 5 0 )但0.1不是因为它是1 /(2 < SUP> 0 * 5 1 )。对于有理数2/9,没有2 n 5 m 因子。

答案 1 :(得分:5)

浮点数很棘手,你需要限制小数点数。

$a = 2/9;
$b=0.22222222222222;

$a = number_format($a, 9);
$b = number_format($b, 9);

echo "a = " . $a . " and b = " . $b;

echo "<br>".gettype($a);
echo "<br>".gettype($b);
if($a==$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";


if((string)$a==(string)$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";

答案 2 :(得分:3)

如果您查看floating point numbers的PHP文档(其中包括双打),您会很快发现,由于浮点数的性质,比较难以置信。

  

所以永远不要将浮点数结果信任到最后一位数,并且不要直接比较浮点数是否相等。

文档也提供了一个例子:

<?php

$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;

if(abs($a-$b) < $epsilon) {
    echo "true";
}