Intval()奇怪的行为 - 失去准确性

时间:2017-02-21 16:19:52

标签: php precision

我偶然发现了一个与intval()的奇怪问题 - 我需要在小数点后显示2个数字而不对结果进行舍入。所以我用这种方式使用intval():

$result = ($data['a'] / $data['b'])*100;
$data['x'] = intval(($result)*100)/100;

这种方式对几乎所有结果都可以正常工作,但它显示了一些奇怪的行为 - 小数点后的第二个数字因某些结果而下降1。例如:

$data['a'] = 16.58; (float)
$data['b'] = 200; (int)
$result = 8.29;
$data['x'] = 8.28;

($ data [' a']始终为浮点数,[' b']始终为int)

有没有办法防止这种奇怪的效果及其发生的原因?

提前致谢。

2 个答案:

答案 0 :(得分:0)

number_format而不是intval呢?

$var = number_format(12.3456, 2, '.', ''); // 12.34

修改

$var = floor(12.3456 * 100) / 100; // 12.34

编辑2

它是" hack",但至少它有效。

$num = 8.294;
$dotPos = strpos((string) $num, '.');
$num = substr((string) $num, 0, $dotPos + 3); // 3 and not 2 because index starts from 0
$num = floatval($num);

echo $num; // 8.29

答案 1 :(得分:0)

好的,我看到这里发生了什么,但很难解释。

浮点数在内部表示为带指数的二进制数。 虽然8.29是十进制的完全准确的指数数,但它不是二进制格式。摩擦很小,所以它实际上比8.29小得多。

打印时没有看到它,因为它被PHP的魔术浮动转换为字符串转换。但是如果你使用intval,这将显示。

您可以在此处查看:http://www.exploringbinary.com/floating-point-converter/

8.29 decimal = 8.28999996185302734375 in binary(或1000.01001010001111010111)

<强>更新

以干净的方式获得你想要的结果:

$result = ($data['a'] / $data['b'])*100;  //8.28999996185302734375 internally or "8.29" as string
$result = round($result * 100)/100;  //rounded 828.999... rounded to 829 devided by 100 to 8.29

更新2

请注意,由于上面提到的二元浮点数限制,结果再次表示为8.28999996185302734375。所以在这种特殊情况下甚至不需要整个操作。但它同时会转换像1.234 =&gt;这样的情况。 1.23和1.235 =&gt; 1.24表示为字符串。

如果你想对结果进行平分(1.235 =&gt; 1.23)但仍然保持8.28999996185302734375 =&gt; 8.29然后你需要

$result = ($data['a'] / $data['b'])*100;
$result = floor(round($result1 * 1000)/10)/100;