数字在计算机中的表示1 - 整数

由于人类有十个手指,所以用十进制表示数字是一件很自然的事情。不过在计算机中,我们更通用的表示方式是二进制,这是由于可以用电路中的高电平和低电平、磁针的逆时针旋转和顺时针旋转等很方便的表示。

进制转换

那么如何在不同进制之间进行转换呢?下面进行简要分析:
在十进制中,有0-9个共10个数字,当数量达到10时,发现用0-9这10个数字已经无法表示了,把十位加1(原来十位是0,高位为0省略不写),然后个位重新回到0。
用这种方式来考虑二进制,二进制中有0, 1两个数字,当数量达到2时,发现无法用0, 1这两个数字表示了,把高一位加1(进位),当前位重新回到0。
所谓十六进制,就是用0-9, a-f一共十六个符号表示,方法和上述方法相同,a-f大小写均可,为了区别十进制,通常在十六进制数前面加上一个0x,比如0x3f3f表示十进制中的16191(3 * (16 ^ 3) + f * (16 ^ 2) + 3 * (16 ^ 1) + f * (16 ^ 0))。
还有一些进制转换的技巧,比如十进制转二进制的“除二取余”,网上资料很多,这里不再赘述。
可以很方便的把完成二进制和十六进制之间的转换,只需要相应的把二进制数每四位转换为一个十六进制数即可,如果二进制数不能被4整除,则在前面加0补足。比如101011,补足后是001010110010是2,1011是b,所以相应的十六进制数是0x2b

无符号整数和有符号整数

在C语言中,整数分两种,有符号的和无符号的,其实就是有符号的支持负数,而无符号的都是非负数而已。其他语言中很少有这么区分的,比如Java就只有有符号的整数。
int是最常用的整数类型,在目前的32位或64位机器上,他通常都是占4个字节的,每个字节能表示表示8位二进制数,也就是说,它可以表示2^32个数字。在古老的十六位机器上,int只能表示2^16个数字的。
那你可能要问,64位机器上int为什么不是64位呢?实际上变化的是long类型的大小和指针类型的大小。long类型在32位机器上是4个字节,而在64位机器上则是8个字节。指针也是如此,我们可以简单计算一下,2^32大约是4G,现在知道为什么内存4G以上的机器通常推荐安装64位的操作系统了吧?因为32位的指针没办法表示那么大的范围!所以4G以上的内存就都用不了了!
C语言现在还有一个数据类型叫long long,他是8字节的,所以能表示很大的整数,至于到底有多大自己计算一下吧~

无符号整数的表示很是干脆,直接就是二进制与十进制之间的简单转换了。
11111111,11111111,11111111,11111111就是十进制的4294967295,这也是无符号整数int所能表示的最大值。

有符号整数在绝大多数机器上的表示都是补码,这里讨论intunsigned int的情况。
补码的方式是,最高位表示的是符号位,它为0的时候表示的是一个非负数,为1的时候表示一个负数。
当补码表示非负数的时候,这个数的绝对值就是除了符号位剩下的二进制数所表示的整数。
当补码表示负数的时候,这个数的绝对值是除了符号位剩下的二进制数所表示的整数减去2^31。
比如11111111,11111111,11111111,11111111表示的就是-1。
比如01111111,11111111,11111111,11111111表示的就是2147483647。
所以有符号整数所能表示的最大值是-2147483648到2147483647,为什么正的比负的少1呢?因为中间有个0啊...
有符号整数还有两种表示形式,分别是原码和反码,除了在考试中,其他时候不常用。

无符号整数和有符号整数之间的转换

如果将一个无符号整数转换为有符号整数会发生什么呢?比如这样:

#include <stdio.h>

int main()
{
    int a = -1;
    unsigned int b = a;
    printf("int a = %d, unsigned int b = %u.\n", a, b);
}

这种尴尬的情况下,会得到这样一个结果:int a = -1, unsigned int b = 4294967295.
其实并不难理解,这是因为在无符号整数和有符号整数之间的转换并不会改变这块内存区域的具体值,只是改变了一种表示方法而已,因为有符号整数-1的二进制是11111111,11111111,11111111,11111111,而这个数字在无符号整数中恰好表示为4294967295

考虑以下代码,它会输出什么?提示:sizeof(int)是一个无符号整数,当一个无符号整数和有符号整数直接进行运算时,会先把有符号整数转换为无符号整数。

#include <stdio.h>

int main()
{
    for(int i = -1; i < sizeof(int); i++) {
        printf("hello\n");
    }
}

不同整数类型之间的转换

我们已经知道了intunsigned int之间转换的微妙关系,那么如果int和其他不同大小的类型之间转换会发生什么呢?
我们以intshort,这里认为int是4字节,而short是2字节。

长变短:截断

#include <stdio.h>

int main()
{
    int a = 65536;
    short b = a;
    printf("%d\n", b);
}

因为65536是'1,00000000,00000000',而short只有16位,所以截断处理,只要最后的16位,结果是0。

短变长:无符号填0补足,有符号填符号位补足

#include <stdio.h>

int main()
{
    short a = -1;
    int b = a;
    printf("%d\n", b);
}

结果输出-1,我们知道short的-1是11111111,11111111,而int的-1是11111111,11111111,11111111,11111111,说明是用符号位补足的。当然,如果a是符号位为0的非负数,转换得到的数前面就是就用0补足了,这样就可以保持值不变。
可以动手验证一下,unsigned short化为int时,是用0补足的。

加法运算

加法运算的规则很简单,无论是有符号整数还是无符号整数,只是把对应的二进制位相加,如果最高位发生进位,则截断处理,只保留后面的有效位。比如-1是11111111,11111111,11111111,11111111(-1)+(-1)的二进制位相加并截取有效位后得到11111111,11111111,11111111,11111110,这个刚好就是-1。

标签: 操作系统

添加新评论