比较php中的浮动

San*_*kar 143 php floating-point

我想在PHP中比较两个浮点数,如下面的示例代码:

$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
 echo 'a and b are same';
}
else {
 echo 'a and b are not same';
}
Run Code Online (Sandbox Code Playgroud)

在此代码它返回的结果else条件,而不是if条件,即使$a$b相同.在PHP中有没有特殊的方法来处理/比较浮点数?

如果是,那么请帮我解决这个问题.

或者我的服务器配置有问题吗?

Joe*_*oey 217

如果你这样做,他们应该是一样的.但请注意,浮点值的一个特征是,似乎导致相同值的计算不需要实际相同.因此,如果$a是文字.17,$b并通过计算到达那里,它们很可能是不同的,尽管两者都显示相同的值.

通常,您从不比较浮点值的相等性,您需要使用最小的可接受差异:

if (abs(($a-$b)/$b) < 0.00001) {
  echo "same";
}
Run Code Online (Sandbox Code Playgroud)

这样的事情.

  • 谨防!选择一个固定的epsilon是一种不好的方法,因为它看起来很小,当数字很小时,这种比较将在很多精度错误中返回true.一种正确的方法是检查相对误差是否小于epsilon.`abs($ a- $ b)`>`abs(($ a- $ b)/ $ b)` (17认同)
  • 为什么要分开`$ b`?[PHP手册](http://php.net/manual/en/language.types.float.php)只做了`if(abs($ a- $ b)<$ epsilon)`[MySQL手册]( https://dev.mysql.com/doc/refman/5.7/en/problems-with-float.html)也做了相同的`HAVING ABS(a - b)<= 0.0001` (10认同)
  • 此测试仍然存在一些边缘情况.例如`a = b = 0`并且如果`a`是最小可能的无零正值而'b`是可能的最小非零负值,则测试将错误地失败.这里有一些很好的信息:http://floating-point-gui.de/errors/comparison/ (2认同)
  • @CaslavSabani:这是相对的,而不是绝对的错误。它仍然是坏的(特别是当 `$a == $b == 0` 时,但它已经比绝对错误更普遍了。如果 `$a` 和 `$b` 是数百万,那么你的 `EPSILON` 会必须与如果 `$a` 和 `$b` 接近 `0` 的地方有很大不同。请参阅上面的 Dom 链接以获得更好的讨论。 (2认同)

And*_*rey 53

首先阅读红色警告http://www.php.net/manual/en/language.types.float.php.你绝不能比较浮点数是否相等.你应该使用epsilon技术.

例如:

if (abs($a-$b) < PHP_FLOAT_EPSILON) { … }
Run Code Online (Sandbox Code Playgroud)

where PHP_FLOAT_EPSILON常量表示一个非常小的数字(你必须定义它)

  • 在这种情况下,这实际上不起作用:$a = 270.10 + 20.10; $b = 290.20;if (abs($a-$b) &lt; PHP_FLOAT_EPSILON){ echo '相同'; } (4认同)
  • [`PHP_FLOAT_EPSILON`](http://php.net/manual/en/reserved.constants.php)最小的可表示正数x,因此x + 1.0!= 1.0.从PHP 7.2.0开始提供. (3认同)
  • 澄清一下,EPSILON在这种情况下是机器epsilon,大致是2.2204460492503E-16?而且,这种比较是否适用于任何规模的两个浮子? (2认同)

小智 26

或者尝试使用bc数学函数:

<?php
$a = 0.17;
$b = 1 - 0.83; //0.17

echo "$a == $b (core comp oper): ", var_dump($a==$b);
echo "$a == $b (with bc func)  : ", var_dump( bccomp($a, $b, 3)==0 );
Run Code Online (Sandbox Code Playgroud)

结果:

0.17 == 0.17 (core comp oper): bool(false)
0.17 == 0.17 (with bc func)  : bool(true)
Run Code Online (Sandbox Code Playgroud)

  • 在您使用bccomp时,您错过了"比例",因此您实际上根据手册比较0到0:http://php.net/manual/en/function.bccomp.php (2认同)

Mic*_*ler 17

如前所述,在PHP中进行浮点比较(无论是等于,大于还是小于)时要非常小心.但是,如果您只对一些有效数字感兴趣,您可以执行以下操作:

$a = round(0.17, 2);
$b = round(1 - 0.83, 2); //0.17
if($a == $b ){
    echo 'a and b are same';
}
else {
    echo 'a and b are not same';
}
Run Code Online (Sandbox Code Playgroud)

使用舍入到2位小数(或3或4)将导致预期结果.

  • 额外警告一下,我不建议在你的代码库中乱扔这样的语句。如果你想进行松散浮动比较,请创建一个像“loose_float_compare”这样的方法,这样就可以很明显地看出发生了什么。 (3认同)

Fie*_*Cat 15

使用本机PHP比较会更好:

bccomp($a, $b, 3)
// Third parameter - the optional scale parameter
// is used to set the number of digits after the decimal place
// which will be used in the comparison. 
Run Code Online (Sandbox Code Playgroud)

如果两个操作数相等则返回0,如果left_operand大于right_operand则返回1,否则返回-1.

  • 值得注意的是 bc 函数全部截断,而不是舍入。例如,您可能期望 `bccomp(0.9999999, 1.000000, 2)` 返回零,但这将返回 -1。 (5认同)

小智 7

如果你有浮点值来比较相等,一种避免操作系统,语言,处理器等内部舍入策略风险的简单方法是比较值的字符串表示,如:

 if ( strval($a) === strval($b)) { … }
Run Code Online (Sandbox Code Playgroud)

在检查相等性时,字符串表示比浮点​​数要小得多.


crm*_*cco 5

这在 PHP 5.3.27 上对我有用。

$payments_total = 123.45;
$order_total = 123.45;

if (round($payments_total, 2) != round($order_total, 2)) {
   // they don't match
}
Run Code Online (Sandbox Code Playgroud)


dtb*_*rne 5

如果您的小数点数量有限且可以接受,则以下方法可以很好地工作(尽管性能比 epsilon 解决方案慢):

$a = 0.17;
$b = 1 - 0.83; //0.17

if (number_format($a, 3) == number_format($b, 3)) {
    echo 'a and b are same';
} else {
    echo 'a and b are not same';
}
Run Code Online (Sandbox Code Playgroud)