Search code examples
javaphpbit-shift

Why after this method the result is different in Java and PHP and how to fix it?


With smaller numbers, this method works correctly in Java and Python, but with larger numbers, the result is different. What is the problem and how can I fix it?

Java code:

private static int combine(int val1, int val2) {
    int val3 = val1 << 8;
    return (val3 & 0xFFFFFFF) + val2 ^ (-0x10000000 & val3) >>> 24;
}

PHP code:

 function combine($val1, $val2) {
    $val3 = $val1 << 8;
    return ($val3 & 0xFFFFFFF) + $val2 ^ uRShift((-0x10000000 & $val3), 24);
 }
 
 function uRShift($a, $b) {
    if ($a < 0) 
    { 
        $a = ($a >> 1); 
        $a &= 2147483647; 
        $a |= 0x40000000; 
        $a = ($a >> ($b - 1)); 
    } else { 
        $a = ($a >> $b); 
    } 
    return $a; 
 }

For example, if in both methods I insert these numbers combine(125,107), the result in Java and PHP will be the same (32107). If I put in a larger number, like combine(287651245, 107), the result for Java is 87403851 and for PHP it is 87407691. I suspect there is something wrong with the left shifting.


Solution

  • Today most of PHP releases are x64 builds, that means integers are 64-bit long, so the result of this arithmetic $val3 = $val1 << 8; is a 64-bit integer.

    In Java you forced the result to be a signed 32-bit integer int, the left shift operator automatically cut off the exceeded bits, but in PHP it won't, so you need do it manually.

    function toSignedInt($a) {
        if(PHP_INT_SIZE == 4) return $a;
        $a &= 0xFFFFFFFF;
        return $a > 2147483647 ? $a - 4294967296 : $a;
    }
    

    You need be carefull in every places of overflow and wrap them with this function. In this code, they are the left shift operator and the add operator.

    function combine($val1, $val2) {
       $val3 = toSignedInt($val1 << 8);
       return toSignedInt(($val3 & 0xFFFFFFF) + $val2)
              ^ uRShift((-0x10000000 & $val3), 24);
    }