强制 PHP 整数溢出

8

我们有一些整数算术运算,由于历史原因,它必须在PHP上与一些静态类型语言相同。自从我们上次升级PHP以来,整数溢出的行为已经发生了变化。基本上,我们使用以下公式:

function f($x1, $x2, $x3, $x4)
{
   return (($x1 + $x2) ^ $x3) + $x4;
}

然而,即使进行了转换:

function f($x1, $x2, $x3, $x4)
{
   return intval(intval(intval($x1 + $x2) ^ $x3) + $x4);
}

我仍然得到完全错误的数字...
例如,对于$x1 = -1580033017,$x2 = -2072974554,$x3 = -1170476976和$x4 = -1007518822,我在PHP中得到-30512150,在C#中得到1617621783。
仅仅将$x1和$x2相加并不能得到正确的答案:
在C#中,我得到
(-1580033017 + -2072974554) = 641959725

在PHP中:

intval(intval(-1580033017) + intval(-2072974554)) = -2147483648

这与以下代码相同:

intval(-1580033017 + -2072974554) = -2147483648

我不介意编写一个"IntegerOverflowAdd"函数或其他什么,但我无法弄清楚(-1580033017 + -2072974554)等于641959725。 (我确实认识它是-2147483648 +(2 * 2 ^ 31),但是-2147483648 + 2 ^ 31是-1505523923,大于Int.Min,那么为什么要加2 * 2 ^ 31而不是2 ^ 31?)
任何帮助将不胜感激...
6个回答

14

我解决了这个问题,并深入了解了PHP的许多特性(至少是在处理整数溢出方面)。

1)它完全取决于机器运行的平台,PHP的版本,是否在运行Suhosin Hardened PHP以及编译为32位或64位。6台机器表现出了我预期的方式(实际上是错误的,至少根据文档是错误的),3台机器的行为我仍然无法解释,而另外3台机器的行为符合intval命令在文档中所述的方式。

2)Intval应该在int > PHP_INT_MAX时返回PHP_INT_MAX(而不是int& 0xffffffff),但这只发生在某些版本的PHP4和PHP5上。不同版本的PHP在int > PHP_INT_MAX时会返回不同的值。

3)以下代码可以返回3种不同的结果(见1):

<?php
echo "Php max int: ".PHP_INT_MAX."\n";
echo "The Val: ".(-1580033017 + -2072974554)."\n";
echo "Intval of the val: ".intval(-3653007571)."\n";
echo "And 0xffffffff of the val: ".(-3653007571 & 0xffffffff)."\n";
?>

它可以返回(对于Intval似乎是正确的,但对于& 0xffffff是错误的)

Php max int: 2147483647
The Val: -3653007571
Intval of the val: -2147483648
And of the val: -2147483648

它可以返回(这与 PHP 文档中 intval 的说明相矛盾):

Php max int: 2147483647
The Val: -3653007571
Intval of the val: -641959725
And of the val: -641959725

在64位计算机上,它返回(正确的是):

Php max int: 2147483647
The Val: -3653007571
Intval of the val: -3653007571
And of the val: -641959725

解决方案

无论如何,我需要一个可以在所有这些平台上工作的解决方案,并且不依赖于特定版本的PHP编译与特定的Max int。因此,我想出了以下跨PHP的thirtyTwoBitIntval函数:

function thirtyTwoBitIntval($value)
{
    if ($value < -2147483648)
    {
        return -(-($value) & 0xffffffff);
    }
    elseif ($value > 2147483647)
    {
        return ($value & 0xffffffff);
    }
    return $value;
}

评论

我认为PHP的设计者应该说一个Int是一个32位的Int,不管它是在32位、64位还是128位的机器上运行(例如DotNet CLR),而不是根据PHP编译器的位数随机将其升级为浮点数。


2
我认为你的意思是 PHP_INT_MAX,而不是 PHP_MAX_INT。 - scotts

11
如果您想要在32位和64位平台上都有100%工作的32位intval解决方案,那么我建议您使用以下解决方案:
function intval32bits($value)
{
    $value = ($value & 0xFFFFFFFF);

    if ($value & 0x80000000)
        $value = -((~$value & 0xFFFFFFFF) + 1);

    return $value;
}

$value = -((~$value & 0xFFFFFFFF) + 1); 这部分的意义是什么? - tonix

3

PHP内部大多数数字使用“整数”类型。但是,这些只能走得那么远:如果将一个大整数加上另一个大整数,PHP将看到结果太大而无法适应普通整数,并将其分配给浮点数。然而,浮点数(floats)本身也只能达到一定高度,在十六位数字左右的位置,PHP将完全失去意义。

有一个选项可以使用任意精度数学,它支持表示为字符串的任何大小和精度的数字。在此处了解更多信息:http://us2.php.net/bc


2
我认为这可能与 PHP 中的整数是无符号 32 位有关,而在 C# 中它们默认为有符号 32 位。
你正在使用超出正常 31-32 位范围的数字。
请参阅 PHP 手册中的附加文档:

http://www.php.net/manual/en/language.types.integer.php

一个整数的大小取决于平台,通常情况下最大值约为20亿(即32位有符号整数)。PHP不支持无符号整数。可以使用常量PHP_INT_SIZE确定整数大小,自PHP 4.4.0和PHP 5.0.5以来可以使用常量PHP_INT_MAX确定最大值。

2

这会有效吗?

echo (-1580033017 + -2072974554) & 0xffffffff

总体来说,您可以这样做(请原谅任何语法错误,我已经很久没有接触PHP了):

function s32add($a, $b) {
    return ($a + $b) & 0xffffffff;
}

1

检查你的 PHP 版本号 - 我相信不同版本的 PHP 可能会对长整型支持产生不同结果。我相信在 PHP 5 的某个版本中存在长整型的一个 bug。

在 PHP 5.2.0 版本中 - 使用你上面提供的确切函数得到的答案与 C# 中的完全相同。

1617621783,

使用你上面所提供的精确函数。

你可以使用 phpinfo() 命令轻松找到你的 PHP 版本号。

$x1 = -1580033017; 
$x2 = -2072974554; 
$x3 = -1170476976 ; 
$x4 = -1007518822;
echo f($x1, $x2, $x3, $x4);

function f($x1, $x2, $x3, $x4)
{
   return intval(intval(intval($x1 + $x2) ^ $x3) + $x4);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接