有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

PHP中未签名的右移/Zerofill Right Shift/>(Java/JavaScript等效)

在将此标记为副本之前,请阅读下面的内容,并检查my code*my updated code

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

我选择了在SO和web上发现的11个最有前途的实现(链接作为注释添加到代码中),并添加了一些测试用例。不幸的是,没有一个函数对所有测试返回与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 

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


*大更新:

从PHP 7开始,按负数移位被认为是无效的,并导致:“致命错误:未捕获算术错误:按负数移位位”。根据这一点,我认为我们不必通过这些测试,所以我更新了问题和代码


共 (2) 个答案

  1. # 1 楼答案

    因为我真的没有主意了,我克隆了Chromium V8引擎和Mozilla Central repo来获取SpiderMonkey。我开始搜索JS>>>;运算符,最后在Mozilla的代码中,我发现了一个有将近20年历史的文件(从1997年开始),其中包含用于测试“按位零填充右移操作”的无运算符代码。在用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.33s,110000个循环;有问题的函数完成~0.04s,110000个循环),也许这段代码中甚至有不必要的函数,但目前我只有时间逐行实现它。如果有人有更好的解决方案、更好的性能或更干净的代码,我非常乐意看到

  2. # 2 楼答案

    在研究了问题中的两个函数(“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