有符号数、无符号数理解

大家都知道,在C/C++中,对于w位编译器,其有符号数表示的数值范围为-2 ^ (w-1)~2 ^(w-1)-1,无符号数表示的数值范围为0 ~ 2 ^ w-1,举个例子,在16位编译器中,有符号数的数值范围为-2 ^ 31 ~ 2 ^ 31-1,无符号数的数值范围为0 ~ 2 ^ 32-1。那么,有符号数和无符号数的区别在哪?同样都是以32位2进制位来表示(后文均以32位为准),为什么各自表示的数值范围不同呢,又为什么是这个范围呢?

1.有符号数与无符号数的区别

为了解释上面的问题,就不得不再提到另一个名词——“补码”,什么是补码呢?在《深入理解计算机系统》一书中,有这样一段话:“对于许多应用,我们还希望表示负数值。最常见的有符号数的计算机表示方式就是补码形式。在这个定义中,将字的最高有效位解释为负权”,这句话的意思其实很简单,就是32位二进制位中,最高位的权重为-1,换句话说,在无符号数中,最高位所代表的值是2 ^ 31,而在有符号数中,其表现方式是补码形式,最高位所代表的值是- 2^ 31。为了更加形象的来理解这句话的意思,我们用-1来作为一个例子。
在这里插入图片描述
关于这段程序的说明及问题
有一点值得注意的是:通常情况下,数字都默认为有符号类型。 因此这里的int a实际上就是signed int a;可见-1的二进制位表示为“11111111 11111111 11111111 11111111”。从这32中的最低位开始,每一位分别表示2 ^ 0、2 ^ 1、2 ^ 2、2 ^ 3…2 ^ 30,但是最高位就不一样了,按照有符号数的补码形式特点,其最高位的1表示的是负权的-2 ^ 31,这样,我们把每一位所表示的值加起来就是 - 2 ^ 31 +2 ^ 30+…+2 ^ 1+2 ^ 0=- 2 ^ 31+2 ^ 31-1=-1,可见其刚好就等于-1了,如果我们把程序中的a换成unsigned int类型,那么按前面所说的,最高位的1就应当表示为2 ^ 31,那么unsigned int(a)的值就应当是2 ^ 31 +2 ^ 30+…+2 ^ 1+2 ^ 0=2^32-1=4294967295,我们来看看是不是这样的呢:
在这里插入图片描述
可以看到,刚好跟我们想的是一样的,反过来,如果先定义一个unsigned 类型的,假设其为2147483649,很明显,这个数已经超出了有符号数的上限,它的二进制表示为“10000000 00000000 00000000 00000001”,按照前面分析的,这里的最高位1表示的是2 ^ 31,把每一位加起来就是2 ^ 31+2^0=2147483649,如果将其转换为signed类型的话,那么最高位1表示的就是-2 ^ 31,那么转换后的(signed)2147483649就应当为-2 ^ 31+2 ^0=-2147483647,我们来验证一下:
在这里插入图片描述
可以看到,运行结果完全符合我们的猜想。这就说明了有符号数和无符号数的区别:在32位编译器中,有符号数的二进制位最高位表示-2^ 31,而无符号数的二进制位最高位表示的是2^31。根据这一区别,我们也不难得到有符号数与无符号数的一些转换原理。

2.有符号数与无符号数的转换

2.1 转换公式

前面已经知道了有符号数与无符号数的区别,那么实际上就很容易得出二者的转换关系:假设一个数x,无论它是有符号数还是无符号数,它的二进制表示肯定都是唯一的(不可能在有符号形式下有一种表示,在无符号形式下也有一种表示),那么假设其二进制位中的最高位为m(m=0或1),其余位组合表示的数为n,打个比方,10的二进制表示为1010,那么它的最高位就是m=1,n=2(010),那么很明显,x=m*2^(w-1)+n,其中w为这个数的二进制位数,在32位编译器中w=32,64位编译器中w=64。

以32位编译器为例,对于无符号数,由于其最高位代表2^31,因此x=m * 2 ^ 31+n ;而对于有符号数而言,由于其最高位代表-2 ^31,因此x=-m * 2 ^31+n,因此,无符号数要想转换为有符号数,就需要减上m * 2 ^32,那什么时候m为0什么时候m为1呢?很简单,对于无符号数来说,m为1表示x>=2 ^31,否则m=0;对于有符号数来说,m=1表示x<0否则m=0。由此得到转换公式如下:转换公式如下:
在这里插入图片描述

其中U代表无符号数,S代表有符号数,w表示编译器的位数。

2.2 显示转换

显示转换的方式如下所示:
在这里插入图片描述
程序不用多说,显示转换还是很简单的,显示转换就是一种强制类型转换。

2.3 隐式转换

隐式转换主要在以下两种情况下发生:
①当一种类型的表达式被赋值给另外一种类型的变量时;
②当执行一个运算时,如果它的一个运算数是有符号的而另一个是无符号的,那么就会隐式地将有符号参数强制类型转换为无符号数。

对于第①种情况,如下所示:

可见,输出的并不是-1,为什么不是-1而是4294967295呢?原因就在于unsigned int a=-1;这一句,前面说过
通常情况下,数字都默认为有符号类型。 因此这里的-1就是有符号类型,当它被赋值无符号型变量a时,-1就被隐式转换为无符号类型了,因此这里就需要采用前面的转换公式,-1+2^32=4294967295。

对于第②种情况,如下所示:
在这里插入图片描述
如图所示,a=1,本身是大于-1的,应该返回true,但是由于这里a为无符号数,-1是有符号数,在进行“>”运算时,-1被强制转换成了无符号数,即成了4294967295,因此返回的真值是1>4294967295的真值结果,就是false。
这种情况往往对于标准的加减乘除运算来说并没有多大差异,但是对于“>”、“<”这样的关系运算符来说,结果就是非直观的了。

补充说明

根据上面的总结,也能解决为什么a<b和a-b<0不等价?
这是因为a<b可能引发b与a的隐式转换,即当a和b中一个为有符号类型,一个为无符号类型时,就会发现隐式转换,这种转换容易引起非直观的错误。如unsigned int a=1;int b=-1,此时a<b实际上是返回true的,而a-b<0则返回false;

另一方面,a-b<0虽然也可能引发隐式转换,但是由于是减法运算,因此一般不会对结果造成影响,它最主要的问题是由于需要作减法运算,因此可能引发数据溢出的问题,比如说int a=-2147483648;int b=100;那么a<b肯定是返回true的,但是a-b实际上就溢出了,最终的a-b就成了一个正数,a-b<0也就返回false了。即使a和b都是无符号数,unsigned int a=1,b=2; 那么a<b肯定返回true的,但是a-b为负数就溢出了,但是a-b依旧是个正数,因此a-b<0也就返回false了。

  • 34
    点赞
  • 181
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值