1.引例:
今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题,被它们之间的转换原理和位移原理搞得头大了。真的很后悔本科的时候没有认真学习《计算机组成原理》/《计算机操作系统》等计算机基础课程。以下是我根据相关知识回顾和整理的材料,如有和某某的文章有雷同之处,请勿见怪。另外也希望看到这篇文章的同志们能够有所收获吧。
#include <cstdio> #include <iostream> using namespace std; int main() { unsigned short int ui; signed short int si; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("ui = %u\n",ui); printf("si = %d\n",si); ui = ui>>1; si = si>>1; printf("ui = %u\n",ui); printf("si = %d\n",si); cout<<"------------------------------"<<endl; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("%u\n",ui); printf("%d\n",si); ui = ((signed short int)ui>>1); si = ((unsigned short int)si>>1); printf("%u\n",ui); printf("%d\n",si); cout<<"------------------------------"<<endl; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("%u\n",ui); printf("%d\n",si); ui = ui<<1; si = si<<1; printf("%u\n",ui); printf("%d\n",si); cout<<"-------------------------------"<<endl; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("%u\n",ui); printf("%d\n",si); ui = ((signed short int)ui<<1); si =((unsigned short int)si<<1); printf("%u\n",ui); printf("%d\n",si); return 0; }
显示结果:
ui = 32768
si = -32768
ui = 16384
si = -16384
------------------------------
32768
-32768
49152
16384
------------------------------
32768
-32768
0
0
-------------------------------
32768
-32768
0
0
2.概念
在计算机中,可以区分正负类型的数,成为“有符号数”(signed);无正负类型的数(只有整数类型),成为“无符号数”(unsigned)。简明的说,无符号说就是其所有的位数都用来表示数值的大小,有符号数除最高位来表示数值的正负外(0表示正数;1表示负数),其余各位用来表示数值的大小。举个例子说明一下:十机制数 正数255 二进制表达形式:1111 1111
十机制数 负数-1 二进制表达形式:1111 1111
可见-1的二进制的最高位为红色的1,可是为什么其表达形式为1111 1111而不是1000 0001呢?这就关于任何数在计算机内是以补码形式存储问题。下面会介绍的,在此先不详细说明了。
3.存储范围
从前面的介绍可以知道,由于有符号数的最高位被拿来用作符号位,所以它所能够表达的最大数值要小于无符号数所能够表达的最大数值。还是举个例子来说明一下吧:
无符号数:1111 1111 十进制值:255
有符号数:0111 1111 十进制值:127
这是有人可能会提出这样的疑问:有符号数所能够表达的数值范围会不会小于无符号数所能够表达的数值范围呢?
呵呵,答案是否定的!虽然有符号数在表达最大值上的能力减弱了,但是它能够表达负数。负数的个数可以弥补其不足。来让我们比较一下:
一个字节的无符号数的表达数值范围是:[0,255]
一个字节的有符号数的表达数值范围是:[-128,0),[0,127]
可见它们都能够表示256个数。
4.各种码(原码/反码/补码)
有些人也许会这样认为"-1"(双字节)在计算机中的表达形式为1000 0000 0000 0001,可是实际上不是的。计算机是以其补码的形式进行表达的,即“-1”(双字节)的表达形式是1111 1111 1111 1111。
说一下各种码的概念吧。
原码:一个整数,按照绝对值的大小转换成二进制数,最高位为符号位。
反码:将原码除最高位(符号位)外,其余各位按位取反,所得到的二进制码。正数的反码为原码。
补码:反码最低位加1即为补码。
关于负数的补码求法说明一下,先得到其反码,之后将反码加1即可。有些大神根据其原码,闭眼即得,这种能力需要修炼一下啊。
这时有些人可能会说,为什么要引入补码的形式呢?直接按照原码存储不就省事很多吗?嘿嘿,要记住,有些事情并不是你想省事就能省事的。好了来欣赏一下补码的优势吧。
计算机的带符号数用补码表示的优点:
- 1负数的补码与对应正数的补码之间的转换可以用同一种方法-求补运算完成,可以简化硬件。
- 2 可将减法变为加法,这样减法就可以用加法器进行计算了。
- 3 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
心算求补(大神求补算法):
从最低位开始至找到的第一个1均不变,符号位不变,这之间的各位“求反”(0变1;1变0)。
原码:1010 1001 补码:1101 0111.
5.有符号数与无符号数的相互转换
无符号整数和有符号整数之间进行强制类型转换时,位模式不改变。
有符号数转换为无符号数时,负数转换为大的正数,相当于在原值上加上2的n次方,而正数保持不变。
无符号数转换为有符号数时,对于小的数将保持原值,对于大的数将转换为负数,相当于原值减去2的n次方。
当表达式中存在有符号数和无符号数类型时,所有的操作都自动转换为无符号类型。可见无符号数的运算优先级高于有符号数。
unsigned int a = 20; signed int b = -130;
运算一下结果是 b>a 。
6.转换大餐
有符号数的转换
原类型 | 目标类型 | 转换方法 |
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 |
7.各种数据类型所占字节
32位平台下:
ps:文中若有不当之处,请指出!