I'm implementing a simple bit rotation in PHP using this code:
(($n>>1)&0xFFFFFFFF)|(($n&0x00000001)<<31)
When I run this code on different machines, I get completely different results. At first I thought it had something to do with endianness but they're both x86, I'm just using different versions of PHP.
What's causing this behavior?
Edit: nevermind my previous example, it was incorrect
The problem is definitely caused by PHP_INT_SIZE being 4 and not 8
PHP 5.5 and 5.6 on x86-64 windows is experimental and uses 32 bit arithmetic. There aren't any ways around this so you just need to take into consideration that word size may vary from 4 to 8 bytes even across the same architecture.
You can examine here step by step. I did a little 3v4l.org script
I illustrated two bit rotations: 1st for 2147483647, 2nd for 2147483648 1st is the max 32 bit number, 2nd is one more
See: PHP: integers -Manual # Integer overflow
You can see, the output is the same for every PHP version from 4.3.0 upwards
Here's the code:
<?php
$n = 2147483647;
shift($n);
echo "\n";
echo "\n";
echo "\n";
echo "\n";
$n = 2147483648;
shift($n);
function out($a, $b, $c, $b2 = '0', $c1 = ' ') {
$padstra = 40;
$padstrb = 23;
echo
str_pad($a, $padstra, ' ', STR_PAD_LEFT)
. " "
. str_pad(decbin($b), 32, $b2, STR_PAD_LEFT)
. " "
. str_pad($c, $padstrb, $c1) . "\n";
}
function shift($n) {
out($n , $n , '$n' );
echo "\n";
out('A:', $n >> 1 , '$n >> 1' );
out('B:', 0xFFFFFFFF , '0xFFFFFFFF' );
out('A & B:', ($n >> 1) & 0xFFFFFFFF , '($n >> 1) & 0xFFFFFFFF' );
echo "\n";
out('C:', $n , '$n' );
out('D:', 0x00000001 , '0x00000001' );
out('C & D:', $n & 0x00000001 , '$n & 0x00000001' );
echo "\n";
out('C & D:', $n & 0x00000001 , '$n & 0x00000001' );
out('(C & D) << 31:', ($n & 0x00000001) << 31 , '($n & 0x00000001) << 31' );
echo "\n";
out('A & B:', ($n >> 1) & 0xFFFFFFFF , '($n >> 1) & 0xFFFFFFFF' );
out('(C & D) << 31:', ($n & 0x00000001) << 31 , '($n & 0x00000001) << 31' );
out('(A & B) | ((C & D) << 31):', ($n >> 1) & 0xFFFFFFFF | ($n & 0x00000001) << 31 , '($n >> 1) & 0xFFFFFFFF | ($n & 0x00000001) << 31');
}
Here's the output
2147483647 01111111111111111111111111111111 $n
A: 00111111111111111111111111111111 $n >> 1
B: 11111111111111111111111111111111 0xFFFFFFFF
A & B: 00111111111111111111111111111111 ($n >> 1) & 0xFFFFFFFF
C: 01111111111111111111111111111111 $n
D: 00000000000000000000000000000001 0x00000001
C & D: 00000000000000000000000000000001 $n & 0x00000001
C & D: 00000000000000000000000000000001 $n & 0x00000001
(C & D) << 31: 10000000000000000000000000000000 ($n & 0x00000001) << 31
A & B: 00111111111111111111111111111111 ($n >> 1) & 0xFFFFFFFF
(C & D) << 31: 10000000000000000000000000000000 ($n & 0x00000001) << 31
(A & B) | ((C & D) << 31): 10111111111111111111111111111111 ($n >> 1) & 0xFFFFFFFF | ($n & 0x00000001) << 31
2147483648 10000000000000000000000000000000 $n
A: 01000000000000000000000000000000 $n >> 1
B: 11111111111111111111111111111111 0xFFFFFFFF
A & B: 01000000000000000000000000000000 ($n >> 1) & 0xFFFFFFFF
C: 10000000000000000000000000000000 $n
D: 00000000000000000000000000000001 0x00000001
C & D: 00000000000000000000000000000000 $n & 0x00000001
C & D: 00000000000000000000000000000000 $n & 0x00000001
(C & D) << 31: 00000000000000000000000000000000 ($n & 0x00000001) << 31
A & B: 01000000000000000000000000000000 ($n >> 1) & 0xFFFFFFFF
(C & D) << 31: 00000000000000000000000000000000 ($n & 0x00000001) << 31
(A & B) | ((C & D) << 31): 01000000000000000000000000000000 ($n >> 1) & 0xFFFFFFFF | ($n & 0x00000001) << 31
User contributions licensed under CC BY-SA 3.0