跨越PHP中的整数限制

跨越PHP中的整数限制,php,math,integer,integer-overflow,Php,Math,Integer,Integer Overflow,有人能解释一下在最后两个案例中发生了什么吗 $x=PHP_INT_MAX; var_dump($x); // int(9223372036854775807) no problem var_dump($x+1); // float(9.2233720368548E+18) value is cast to float still no problem var_dump($x+1-1); // float(9.22337

有人能解释一下在最后两个案例中发生了什么吗

$x=PHP_INT_MAX;
var_dump($x);            // int(9223372036854775807)        no problem
var_dump($x+1);          // float(9.2233720368548E+18)      value is cast to float still no problem
var_dump($x+1-1);        // float(9.2233720368548E+18)      still okay
var_dump((int)($x+1-1)); // int(-9223372036854775808)       negative value?!!
var_dump($x+1-$x);       // float(0)                        zero?!!!!!!!!!!!
根据报告:

  • PHP_INT_MAX是最大整数大小
  • 如果PHP遇到一个超出整数类型边界的数字,它将被解释为浮点。还有,一个操作 结果将返回超出整数类型边界的数字 而是一个浮子
所以前两次转储是可以的。第三次转储给出了预期结果,但为什么最后两次转储会给出负值和零?

关于第四次测试: 整数是有符号的。在内部,它们是以这样的方式存储的:当您添加一个值时,该值会包装并变成最小的负整数。如果是4位的有符号半字节,则如下所示:

0111 (=7) + 0001 (=1) = 1000 (= -7)
这部分解释了一个数字如何突然变成一个非常大的负数

但是你会期望x+1-1是x,但这可能是一个有效数字的问题。PHP只存储14个有效数字,这不足以准确地存储像
9223372036854775807这样的数字而不丢失信息

我将使用第5个测试进行解释:

浮点可以包含比整数大得多的数字,但有效位数的数量有限。这就是为什么它看起来像
9.2233720368548E+18
而不是
9223372036854800000
9223372036854775808

由于像
1
这样的低位数字在值的有效位之外,我认为
9.2233720368548E+18+1
仍然是
9.2233720368548E+18
。这也意味着
9.2233720368548E+18+1-9.2233720368548E+18
0
,因为
+1
没有任何效果

我认为这实际上是对这两种现象的解释

奇怪的是,您通常不必知道这些实现细节中的任何一个,特别是在PHP这样的脚本语言中,但在这样的测试中,它们可能会出现,有时会导致奇怪的行为


此外,你可能想阅读。它很好地解释了使用浮点数可能出现的其他舍入错误,它将使您更深入地了解浮点数在一般情况下以及在PHP中的具体工作方式。

这与计算机存储数字的方式有关。在本例中,是带符号的数字,这意味着它们既可以是负数也可以是正数

为了方便起见,我将使用一个8位示例。计算机可以存储从
-128
127
的数字。
-128
表示为
1000 0000
127
表示为
0111111
。第一位表示数字是负数还是正数。每个位都值
2^n
,WARE
n
是从右到左的位置。对于127,它是
2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^7=127。
负值则相反

var_dump($x);            // int(9223372036854775807)        no problem
没有问题,因为我们只是将其设置为最大值

var_dump($x+1);          // float(9.2233720368548E+18)      value is cast to float still no problem
var_dump($x+1-1);        // float(9.2233720368548E+18)      still okay
这里有一些问题,+1和+1-1等于相同的东西。他们不应该这样做,但问题确实发生在这里,有点微妙

var_dump((int)($x+1-1)); // int(-9223372036854775808)       negative value?!!
在这种情况下,
0111(…)
+1等于
1000(…)
,这是绝对值,请注意数字末尾的8,而正值为7设置了CPU中的溢出标志。

var_dump($x+1-$x);       // float(0)                        zero?!!!!!!!!!!!
$x+1强制强制强制强制转换为浮点,然后减去相同的值(因为浮点数字存在舍入错误),$x+1与$x相同,因此$x-$x=0


希望这能澄清一些问题。

两种不同的东西结合在一起:浮点舍入和整数溢出

var_dump($x+1-$x);       // float(0)                        zero?!!!!!!!!!!!
这是因为算术是不精确的(它不可能精确,因为实数可能是无限的)
$x
$x+1
靠得很近,所以它们四舍五入到同一个浮点值,所以
浮点($x+1)==浮点($x)
。现在您知道为什么
$x+1-$x==0

结合整数溢出,可以得到以下结果:

var_dump((int)($x+1-1)); // int(-9223372036854775808)       negative value?!!
由于上述原因,
$x+1-1==float(9223372036854775808)
。当将其转换为
int
时,它将变为负值


9223372036854775808==2^63
,它在64位有符号整数中变成了
-2^63

,这与计算机存储数字的方式有关。特别是负数和浮点数。您的整数溢出解释是正确的,但您没有正确解释
var_dump($x+1-$x);//浮动(0)
。在这种情况下,浮点数不会溢出。