在将此标记为重复之前,请阅读以下内容,并检查 my code * my updated code!
所以我的问题是,我必须实现Java / JavaScript'>>>' (无符号右移/零填充右移),但我无法以完全相同的方式工作。
我选择了我在SO和网络上找到的11个最有希望的实现(链接在代码中添加为注释)并添加了一些测试用例。不幸的是, NONE 函数返回了与所有测试相同的Java / JS响应。 (可能其中一些只在32位系统上工作)
实时代码+ 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&gt;&gt;&gt; -3)和 *(32&gt;&gt; 32)上失败,但是仅通过(-3>&gt;&gt; 0);并且shr11失败(-3&gt;&gt;&gt; 0)以及(32&gt;&gt;&gt; 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 >>> 0
仅在JavaScript (为什么?)中等于4294967293
,但在Java中,它等于-3
。不幸的是,这并没有改变我仍然无法通过所有测试的任何功能的事实。
* BIG UPDATE:
自PHP 7起,按负数位移位被视为无效并导致:“致命错误:未捕获的算术错误:按负数移位”。根据这一点,我认为我们不必通过这些测试,所以我已经更新了问题和代码。
答案 0 :(得分:4)
在查看问题(“shr9”和“shr11”)中的两个函数并合并/调整好的部分之后,我终于找到了解决方案。所有测试都通过了(我甚至在演示中添加了更多),它也适用于负数的移位。
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
答案 1 :(得分:2)
由于我真的没有想法,我克隆了Chromium V8引擎和Mozilla Central回购以获得SpiderMonkey。我开始搜索JS&gt;&gt;&gt;运算符,最后在Mozilla的代码中,我找到了一个近20年的文件(从1997年开始),js/src/tests/ecma/Expressions/11.7.3.js,其中包含用于测试“零填充按位右移操作”的无操作符代码。在PHP中重写后,此代码通过了所有测试。
<?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&gt;&gt;&gt; 3)
免责声明:我知道这甚至不接近“专业”解决方案,性能不好(4.33s,110,000循环;有问题的功能完成~0.04s,110,000循环),也许有甚至在这个片段中不必要的功能,但目前我只有时间逐行实现它。如果有人有更好的解决方案,更好的性能或更清晰的代码,我很高兴看到它。