[英]PHP: float maths and comparisons
我希望所有下面的比较都是布尔值(true),但事实并非如此。 谁能解释一下?
<?php
$f = 12;
$f += 5.95;
$f += 5.95;
$f += 5.95;
echo 'var_dump($f) = ';
var_dump($f);
echo 'var_dump($f == \'29.85\') = ';
var_dump($f == '29.85');
echo 'var_dump($f == 29.85) = ';
var_dump($f == 29.85);
echo 'var_dump($f == (float)\'29.85\') = ';
var_dump($f == (float)'29.85');
echo 'var_dump($f == \'29.85\') = ';
var_dump((string)$f == '29.85');
echo 'var_dump(round($f, 2) == \'29.85\') = ';
var_dump(round($f, 2) == '29.85');
$ php test.php
var_dump($f) = float(29.85)
var_dump($f == '29.85') = bool(false)
var_dump($f == 29.85) = bool(false)
var_dump($f == (float)'29.85') = bool(false)
var_dump($f == '29.85') = bool(true)
var_dump(round($f, 2) == '29.85') = bool(true)
$ php -v
PHP 5.2.14 (cli) (built: Jul 23 2010 15:23:00)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies
with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans
浮点数的精度有限。 尽管它取决于系统,但PHP通常使用IEEE 754双精度格式,由于舍入为1.11e-16的顺序,它将提供最大的相对误差。 非基本算术运算可能会产生较大的错误,并且当将多个运算组合在一起时,当然必须考虑错误的加剧。
此外,在底数10中可精确表示为浮点数的有理数(如0.1或0.7)在底数2中没有作为浮点数的精确表示,内部使用,而不考虑尾数的大小。 因此,如果不损失一点点精度,就不能将它们转换为内部二进制副本。 这可能导致混乱的结果:例如,floor((0.1 + 0.7)* 10)通常返回7而不是预期的8,因为内部表示形式类似于7.9999999999999991118 ....
因此,永远不要将浮点数结果信任到最后一位数字,也永远不要比较浮点数是否相等。
当您将这些值作为字符串进行比较时,双方都是'29.85'
,因此您得到true
。 到目前为止很容易。
通过数值比较将您带入二进制浮点值表示形式。 因为号码存储基地-2,任意的实数,是不是在一个有限二进制展开不能由浮点数被精确地表示可表达的。
换句话说,不能表示不能以分母为2的幂表示的整数形式的每个数字。 这包括1 / 5、1 / 10和597/20(即29.85)。
由于无法精确表示这些数字,因此涉及这些数字的运算结果取决于运算的顺序以及舍入和截断误差,因此.1 + .1 + .1
与.3
,并且与您的计算类似。
就像@Shakti Singh所说的那样,浮点数没有被精确地存储,所以我猜$ f实际上被存储为29.849999999999999 ...之类的东西,因此不完全等于29.85。
另外,此代码行看起来是错误的:
echo 'var_dump($f == \'29.85\') = ';
var_dump($f == 29.85);
我认为应该是:
echo 'var_dump($f == \'29.85\') = ';
var_dump($f == '29.85');
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.