【问题标题】:Unsigned Right Shift / Zero-fill Right Shift / >>> in PHP (Java/JavaScript equivalent)PHP 中的无符号右移/零填充右移/>>>(Java/JavaScript 等效项)
【发布时间】:2017-04-29 07:05:27
【问题描述】:

在将此标记为重复之前,请阅读以下内容并检查 my code * my updated code!

所以我的问题是,我必须实现 Java/JavaScript '>>>'(无符号右移 / 零填充右移),但我不能让它以完全相同的方式工作。

我选择了我在 SO 和 Web 上找到的 11 个最有前途的实现(链接在代码中作为 cmets 添加)并添加了一些测试用例。不幸的是,NONE 函数向所有测试返回了与 Java/JS 相同的响应。 (也许其中一些只适用于 32 位系统)

Live Code + JS+PHP 结果演示(点击运行):
http://phpfiddle.org/main/code/bcv7-bs2q *
http://phpfiddle.org/main/code/dpkw-rxfe

最接近的函数是:

// http://stackoverflow.com/a/27263298
function shr9($a,$b) { 
    if($a>=0) return $a>>$b;
    if($b==0) return (($a>>1)&0x7fffffff)*2+(($a>>$b)&1);
    return ((~$a)>>$b)^(0x7fffffff>>($b-1)); 
}

// http://stackoverflow.com/a/25467712
function shr11($a, $b) { 
    if ($b > 32 || $b < -32) {
        $m = (int)($b/32);
        $b = $b-($m*32);
    }

    if ($b < 0)
        $b = 32 + $b;

    if ($a < 0) 
    { 
        $a = ($a >> 1); 
        $a &= 2147483647; 
        $a |= 0x40000000; 
        $a = ($a >> ($b - 1)); 
    } else { 
        $a = ($a >> $b); 
    } 
    return $a; 
}

不幸的是 shr9 在 (-10 >>> -3) 和 * (32 >> 32) 上失败,但它是 唯一 通过 (-3 >> > 0);并且 shr11 在 (-3 >>> 0) 和 (32 >>> 32) 上失败。

测试用例:

         0 >>> 3    == 0 
         3 >>> 0    == 3 
         0 >>> -3   == 0 
        -3 >>> 0    == 4294967293 (in JS); -3 (in Java)  
        10 >>> 3    == 1 
        10 >>> -3   == 0 
       -10 >>> 3    == 536870910 
       -10 >>> -3   == 7 
-672461345 >>> 25   == 107 
        32 >>> 32   == 32 
       128 >>> 128  == 128 

编辑:我发现 -3 &gt;&gt;&gt; 0 仅在 JavaScript 中等于 4294967293(为什么?),但在 Java 中,它等于 -3。不幸的是,这并没有改变我仍然无法让任何函数通过所有测试的事实。


*大更新:

自 PHP 7 起,负数位移被视为无效并导致:“致命错误:未捕获的 ArithmeticError:负数位移”。据此,我认为我们不必通过这些测试,所以我更新了问题和代码。

