负数的表示

198 阅读4分钟

纸上得来终觉浅 绝知此事要躬行 - Python初学者的看书记录

用二进制表示一个正整数这很普通,比如7 = 0b0111。那二进制表示-7呢?

十进制中有7 +(-7) = 0,通过二进制计算我们有0b0111+0b1001 = 0,所以 -7就是0b1001,那0b1001为什么是-7而不是9?

有符号和无符号

要回答上面的问题,得知道存储于计算机中的数据都是有类型和长度限制的,什么类型使用多少个比特位。比如c语言中就有有符号类型和无符号类型。

// 有符号类型
short a = -7;
// 无符号类型
unsigned short b = 7;

当你声明为有符号时,编译器存的-7就是0b1001,申明为无符号时,存的是0b0111。所以对于0b1001究竟是-7还是+9,要根据存取时的具体类型来决定,是写程序的时候你自己声明的类型来决定的。

二进制补码

实际上在计算机中,如果是有符号类型的数字,最高位都有一个符号位,0表示正,1表示负。为了简单,我们用4个bit来表示一个有符号的数,如下表:

二进制十进制二进制十进制
000001000-8
000111001-7
001021010-6
001131011-5
010041100-4
010151101-3
011061110-2
011171111-1

上面二进制负数一列,我们称为“二进制补码”,它可以看成是对应正数的二进制数将其0和1取反,然后加1得来的。比如-5(1011) 就是 0101(5) ->1010->1011

要表示-5,
-> 取反:将5(0101)取反得1010
-> 加1:1010+1得1011

更多的时候,我们不需要关心负数怎么得来的,在调试程序查看内存数据时,我们要能识别某个字节存的是负数还是正数。所以更多的时候,我们需要将二进制负数转为十进制来看。

当我们看到某块内存中存的是1101,第一步先搞清楚它是有符号数还是无符号数(根据数据类型)。如果是无符号的,那就是13,如果是有符号的,那就是-3。所以二进制补码如何转为十进制负数?

前提:内存中看到某个字节内容是:1011,我们知道它是一个有符号数
-> 取反:0100
-> 加1:0101(只做无符号解释)
-> 加符号‘-’: -5(0101)

数字的范围

类型除了决定正负,还决定了存储数字的范围,比如下图是c语言中的各个类型的有效范围

TypeStorage sizeValue range
char1 byte-128 to 127 or 0 to 255
unsigned char1 byte0 to 255
signed char1 byte-128 to 127
int2 or 4 bytes-32,768 to 32,767 or -2,147,483,648 to 2,147,483,647
unsigned int2 or 4 bytes0 to 65,535 or 0 to 4,294,967,295
short2 bytes-32,768 to 32,767
unsigned short2 bytes0 to 65,535
long8 bytes or (4bytes for 32 bit OS)-9223372036854775808 to 9223372036854775807
unsigned long8 bytes0 to 18446744073709551615

根据类型我们可以知道其占用的bit个数,假设某个类型占用了n个比特,如果是无符号类型那它的范围是:,如果是有符号的类型,范围是:

在python中数字没有那么多类型,只有int和float类型,且既可以表示负数也可以表示正数,声明的时候根据符号来决定是有符号还是无符号,且长度是不固定的,没有c语言限制这么多。