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.
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);
}