强制 PHP 整数溢出

发布于 2024-07-08 10:33:30 字数 1050 浏览 9 评论 0原文

我们有一些整数算术,由于历史原因,它们在 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 加在一起,我无法得到正确的答案:

In C# I get

(-1580033017 + -2072974554) = 641959725

In PHP:

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

这与:

intval(-1580033017 + -2072974554) = -2147483648

I don't介意编写“IntegerOverflowAdd”函数或其他东西,但我不太明白(-1580033017 + -2072974554) 等于 641959725。(我确实认识到它是 -2147483648 + (2 * 2^31),但 -2147483648 + 2^31 是 -1505523923,它大于 Int.Min 那么为什么这样做您添加 2*2^31 而不是 2^31?)

任何帮助将不胜感激......

We have some integer arithmetic which for historical reasons has to work the same on PHP as it does in a few statically typed languages. Since we last upgraded PHP the behavior for overflowing integers has changed. Basically we are using following formula:

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

However, even with conversions:

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

I am still ending up with the completely wrong number...

For example, with $x1 = -1580033017, $x2 = -2072974554, $x3 = -1170476976) and $x4 = -1007518822, I end up with -30512150 in PHP and 1617621783 in C#.

Just adding together $x1 and $x2 I cannot get the right answer:

In C# I get

(-1580033017 + -2072974554) = 641959725

In PHP:

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

which is the same as:

intval(-1580033017 + -2072974554) = -2147483648

I don't mind writing a "IntegerOverflowAdd" function or something, but I can't quite figure out how (-1580033017 + -2072974554) equals 641959725. (I do recognize that it is -2147483648 + (2 * 2^31), but -2147483648 + 2^31 is -1505523923 which is greater than Int.Min so why is do you add 2*2^31 and not 2^31?)

Any help would be appreciated...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦境 2024-07-15 10:33:30

所以我解决了这个问题,并发现了很多关于 PHP 的知识(至少是它处理整数溢出的方式)。

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

2) 当 int > 时,Intval 应该返回 PHP_INT_MAX PHP_INT_MAX(不是 int & 0xffffffff),但这仅发生在 PHP4 和 PHP5 的某些版本上。 当 int > 时,不同版本的 PHP 返回不同的值 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

并且它可以返回(这与 intval 的 PHP 文档相矛盾):

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

And on 64它返回的位机(这是正确的):

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

解决方案

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

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

Comment

我确实认为PHP的设计者应该说Int是32位Int,无论它是在32位还是64位上运行128 位机器(例如 DotNet CLR),并且不会根据 PHP 编译器使用的位数随机将其上转换为浮点数。

So I solved the problem, and discovered a lot about PHP (at least in the way it handles Integer overflow).

1) It completely depended on a cross between which platform the machine was running on, which version of PHP, whether or not it had Suhosin Hardened PHP running, and how many bits it was compiled for (32 or 64). 6 machines behaved the way I expected (which was actually wrong, at least wrong according to their documentation) and 3 machines behaved in a way I still can't explain, and 3 machines behaved according to what the intval command says it does in the documentation.

2) Intval is supposed to return PHP_INT_MAX when int > PHP_INT_MAX (not int & 0xffffffff), but this only happens on some versions of PHP4 and PHP5. Different versions of PHP return different values when int > PHP_INT_MAX.

