1.概念
计算机里的数是用二进制表示的,最左边的这一位一般用来表示这个数是正数还是负数,这样的话这个数就是有符号整数(signed integer)。如果最左边这一位不用来表示正负,而是和后面的连在一起表示整数,那么就不能区分这个数是正还是负,就只能是正数,这就是无符号整数(unsigned integer)。
计算机中的整数分为两类:不带符号位的整数(unsigned integer,也称为无符号整数),此类整数一定是正整数;带符号位的整数(signed integer),此类整数可以表示正整数,又可以表示负整数。
简明的说,无符号数就是其所有的位数都用来表示数值的大小,有符号数除最高位来表示数值的正负外(0表示正数;1表示负数),其余各位用来表示数值的大小。
举个例子说明一下:
十机制数 正数255 二进制表达形式:1111 1111
十机制数 负数-1 二进制表达形式:1111 1111
可见-1的二进制的最高位为红色的1,可是为什么其表达形式为1111 1111而不是1000 0001呢?这就关于任何数在计算机内是以补码形式存储问题,下面会介绍的。
2.存储范围
从前面的介绍可以知道,由于有符号数的最高位被拿来用作符号位,所以它所能够表达的最大数值要小于无符号数所能够表达的最大数值。还是举个例子来说明一下吧:
无符号数:1111 1111 十进制值:255
有符号数:0111 1111 十进制值:127
这是有人可能会提出这样的疑问:有符号数所能够表达的数值范围会不会小于无符号数所能够表达的数值范围呢?
答案是否定的!虽然有符号数在表达最大值上的能力减弱了,但是它能够表达负数。负数的个数可以弥补其不足。来让我们比较一下:
一个字节的无符号数的表达数值范围是:[0,255]
一个字节的有符号数的表达数值范围是:[-128,0),[0,127]
可见它们都能够表示256个数。
无符号整数常用于表示地址、索引等正整数,它们可以是8位、16位、32位、64位甚至更多。
8个二进制表示的正整数其取值范围是 0~255( -1)
16位二进制位表示的正整数其取值范围是 0~65535( -1)
32位二进制位表示的正整数其取值范围是 0~ -1
有符号和无符号的差别:
int是有符号的,unsigned是无符号的。
它们所占的字节数其实是一样的,但是有符号的需要安排一个位置来表达我这个数值的符号,因此说它能表示的绝对值就要比无符号的少一半。举个例子,我们有一个1个 [1] 字节的整数(虽然这种类型不存在),那么无符号的就是这样:00000000~11111111 这个就是无符号的范围。
一个字节是8位, 有符号的数,因为第一个位要用来表示符号,那么就只剩下7个位置可以用来表示数了0000000~1111111因为有符号,所以还可以表示范围:-1111 111 ~ +1111 111。
3.各种码(原码/反码/补码)
有些人也许会这样认为"-1"(双字节)在计算机中的表达形式为1000 0000 0000 0001,可是实际上不是的。计算机是以其补码的形式进行表达的,即“-1”(双字节)的表达形式是1111 1111 1111 1111。
说一下各种码的概念吧。
原码:一个整数,按照绝对值的大小转换成二进制数,最高位为符号位。
反码:将原码除最高位(符号位)外,其余各位按位取反,所得到的二进制码。正数的反码为原码。
补码:反码最低位加1即为补码。
关于负数的补码求法说明一下,先得到其反码,之后将反码加1即可。有些大神根据其原码,闭眼即得,这种能力需要修炼一下啊。
这时有些人可能会说,为什么要引入补码的形式呢?直接按照原码存储不就省事很多吗?嘿嘿,要记住,有些事情并不是你想省事就能省事的。好了来欣赏一下补码的优势吧。
计算机的带符号数用补码表示的优点:
1负数的补码与对应正数的补码之间的转换可以用同一种方法-求补运算完成,可以简化硬件。 2 可将减法变为加法,这样减法就可以用加法器进行计算了。 3 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
心算求补(大神求补算法):
从最低位开始至找到的第一个1均不变,符号位不变,这之间的各位“求反”(0变1;1变0)。
原码:1010 1001 补码:1101 0111.
4.有符号数与无符号数的相互转换
无符号整数和有符号整数之间进行强制类型转换时,位模式不改变。
有符号数转换为无符号数时,负数转换为大的正数,相当于在原值上加上2的n次方,而正数保持不变。
无符号数转换为有符号数时,对于小的数将保持原值,对于大的数将转换为负数,相当于原值减去2的n次方。
当表达式中存在有符号数和无符号数类型时,所有的操作都自动转换为无符号类型。可见无符号数的运算优先级高于有符号数。
5.转换大餐
有符号数的转换
原类型 | 目标类型 | 转换方法 |
char | short | 符号位扩展 |
char | long | 符号位扩展 |
char | unsigned char | 最高符号位失去位意义,变为数据位 |
char | unsigned short | 符号位扩展到short;然后从short转到unsigned short |
char | unsigned long | 符号位扩展到long;然后从long转换到unsigned long |
char | float | 符号位扩展到long;然后从long转到float |
char | double | 符号位扩展到long;然后从long转换到double |
char | long double | 符号位扩展到long;然后从long转换到long double |
short | char | 保留低位字节 |
short | long | 符号位扩展 |
short | unsigned char | 保留低位字节 |
short | unsigned short | 最高为失去意义,变为数据位 |
short | unsigned long | 符号位扩展到long;然后从long转到unsigned long |
short | float | 符号位扩展到long;然后从long转到float |
short | double | 符号位扩展到long;然后从long转到double |
short | long double | 符号位扩展到long;然后从long转换到long double |
long | char | 保留低位字节 |
long | short | 保留低位字节 |
long | unsigned char | 保留低位字节 |
long | unsigned short | 保留低位字节 |
long | unsigned long | 最高为失去意义,变为数据位 |
long | float | 使用单精度浮点数表示,可能失去精度 |
long | double | 使用单精度浮点数表示,可能失去精度 |
long | long double | 使用单精度浮点数表示,可能失去精度 |
无符号数的转换
原类型 | 目标类型 | 转换方法 |
unsigned char | char | 最高为作符号位 |
unsigned char | short | 0扩展 |
unsigned char | long | 0扩展 |
unsigned char | unsigned short | 0扩展 |
unsigned char | unsigned long | 0扩展 |
unsigned char | float | 转换到long;然后从long转换到float |
unsigned char | double | 转换到long;然后从long转换到double |
unsigned char | long double | 转换到long;然后从long转换到long double |
unsigned short | char | 保留低位字节 |
unsigned short | short | 最高为作符号位 |
unsigned short | long | 0扩展 |
unsigned short | unsigned char | 保留低位字节 |
unsigned short | unsigned long | 0扩展 |
unsigned short | float | 转换到long;然后从long转换到float |
unsigned short | double | 转换到long;然后从long转换到double |
unsigned long | long double | 转换到long;然后从long转换到long double |
unsigned long | char | 保留低位字节 |
unsigned long | short | 保留低位字节 |
unsigned long | long | 最高位作符号位 |
unsigned long | unsigned char | 保留低位字节 |
unsigned long | unsigned short | 保留低位字节 |
unsigned long | float | 转换到long;然后从long转换到float |
unsigned long | double | 直接转换到double |
unsigned long | long double | 转换到long;然后从long转换到long double |