在计算机科学中,一切都是关于解释的。对于计算机来说,一切都是可以用许多方式解释的比特序列。例如,
0100001
可以是数字33或者是
!
(这就是
ASCII 将该比特序列映射成的方式)。
对于计算机来说,一切都是比特序列,无论你将其视为数字、字母、文本、Word 文档、屏幕上的像素、显示的图像还是硬盘驱动器上的 JPG 文件。如果你知道如何解释这个比特序列,它可能会被转化为人类可读的有意义的东西,但在 RAM 和 CPU 中只有比特。
因此,当你想要在计算机中存储一个数字时,你必须对其进行编码。对于非负数来说,这很简单,你只需要使用二进制表示法。但负数怎么办呢?
您可以使用一种称为二进制补码的编码方式。在这种编码方式中,您需要决定每个数字将有多少位(例如8位)。最高有效位被保留作为符号位。如果它是0
,那么该数字应被解释为非负数,否则为负数。其他7位包含实际数字。
00000000
表示零,就像无符号数字一样。 00000001
是一,00000010
是二,依此类推。在二进制补码中,您可以在8位上存储的最大正数是127(01111111
)。
下一个二进制数(10000000
)是-128。这可能看起来很奇怪,但稍后我会解释为什么这是有意义的。10000001
是-127,10000010
是-126等等。 11111111
是-1。
因为它有一些有趣的属性,所以我们为什么要使用这种奇怪的编码方式。具体来说,在执行加法和减法时,CPU 不必知道它是作为二进制补码存储的有符号数。它可以将两个数都解释为无符号数,将它们相加,结果将是正确的。
让我们试试:-5 + 5。-5 是
11111011
,
5
是
00000101
。
11111011
+ 00000101
000000000
结果有9个比特长。最高有效位溢出了,我们得到了
00000000
,也就是0。看起来它是起作用的。
另一个例子:23 + -7。23是
00010111
,-7是
11111001
。
00010111
+ 11111001
100010000
再次举例,最高有效位丢失了,我们得到00010000
== 16。它起作用了!
这就是二进制补码的工作原理。计算机在内部使用它来存储带符号的整数。
您可能已经注意到,在二进制补码中,当您否定数字N
的位时,它会变成-N-1
。例如:
- 0否定==
~00000000
== 11111111
== -1
- 1否定==
~00000001
== 11111110
== -2
- 127否定==
~01111111
== 10000000
== -128
- 128否定==
~10000000
== 01111111
== 127
这正是您观察到的:JS假装使用二进制补码。那么为什么parseInt('11111111111111111111111111111110', 2)
是4294967294?因为它只是在假装。
在内部,JS总是使用浮点数表示。它的运作方式与二进制补码完全不同,其位求反大多数情况下也是无用的,因此JS假装一个数字是二进制补码,然后取反其位并将其转换回浮点数表示。这在parseInt
中不会发生,因此即使二进制值似乎相同,您仍会得到4294967294。
11111111111111111111111111111111
是-1
>~0 === -1
- CerbrusparseInt
的第一个参数必须是一个字符串。parseInt('11111111111111111111111111111110', 2) | 0
- zerkms