3) The following code can return 3 different results (see 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";
?>

It can return (which appears to be right for Intval but wrong for & 0xffffff)

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

And it can return (which contradicts the PHP documentation for intval):

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

And on 64 Bit machines it returns (which is correct):

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

Solution

Anyhow, I needed a solution that would work on all these platforms, and not be dependent upon quirks of a particular version of PHP compiled with a particular Max int. Thus I cam up with the following cross-PHP thirtyTwoBitIntval function:

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

Comment

I do think the designers of PHP should have said an Int is a 32 Bit Int no matter whether it is running on a 32 or 64 or 128 bit machine (like the DotNet CLR for example), and didn't randomly upconvert it to a float depending on the number of Bits that PHP is compiler under.

醉南桥 2024-07-15 10:33:30

如果您想在 32 位和 64 位平台上获得 32 位 intval 100% 工作的解决方案,那么我建议您使用以下解决方案:

function intval32bits($value)
{
    $value = ($value & 0xFFFFFFFF);

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

    return $value;
}

If you want to have 100% working solution for 32-bit intval both on 32 and 64 bit platforms, then I suggest you to use the following solution:

function intval32bits($value)
{
    $value = ($value & 0xFFFFFFFF);

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

    return $value;
}
無心 2024-07-15 10:33:30

在内部,PHP 对大多数数字使用“整数”类型。 然而,这些仅限于此:如果将一个大整数与一个大整数相加,PHP 会发现结果太大而无法放入普通整数中,并将其分配给浮点数。 然而,浮点数(float)本身只能达到这么高,并且在 16 位数字附近有一个点,PHP 将完全失去控制。

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

Internally, PHP uses an "integer" type for most numbers. However, these only go so far: if you add a large integer to a large integer, PHP will see that the result is too big to fit into a normal integer and will assign it to a floating-point number. Floating-point numbers (floats) themselves only go so high, however, and there's a point around the sixteen-digit mark where PHP will just lose the plot entirely.

There is an option to use arbitrary-precision mathematics which supports numbers of any size and precision, represented as strings. See more here: https://www.php.net/bc

别念他 2024-07-15 10:33:30

我认为这可能与 PHP 中的整数是无符号 32 位有关,因为在 C# 中它们默认是有符号的 32 位。

您正在使用正常 31-32 位范围边缘的数字。

请参阅 PHP 手册中的其他文档:

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

整数的大小取决于平台,但通常的最大值约为 20 亿(即 32 位有符号)。 PHP 不支持无符号整数。 从 PHP 4.4.0 和 PHP 5.0.5 开始,可以使用常量 PHP_INT_SIZE 确定整数大小,并使用常量 PHP_INT_MAX 确定最大值。

I think it may have to do with the integer in PHP being unsigned 32 bits as in C# they are signed 32 bits by default.

You are playing with numbers on the edge of the normal 31-32 bits range.

Please see additional documentation in the PHP manual:

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

The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed). PHP does not support unsigned integers. Integer size can be determined using the constant PHP_INT_SIZE, and maximum value using the constant PHP_INT_MAX since PHP 4.4.0 and PHP 5.0.5.

坦然微笑 2024-07-15 10:33:30

这行得通吗?

echo (-1580033017 + -2072974554) & 0xffffffff

概括地说,你可以这样做(请原谅任何语法错误,我已经很长时间没有接触 PHP 了):

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

Will this work?

echo (-1580033017 + -2072974554) & 0xffffffff

To generalise, you could do (pardon any syntax errors, I've not touched PHP for a long time):

function s32add($a, $b) {
    return ($a + $b) & 0xffffffff;
}
娇柔作态 2024-07-15 10:33:30

检查您的 PHP 版本号 - 我相信不同版本的 PHP 可能对长整数的支持不同,您可能会得到不同的结果。 我相信 PHP 5 的最后一个版本中存在一个长整数错误。

中得到的答案完全相同

在 PHP 5.2.0 版本中 - 答案与您在 C# 1617621783

,利用了上面的函数。

您可以使用 phpinfo() 命令轻松查找您的版本号。

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

Check your version number of PHP - I believe it is possible you will get different results with different versions of PHP that may have differing support for long integers. I believe that there was a bug with long integers in at last one of the PHP 5 versions.

In version PHP 5.2.0 - the answer is EXACTLY the same as you got in C#

1617621783,

utilizing the exact function you have above.

You can use the phpinfo() command to find your version number, easily.

$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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文