COMPLEMENT
Intro(Complement) #
重新认识计算机中的数字表示 #
主要以一个字节,8比特位进行分析
首先要知道计算机中的数字表示有空间限制,比如 java 中 byte 类型就用一个字节(8 bit)表示,而 int 得用 4byte(32 bit)。而且 java 默认都是有符号的,也即第一个比特位表示符号,一个byte 类型的变量可表示的值就是 \(00000000\) - \(11111111\)。
还有一点至关重要,就是进位后的值超出 8 位后会截断。比如 \(11111111 + 1 = 1{\hspace{0.5em}}00000000 ==截断==> 00000000 \),要不然就没法循环了,跟钟表一样,到极限得归位。
至于这些值表示的实际意义,可以在下面慢慢进行分析。(先不用考虑补码)1).
: 首先分析正数,总共 8 位,一个符号位占一个[0],剩下的 7 位表示值的话就就是 \(00000000\) - \(01111111\) 表示数值 0 到 127,这个毋庸置疑的。2).
: 不管一个负数怎么表示,都应该符合我们的理性认知。比如 -1 + 1 的值肯定是 0 的。3).
: 现在有了正数值比如 1 (\(00000001\)),加一操作后肯定等于 0。也即 \(00000001\) + \(8(*) = 00000000\)4).
: 目前只有让 \(8(*)\) 这个未知值在加完后整体位溢出,被截断,才能达到加完一个值后更小。5).
: 所以此处的 \(8(*)\) 二进制表示应该是 \(11111111\),加完 \(00000001\) 后得到 \(1{\hspace{0.5em}}00000000\),截断,然后正好等于 \(00000000\)。6).
: 所以才让这个二进制 \(11111111\) 表示的 -1。7).
: 其他情况依次了类推,重要的是理解截断才能循环。再举个例子(-128)。8).
: 已知上述得出的 -1\((11111111)\),和127\((01111111)\),所以 -128 \(8(*)\) + 127 \(01111111\) = -1 \(11111111\)9).
: 后面的值在二进制表示上要比被加数大,所以这次不用截断,给 \(8(*)\) 值设为 \(10000000\) 即可。也就是 -128 = \(10000000\)
为了更好的理解数据的分布,可以画一个圆来表示,方便记忆。认识补码 #
其实上述得出的二进制表示在计算机中被成为补码,不管是正数的也好,还是负数的也好。只是正数的补码与原码相同,而负数的补码与原码的关系可以推算出来,比如:
\([-128_补 = 10000000]\) 减一后 ==> \([01111111]\) 取反 ==> \([128_{补|原} = 10000000]\),注意 \(10000000\) 是更多字节中的表示的正数值 128,而在一个字节里面只能表示有符号位的负数。
\([-1_补 = 1111111]\) 减一后 ==> \([11111110]\) 取反 ==> \([1_{补|原} = 00000001]\)
\([-2_补 = 1111110]\) 减一后 ==> \([11111101]\) 取反 ==> \([2_{补|原} = 00000010]\)
也就是对于一个负数来说,补码等于它的正数值按位取反加一。辅助验证 #
Reference #