PHP中十六进制数的否定,有趣的行为

9

我遇到了一些奇怪的行为,希望有人能帮我澄清一下。

看看这个:

$hex = 0x80008000;

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

输出

10000000000000001000000000000000
10000000000000001000000000000000

如预期所料。

但是

$hex = 0x80008000;

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

输出

1111111111111110111111111111111
1111111111111111111111111111111

为什么当取反$hex时,中间的位没有切换?

你使用的是哪个版本的PHP?在我这里第二种情况在5.3.6上可以工作。 - Jeff Lambert
我正在运行5.3.8版本,问题仍然存在。 - Pateman
可能与这个错误 https://bugs.php.net/bug.php?id=61095 有关。PHP只是一个有缺陷的语言。顺便说一下,我在os x lion上运行5.3.8版本的suhosin,你的代码完全正常。(不过是64位的) - Basti
PHP 经常嘲讽其用户 :) - Niklas B.
仅使用 print_r(~$hex)print_r(~intval($hex)),你会得到什么? - Damp
2个回答

0

我要在这里尝试回答自己的问题。

是的,这是32位/64位的区别。

在32位系统中,浮点类型必须占用两个内存空间才能获得所需的64位。Php使用双精度(参见http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers

$hex评估为浮点类型。intval和decbin函数将其转换为int类型(上面的第一个示例)

在第二个示例中,我们在使用decbin之前使用非按位运算符。这首先翻转双精度浮点数中的两个内存空间中的位,然后转换为int。这给了我们与预期不同的结果。

确实,如果我们将否定放在intval()内部,就像这样:

$hex = 0x80008000;

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

我们得到
1111111111111111111111111111111
1111111111111111111111111111111

作为输出。

我还不够好,无法用数学证明这一点(可以通过本文http://en.wikipedia.org/wiki/Double_precision的帮助来解决)。但也许等我有时间了再说吧 -_-

我认为学习计算机中数字的表示方式非常重要,这样我们就能理解这种异常情况,而不将其称为错误。


-1
可能是属于这种情况:
从 PHP 位运算符页面 http://us3.php.net/manual/en/language.operators.bitwise.php
NOT或补码运算符(~)和负二进制数可能会让人感到困惑。
因为使用了公式~x = -x-1,所以~2 = -3。十进制数的位补码是该数的取反再减1。
注意:以下示例仅使用4位,但实际上PHP使用32位。
将负十进制数(例如-3)转换为二进制需要3个步骤:1) 将正的十进制数转换为二进制(例如3 = 0011);2) 翻转比特位(例如0011变为1100);3) 加1(例如1100 + 0001 = 1101)。
你可能会想知道为什么1101等于-3。好吧,PHP使用“二进制补码”方法来表示负二进制数。如果最左边的比特位是1,则该二进制数为负数,您需要翻转比特位并加1。如果最左边的比特位是0,则它是正数,您无需进行任何操作。因此,0010将是一个正2。如果它是1101,则为负数,您需要翻转比特位以获取0010。加1后,您将得到0011,这等于-3。

我认为这与问题无关。据我理解,intval($hex)应该完全等同于$hex,因此我期望~intval($hex) === ~$hex - Niklas B.
是的,我这里不进行任何转换,只是以二进制格式表示数字。 - Vigrond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接