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

Intval() strange behavior - losing accuracy

我偶然发现了 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'] 总是整型)

有什么方法可以防止这种奇怪的效果以及为什么会发生这种情况?

提前致谢。

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

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

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

打印时您看不到它,因为它被 PHP 的魔术浮点数到字符串转换舍入了。但是如果你使用 intval,这将显示。

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

8.29 十进制 = 8.28999996185302734375 二进制(或 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 => 1.23 和 1.235 => 1.24 等情况。

如果你想降低结果 (1.235 => 1.23) 但仍然保持 8.28999996185302734375 => 8.29 那么你需要

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