纸上得来终觉浅 绝知此事要躬行 - 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来表示一个有符号的数,如下表:
| 二进制 | 十进制 | 二进制 | 十进制 |
|---|---|---|---|
| 0000 | 0 | 1000 | -8 |
| 0001 | 1 | 1001 | -7 |
| 0010 | 2 | 1010 | -6 |
| 0011 | 3 | 1011 | -5 |
| 0100 | 4 | 1100 | -4 |
| 0101 | 5 | 1101 | -3 |
| 0110 | 6 | 1110 | -2 |
| 0111 | 7 | 1111 | -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语言中的各个类型的有效范围
| Type | Storage size | Value range |
|---|---|---|
| char | 1 byte | -128 to 127 or 0 to 255 |
| unsigned char | 1 byte | 0 to 255 |
| signed char | 1 byte | -128 to 127 |
| int | 2 or 4 bytes | -32,768 to 32,767 or -2,147,483,648 to 2,147,483,647 |
| unsigned int | 2 or 4 bytes | 0 to 65,535 or 0 to 4,294,967,295 |
| short | 2 bytes | -32,768 to 32,767 |
| unsigned short | 2 bytes | 0 to 65,535 |
| long | 8 bytes or (4bytes for 32 bit OS) | -9223372036854775808 to 9223372036854775807 |
| unsigned long | 8 bytes | 0 to 18446744073709551615 |
根据类型我们可以知道其占用的bit个数,假设某个类型占用了n个比特,如果是无符号类型那它的范围是:,如果是有符号的类型,范围是:
。
在python中数字没有那么多类型,只有int和float类型,且既可以表示负数也可以表示正数,声明的时候根据符号来决定是有符号还是无符号,且长度是不固定的,没有c语言限制这么多。