当然,PHP浮点精度的问题经常是一个话题,但我仍然不明白为什么这个问题会被否决18票,因为这个问题被非常精确地问到具体浮点数的具体问题。我以一种人人都能清楚理解问题所在的方式解决了这个问题(不仅仅是那些不精确的推理)。
我们来看看下面的结果:
$example = 90000000000009132 - 1;
var_dump(PHP_INT_MAX);
var_dump($example);
var_dump(PHP_FLOAT_MAX);
var_dump(sprintf('%.0g', $example));
var_dump(sprintf('%.0f', $example));
结果:
int(9223372036854775807)
int(90000000000009131)
float(1.7976931348623E+308)
string(7) "9.0e+16"
string(17) "90000000000009136"
这意味着:
-
您不超过最大整数大小
算术运算失败
-
用整数计算的结果是正确的
-
您没有超过php_float_max(需要php 7.2),但其大小取决于您的环境。
-
计算的浮点表示法意外错误(因为printf语句转换为float,然后转换为string)
为什么会有这样奇怪的结果呢?让我们做一些测试:
var_dump(sprintf('%.0f', 90000000000009051));
var_dump(sprintf('%.0f', 90000000000009101));
var_dump(sprintf('%.0f', 90000000000009105));
var_dump(sprintf('%.0f', 90000000000009114));
var_dump(sprintf('%.0f', 90000000000009121));
var_dump(sprintf('%.0f', 90000000000009124));
var_dump(sprintf('%.0f', 90000000000009128));
var_dump(sprintf('%.0f', 90000000000009130));
var_dump(sprintf('%.0f', 90000000000009131));
var_dump(sprintf('%.0f', 90000000000009138));
var_dump(sprintf('%.0f', 90000000000009142));
var_dump(sprintf('%.0f', 90000000000009177));
结果是:
string(17) "90000000000009056"
string(17) "90000000000009104"
string(17) "90000000000009104"
string(17) "90000000000009120"
string(17) "90000000000009120"
string(17) "90000000000009120"
string(17) "90000000000009120"
string(17) "90000000000009136"
string(17) "90000000000009136"
string(17) "90000000000009136"
string(17) "90000000000009136"
string(17) "90000000000009184"
看看那些数字。结尾的数字总是可以16分。信息技术中的16是什么意思?一个字节。十六进制。它告诉我们什么?
您可能已经超过了浮动基本上是精确的限制(在算术运算中不一定如此)。
如果我们把这些数字减少一个零到16位呢?
var_dump(sprintf('%.0f', 9000000000009051));
var_dump(sprintf('%.0f', 9000000000009101));
var_dump(sprintf('%.0f', 9000000000009124));
var_dump(sprintf('%.0f', 9000000000009138));
var_dump(sprintf('%.0f', 9000000000009142));
var_dump(sprintf('%.0f', 9000000000009177));
…
string(16) "9000000000009051"
string(16) "9000000000009101"
string(16) "9000000000009124"
string(16) "9000000000009138"
string(16) "9000000000009142"
string(16) "9000000000009177"
输出总是正确的。好。如果我们把它提高一个零位数呢?
var_dump(sprintf('%.0f', 900000000000009051));
var_dump(sprintf('%.0f', 900000000000009101));
var_dump(sprintf('%.0f', 900000000000009124));
var_dump(sprintf('%.0f', 900000000000009138));
var_dump(sprintf('%.0f', 900000000000009142));
var_dump(sprintf('%.0f', 900000000000009177));
…
string(18) "900000000000009088"
string(18) "900000000000009088"
string(18) "900000000000009088"
string(18) "900000000000009088"
string(18) "900000000000009088"
string(18) "900000000000009216"
有趣的是,这又是16个因素——更不精确。
现在让我们来解开这个谜:
var_dump(strlen(sprintf('%.0f', 9000000000009051)));
猜猜结果是什么:
利息(16)
真的。这意味着在PHP中浮点数基本上可以精确地降到16位。(
如果你超过这个神奇的数字,它就会变得不精确。
)
那么,你的计算结果是怎样的呢?
它在结尾处被四舍五入到下一个16位,因为您超过了16位数字1。
关于php中浮点精度的更多信息,可以在stackoverflow的许多问题中找到,当然也可以在php手册中找到。
要回答如何使代码正确工作:
如果你希望你的数字是精确的,不要转换成浮点数。例如,对于整数,应该可以进行更高的计算。
如果超过16位数,那么简单的算术(如-1或+1)也会失败(至少在当前版本的PHP中是这样)。