Negation of Hex in PHP, funny behavior
Asked Answered
B

2

9

Got some weird behavior I was wondering if someone could clear up for me.

Check it out

$hex = 0x80008000;

print_r(decbin(intval($hex)) . '<br/>');
print_r(decbin($hex));

Outputs

10000000000000001000000000000000
10000000000000001000000000000000

As expected.

But

$hex = 0x80008000;

print_r(decbin(~intval($hex)) . '<br/>');
print_r(decbin(~$hex));

Outputs

1111111111111110111111111111111
1111111111111111111111111111111

Why is the middle bit not switching when $hex is negated?

Betimes answered 8/3, 2012 at 20:2 Comment(5)
What version of PHP are you using? The second case works for me on 5.3.6Corbie
I'm running 5.3.8 and the problem exists.Azerbaijan
May be related to this bug bugs.php.net/bug.php?id=61095 PHP is just a buggy language. Btw I'm running 5.3.8 with suhosin on os x lion and your code works just fine. (64bit though)Drue
PHP is constantly trolling its users :)Proudman
What do you get for just print_r(~$hex) and print_r(~intval($hex)) ?Missis
B
0

Gonna give a shot to my own question here.

Yes this is a 32-bit / 64-bit difference.

In 32-bit systems, a float type has to take up two memory spaces to get the required 64 bits. Php uses double-precision (see http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers)

The $hex evaluates to a float type. Intval and decbin functions convert this into an int type (1st example above)

In the 2nd example we are using the not bitwise operator BEFORE we use decbin. This flips the bits in the two-memory space double-precision float first, and then is converted to int second. Giving us something different than what we expected.

Indeed, if we put the negate inside of the intval() like so:

$hex = 0x80008000;

print_r(decbin(intval(~$hex)) . '<br/>');
print_r(decbin(~$hex));

We get

1111111111111111111111111111111
1111111111111111111111111111111

As output.

I'm not good enough to prove this with math yet (which can be figured out with the help of this article http://en.wikipedia.org/wiki/Double_precision). But maybe when I have time later -_-

I think it's very important to learn how numbers are represented in computers so we can understand anomalies like this and not call them bugs.

Betimes answered 11/3, 2012 at 20:9 Comment(0)
M
-1

Possibly a case falling under this:

From the php bitwise operators page https://www.php.net/manual/en/language.operators.bitwise.php

The NOT or complement operator ( ~ ) and negative binary numbers can be confusing.

~2 = -3 because you use the formula ~x = -x - 1 The bitwise complement of a decimal number is the negation of the number minus 1.

NOTE: just using 4 bits here for the examples below but in reality PHP uses 32 bits.

Converting a negative decimal number (ie: -3) into binary takes 3 steps: 1) convert the positive version of the decimal number into binary (ie: 3 = 0011) 2) flips the bits (ie: 0011 becomes 1100) 3) add 1 (ie: 1100 + 0001 = 1101)

You might be wondering how does 1101 = -3. Well PHP uses the method "2's complement" to render negative binary numbers. If the left most bit is a 1 then the binary number is negative and you flip the bits and add 1. If it is 0 then it is positive and you don't have to do anything. So 0010 would be a positive 2. If it is 1101, it is negative and you flip the bits to get 0010. Add 1 and you get 0011 which equals -3.

Missis answered 8/3, 2012 at 20:25 Comment(2)
I don't think that's related to the question. From my understanding, intval($hex) should be exactly equivalent to $hex, so I'd expect ~intval($hex) === ~$hex.Proudman
Yeah I'm not converting anything here, just representing numbers in binary format.Betimes

© 2022 - 2024 — McMap. All rights reserved.