【问题讨论】:

    标签: javascript java php bit-manipulation bit-shift


    【解决方案1】:

    从问题(“shr9”和“shr11”)中查看两个函数并合并/调整好的部分后,我终于找到了解决方案。所有测试都通过了(我什至在演示中添加了更多测试),它也适用于负数班次。

    [Live Demo]

    function unsignedRightShift($a, $b) {
        if ($b >= 32 || $b < -32) {
            $m = (int)($b/32);
            $b = $b-($m*32);
        }
    
        if ($b < 0) {
            $b = 32 + $b;
        }
    
        if ($b == 0) {
            return (($a>>1)&0x7fffffff)*2+(($a>>$b)&1);
        }
    
        if ($a < 0) 
        { 
            $a = ($a >> 1); 
            $a &= 0x7fffffff; 
            $a |= 0x40000000; 
            $a = ($a >> ($b - 1)); 
        } else { 
            $a = ($a >> $b); 
        } 
        return $a; 
    }
    

    这段代码不仅准确,而且速度也很快。
    基准测试结果:100000 次循环:0.25 秒
    基准测试:http://phpfiddle.org/main/code/mj68-1s7e

    【讨论】:

      【解决方案2】:

      由于我真的没有想法,我克隆了 Chromium V8 引擎和 Mozilla Central 存储库来获取 SpiderMonkey。我开始搜索 JS >>> 运算符,最后在 Mozilla 的代码中,我找到了一个将近 20 年的文件(从 1997 年开始),js/src/tests/ecma/Expressions/11.7.3.js,其中包含用于测试 “零填充的无运算符代码按位右移操作”。用 PHP 重写后,这段代码通过了所有测试。

      [LiveDemo]

      <?php
      
      function ToInteger( $n ) {
        $sign = ( $n < 0 ) ? -1 : 1;
      
        if ( $n != $n ) {
          return 0;
        }
        if ( abs( $n ) == 0 || abs( $n ) == INF ) {
          return $n;
        }
        return intval( $sign * floor(abs($n)) );
      }
      
      function ToInt32( $n ) {
        $sign = ( $n < 0 ) ? -1 : 1;
      
        if ( abs( $n ) == 0 || abs( $n ) == INF) {
          return 0;
        }
      
        $n = ($sign * floor( abs($n) )) % pow(2,32);
        $n = ( $n >= pow(2,31) ) ? $n - pow(2,32) : $n;
      
        return ( $n );
      }
      
      function ToUint32( $n ) {
        $sign = ( $n < 0 ) ? -1 : 1;
      
        if ( abs( $n ) == 0 || abs( $n ) == INF) {
          return 0;
        }
      
        $n = $sign * floor( abs($n) );
        $n = $n % pow(2,32);
      
        if ( $n < 0 ){
          $n += pow(2,32);
        }
      
        return ( $n );
      }
      
      function ToUint16( $n ) {
        $sign = ( $n < 0 ) ? -1 : 1;
      
        if ( abs( $n ) == 0 || abs( $n ) == INF) {
          return 0;
        }
      
        $n = ( $sign * floor( abs($n) ) ) % pow(2,16);
      
        if ($n <0) {
          $n += pow(2,16);
        }
      
        return ( $n );
      }
      
      function Mask( $b, $n ) {
        $b = ToUint32BitString( $b );
        $b = substr( $b, strlen($b) - $n );
        $b = ToUint32Decimal( $b );
        return ( $b );
      }
      
      function ToUint32BitString( $n ) {
        $b = "";
        for ( $p = 31; $p >=0; $p-- ) {
          if ( $n >= pow(2,$p) ) {
            $b .= "1";
            $n -= pow(2,$p);
          } else {
            $b .= "0";
          }
        }
        return $b;
      }
      
      function ToInt32BitString( $n ) {
        $b = "";
        $sign = ( $n < 0 ) ? -1 : 1;
      
        $b .= ( $sign == 1 ) ? "0" : "1";
      
        for ( $p = 30; $p >=0; $p-- ) {
          if ( ($sign == 1 ) ? $sign * $n >= pow(2, $p) : $sign * $n > pow(2,$p) ) {
            $b .= ( $sign == 1 ) ? "1" : "0";
            $n -= $sign * pow( 2, $p );
          } else {
            $b .= ( $sign == 1 ) ? "0" : "1";
          }
        }
      
        return $b;
      }
      
      function ToInt32Decimal( $bin ) {
        $r = 0;
        $sign;
      
        if ( intval($bin[0]) == 0 ) {
          $sign = 1;
          $r = 0;
        } else {
          $sign = -1;
          $r = -(pow(2,31));
        }
      
        for ( $j = 0; $j < 31; $j++ ) {
          $r += pow( 2, $j ) * intval($bin[31-$j]);
        }
      
        return $r;
      }
      
      function ToUint32Decimal( $bin ) {
        $r = 0;
      
      
        for ( $l = strlen($bin); $l < 32; $l++ ) {
          $bin = "0" . $bin;
        }
      
        for ( $j = 0; $j < 32; $j++ ) {
          $r += pow( 2, $j ) * intval($bin[31-$j]);
      
        }
      
        return $r;
      }
      
      function RShift( $s, $a ) {
        $s = ToUint32BitString( $s );
        for ( $z = 0; $z < $a; $z++ ) {
          $s = "0" . $s;
        }
        $s = substr( $s, 0, strlen($s) - $a );
      
        return ToUint32Decimal($s);
      }
      
      function UnsignedRightShift( $s, $a ) {
        $s = ToUint32( $s );
        $a = ToUint32( $a );
        $a = Mask( $a, 5 );
        return ( RShift( $s, $a ) );
      }
      

      使用示例: UnsignedRightShift(10, 3); ( = 10 >>> 3 )

      免责声明:我知道这甚至不是“专业”解决方案,性能很差(4.33 秒,110,000 次循环;有问题的函数完成约 0.04 秒,110,000 次循环),也许有在这个sn-p中甚至是不必要的功能,但目前我只有时间逐行实现它。如果有人有更好的解决方案、更好的性能或更简洁的代码,我很高兴看到它。

      【讨论】:

        猜你喜欢
        • 2011-02-08
        • 1970-01-01
        • 2015-03-19
        • 2011-05-08
        • 2013-01-03
        • 2015-10-13
        • 2022-07-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多