2016-12-14 123 views
5

在將其標記爲副本之前,請閱讀以下內容並檢查my code * my updated codePHP中的無符號右移/零填充右移(Java/JavaScript等效)

所以我的問題是,我必須實現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 >>> -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 >>> 0只有在JavaScript 等於4294967293,但在Java中,它等於-3(爲什麼?)。不幸的是,這並沒有改變我仍然無法通過所有測試的功能。


*大的更新:

自PHP 7中,由負數位移位被認爲是無效的,並導致:「致命錯誤:未捕獲ArithmeticError:由負數位移」 。據此,我認爲我們不必通過這些測試,所以我更新了問題和代碼。

回答

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

這個代碼不只是準確,但也快。
基準測試結果:100000個循環中:0.25秒
基準測試:http://phpfiddle.org/main/code/mj68-1s7e

1

由於我真的沒有想法,我克隆了Chromium V8引擎和Mozilla中央回購站以獲得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.33s有110,000個循環;有問題的功能用110,000個循環完成〜0.04s),也許在這個片段中甚至有不必要的功能,但目前我只有時間逐行實現它。如果任何人有更好的解決方案,更好的性能或更清晰的代碼,我很樂意看到它。

+0

基準:http://phpfiddle.org/main/code/1x1n-kzfc – frzsombor