I'm experiencing different outputs in PHP code running in Mac and Linux.
I have 2 servers running the following code:
$ltt = ((ord($str[7]) << 24) | (ord($str[8]) << 16) | (ord($str[9]) << 8) | (ord($str[10]))) / 1000000;
Even the ord(str[ ])
outputs are the same:
[7] = 254
[8] = 26
[9] = 22
[10] = 216
But, on the MAMP stack (Mac) running php 5.3.6, if $ltt is originally supposed to be a negative number, it returns 4263.12265
(incorrect).
On the LAMP stack (Ubuntu) running same php version, it will return the exact negative value -31.84465
.
This happens only with negative numbers..
Update Addl. Info:
- A var dump gives
þØçï_Kstring(25) "þØçï_K"
- bin2hex gives
000e1b00000000fe1a16d806e707ef0000045f0000004b0000
Simplying the function to include only numeric inputs, the output still differs:
$ltt = (254 << 24 | 26 << 16 | 22 << 8 | 216)/ 1000000;
4263.12265
on MAMP and -31.84465
on LAMP
This is a 32 vs 64 bit problem.
Because your most significant byte is > 127, on a 32 bit platform this is interpreted as a negative value because of integer overflow - the most significant bit is set. On a 64-bit platform it is not.
The solution is to use pack()
and unpack()
so you can specify that the integer should be signed. EDIT Fixed this code sample See edit 2
$packed = pack('C*', ord($str[7]), ord($str[8]), ord($str[9]), ord($str[10]));
$unpacked = unpack('l', $packed);
$lat = current($unpacked);
...however you should also be conscious that this will not work on a little-endian architecture, because the byte ordering will be wrong. You can simply reverse the order of the packed bytes to work around this, but I am just trying to wrap my head around a works-everywhere solution.
EDIT 2
OK, it took me a while to wrap my head around this but we got there in the end:
What you need to do is, if the most significant bit is set, OR the result with a number where the least significant 32 bits are not set but the rest are. So the following works on both 32 and 64 bit:
<?php
// The input bytes as ints
$bytes = array(254, 26, 22, 216);
// The operand to OR with at the end
$op = $bytes[0] & 0x80 ? (~0 << 16) << 16 : 0;
// Do the bitwise thang
$lat = (($bytes[0] << 24) | ($bytes[1] << 16) | ($bytes[2] << 8) | $bytes[3]) | $op;
// Convert to float for latitude
$lat /= 1000000;
echo $lat;