Weird behaviour in PHP and Apache2: different outp

2019-06-11 22:31发布

问题:

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

回答1:

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;