在PHP

时间:2018-07-02 17:19:29

标签: php floating-point

我想要最简单的方法来计算:

$example = 90000000000009132 - 1;
echo sprintf('%.0f',  $example);

我惊讶地得到:

           90000000000009136

我的问题是-如何针对此类操作在php中获得正确答案您能给我正确的密码吗?

p.s。在网上搜索后,我找到了解释,说“这不是错误”以及有关float的一些神话等等。。。嗯,我不会开始证明为什么我认为这是错误的。 确实是一个明显的错误,并且我们不在乎在PC后台会发生什么,但显而易见的是,我们在减去{{ {} {1}}中的1}},所以肯定是错误的! (但是请不要在这个问题上与我争论,因为我的问题是不同的-上面已经清楚地写过。)

ps。是这个问题异常消失之后,我在引荐链接中找不到真正的解决方案。他们都没有工作,至少在我的托管上。因此,所有声称是解决方案的所有“答案”,在某些主机上似乎都没有用。

1 个答案:

答案 0 :(得分:3)

当然,PHP浮点数精度问题是SO上经常讨论的话题,但我仍然看不到为什么这个问题尽管有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语句先转换为浮点,然后转换为字符串)

那我们为什么会有这个奇怪的结果?让我们做一些测试:

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)));

猜猜结果是什么

  

int(16)

哇。这意味着在PHP中,浮点数基本上可以精确地降到16位(如果您超过了这个神奇的数字,它将变得不精确。)

那么您的计算发生了什么?

  

当您将16位数字超出一位时,该数字将四舍五入到最后16位。

有关PHP中浮点精度的更多信息,可以在关于Stackoverflow的数十个问题中找到,当然也可以在PHP手册中找到。

要回答如何使代码正常工作:

  

如果您希望数字精确,请不要转换为浮点数。例如,对于整数,应该可以进行更高的计算。

     

如果超过16位数字,则简单的算术运算(如-1或+1)也会失败(至少在当前的PHP版本中如此)。