欢迎来到小护士读书笔记系列之《深入理解计算机系统》第二章信息的表示和处理(二)。本文将延续上篇内容,继续为大家抒写:2.2整数表示。
我们都知道C语言是最古老的语言,它支持两种整数表示,一种是只可以表示非负数的无符号数,另一种是正负数都可以表示的有符号数。虽然在更加古老的年代中,负数表示除了补码以外,还有反码和原码来表示负数;但因为现代计算机都默认以补码来实现,所以作者直接把补码等同于有符号数。
尴尬的是,在2.2开篇,作者就直接扔你一脸函数表格,让读者不知所措。小护士表示见过大场面,直接忽略掉。
这小节介绍了C语言数据类型的取值范围。其中特别表明,long
类型在32位系统和64位系统有不同的数据长度表现,因此,C语言设计了int32_t
和int64_t
来明确这种取值范围。
由于,当前大部分系统都可以运行64位程序,小护士为了方便大家复习,就在这里列出数值表格:
C数据类型 | 最小值 | 最大值 |
---|---|---|
[signed] char | -128 | 127 |
unsigned char | 0 | 255 |
short | -32 768 | 32 767 |
unsigned short | 0 | 65 535 |
int | -2 147 483 648 | 2 147 483 647 |
unsigned | 0 | 4 294 967 295 |
long | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 807 |
unsigned long | 0 | 18 446 744 073 709 551 615 |
int32_t | -2 147 483 648 | 2 147 483 647 |
uint32_t | 0 | 4 294 967 295 |
int64_t | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 807 |
uint64_t | 0 | 18 446 744 073 709 551 615 |
Java没有无符号数。:)
什么都不用说,故意无视数学细节,直接上二进制转十进制等式:
[0001]=0∗23+0∗22+0∗21+1∗20=0+0+0+1=1 [ 0001 ] = 0 ∗ 2 3 + 0 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 0 + 0 + 1 = 1
[0101]=0∗23+1∗22+0∗21+1∗20=0+4+0+1=5 [ 0101 ] = 0 ∗ 2 3 + 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 4 + 0 + 1 = 5
[1011]=1∗23+0∗22+1∗21+1∗20=8+0+2+1=11 [ 1011 ] = 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = 8 + 0 + 2 + 1 = 11
[1111]=1∗23+1∗22+1∗21+1∗20=8+4+2+1=15 [ 1111 ] = 1 ∗ 2 3 + 1 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = 8 + 4 + 2 + 1 = 15
什么都不用说,继续无视数学细节,还是那道二进制转十进制等式:
[0001]=−0∗23+0∗22+0∗21+1∗20=0+0+0+1=1 [ 0001 ] = − 0 ∗ 2 3 + 0 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 0 + 0 + 1 = 1
[0101]=−0∗23+1∗22+0∗21+1∗20=0+4+0+1=5 [ 0101 ] = − 0 ∗ 2 3 + 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 4 + 0 + 1 = 5
[1011]=−1∗23+0∗22+1∗21+1∗20=−8+0+2+1=−5 [ 1011 ] = − 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 8 + 0 + 2 + 1 = − 5
[1111]=−1∗23+1∗22+1∗21+1∗20=−8+4+2+1=−1 [ 1111 ] = − 1 ∗ 2 3 + 1 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 8 + 4 + 2 + 1 = − 1
补码就是把第一位(高位)作为符号表示,1为负数,0为正数。
补码的正数与负数不对称,是因为用了最高位的1作为负数标记符号。
其他补充:
[1111 [ 1111 1111]= 1111 ] = 0xFF =−1 = − 1
[0000 [ 0000 0000]= 0000 ] = 0x00 =0 = 0
补码最小值:
8位字长: 0x80 =−128 = − 128
16位字长:0x8000 =−32768 = − 32768
补码最大值:
8位字长: 0x7F =127 = 127
16位字长:0x7FFF =32767 = 32767
怎么转?位不变,直接用不同的二进制转十进制等式就行了。
因为二进制的数位不变,所以实际上就是没转。
关于扩展的画风是这样的:
类型 | 十进制 | 十六进制 |
---|---|---|
short | -12345 | 0x CF C7 |
unsigned short | 53191 | 0x CF C7 |
int | -12345 | 0x FF FF CF C7 |
unsigned int | 53191 | 0x 00 00 CF C7 |
大家肯定会觉得如下有符号数等式很奇怪:
int
0xCFC7 == 0xFFFFCFC7
手算一下就知道了,不解释,小学数学范畴。
关于截断的画风是这样的:
类型 | 十进制 | 十六进制 |
---|---|---|
int | 53191 | 0x 00 00 CF C7 |
unsigned int | 53191 | 0x 00 00 CF C7 |
short | -12345 | 0x CF C7 |
unsigned short | 53191 | 0x CF C7 |
所谓的截断就是把高位的位数砍掉,例如8位数截断到4位数,则把前4位(高位的4位)砍掉。
由于补码以最高位作为符号标记,所以出现截断时,才会有可能得出负数的结果。
小护士一句话概括:
不要随便转来转去。
小护士再来一句:
要是在C语言代码中出现这种无符号与有符号互转的,代码评审直接不通过。:)
2.2就是讲讲无符号与有符号(补码)的恩怨情仇。下一篇,《深入理解计算机系统》第二章信息的表示和处理(三),2.3整数运算,将会给大家讲述无符号数与有符号数的加减乘除法。别走太远哦。