【问题标题】:PHP Bit shift Unexpected Results/Overflow?PHP位移意外结果/溢出?
【发布时间】:2018-09-28 00:00:48
【问题描述】:

在我正在编写的一个小脚本中,我使用以下函数:

function ROTR( $x, $t ){
    return( $x >> $t ) | ( $x << ( 32 - $t ) );
}

function Σ0( $x ){
    echo("SIG INPUT: " . $x . "<br>" );

    $s0 = ROTR( $x, 2 );
    $s1 = ROTR( $x, 13 );
    $s2 = ROTR( $x, 22 );

    echo( "SIGMA0 2: " . $s0 . "<br>" );
    echo( "SIGMA0 13: " . $s1 . "<br>" );
    echo( "SIGMA0 22: " . $s2 . "<br>" );

    return ( $s0 ^ $s1 ^ $s2 );
}

这些都在第 5 页和第 10 页的this document 中定义

我应用以下内容并得到这些结果:

Σ0( 1779033703 )
> SIG INPUT: 1779033703
> SIGMA0 2: -628983399
> SIGMA0 13: 859525199
> SIGMA0 22: 664378792

输入和最后两个(SIGMA0 13SIGMA0 22)完全正常,但是 ROTR( 1779033703, 2 ) 移位似乎溢出了 2^31-1 有符号整数限制。

期望的值3665983897,我可以在VB.Net中获得:

' THIS SCRIPT HAS INTEGER OVERFLOW CHECKS DISABLED!
Function RotRight(x As UInt32, count As UInt32) As UInt32
    Dim t5 = x \ (2 ^ count)
    Dim t6 = x * (2 ^ (32 - count))

    Return (t5 Or t6)
End Function

Function EP0(x As UInt32) As UInt32
    Dim t7 = RotRight(x, 2)
    Dim t8 = RotRight(x, 13)
    Dim t9 = RotRight(x, 22)

    Return (t7 Xor t8 Xor t9)
End Function

>

SIG INPUT: 1779033703
SIGMA0 2: 3665983897
SIGMA0 13: 859525199
SIGMA0 22: 664378792

我读到有几种方法可以通过 gmp 库将整数视为字符串来规避溢出问题,但我还没有找到可行的方法。

到目前为止,我已经尝试将整数转换为字符串,然后使用gmp_init( string ) 将字符串转换为 GMP 数字,但该库似乎不支持位移。

PS:我使用的是 32 位构建的 PHP(我在 Windows 上使用尚不支持 64 位的 XAMPP)

【问题讨论】:

  • 您能否从您的 vb.net 代码 t7 = RotRight(x, 2) t8 = RotRight(x, 13) t9 = RotRight(x, 22) 中附加每个输出的输出
  • @RinsadAhmed 对不起,我忘了包括那个。我已经用这些细节编辑了这个问题。谢谢:)
  • 这个运算符\是什么?
  • @RinsadAhmed "将一个操作数除以另一个并返回一个整数结果" tutorialspoint.com/vb.net/vb.net_operators.htm
  • “但该库似乎不支持位移”,那么您将需要乘以或除以 2 的幂。

标签: php bit-shift


【解决方案1】:

因为您安装的是 32 位 PHP,并且 PHP 不支持无符号整数,所以您需要使用像 GMP 这样的库来解决您的问题。不幸的是,GMP 没有位移算术函数,但是您可以使用除法和乘法来模拟它们:

function ROTR( $x, $t ){
    return gmp_and(gmp_or(gmp_div($x, 1 << $t), gmp_mul($x, 1 << (32 - $t))), "4294967295");
}

function Σ0( $x ){
    echo("SIG INPUT: " . $x . "\n" );

    $s0 = ROTR( $x, 2 );
    $s1 = ROTR( $x, 13 );
    $s2 = ROTR( $x, 22 );

    echo( "SIGMA0 2: " . gmp_strval($s0, 10) . "\n" );
    echo( "SIGMA0 13: " . gmp_strval($s1, 10) . "\n" );
    echo( "SIGMA0 22: " . gmp_strval($s2, 10) . "\n" );

    return ( gmp_xor($s0, gmp_xor($s1, $s2)) );
}

Σ0( 1779033703 );

请注意,由于 GMP 是任意精度,因此您需要将 ROTR 的输出通过 and'ing(1 &lt;&lt; 32) - 1 逐位限制为 32 位。

输出:

SIG INPUT: 1779033703
SIGMA0 2: 3665983897
SIGMA0 13: 859525199
SIGMA0 22: 664378792

这是3v4l上的演示

【讨论】:

  • 也许我在这里仍然做错了:(我可以确认$x 是作为integer 传递的。$g$xΣ0 时是相同的数值运行(gmp_init 返回一个对象,但整数部分是相同的)。这一次(我已经为两者复制了完全相同的函数)我得到SIG INPUT: 1779033703SIGMA0 2: 1910222893661452697
  • 1910222893661452697 是使用 64 位算术时得到的值。 ROTR 中的屏蔽(外部 gmp_and(1 &lt;&lt; 32) - 1 应该使这不可能。请注意,分配给 $g 是不必要的,我已将其编辑掉。
  • 我在34vl.org添加了一个演示链接
  • 我现在确信我的 PHP 安装有问题,或者可能是 x86 正在改变我的结果。我发现的几乎所有支持GMP(包括您提供的示例)的在线IDE 都可以工作。但是,在我的本地 x86 PHP 服务器上,我得到的结果好像该数字没有被屏蔽。确实很奇怪。无论如何,我非常感谢您的帮助!
  • 对不起,我是个白痴。因为你的系统是 32 位的,所以它不能代表(1&lt;&lt;32)。我用字符串值(1&lt;&lt;32)-14294967295 编辑了代码,这应该适合你。我也更新了演示链接。
猜你喜欢
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 1970-01-01
  • 2012-04-09
  • 1970-01-01
  • 2013-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多