前面转自别人的博客,为了防止自己忘记,同时也在后面添加了一些自己的理解,原文地址:https://blog.youkuaiyun.com/findsafety/article/details/80539536,侵删。
初学C,问题源自:为什么C中的int类型(16位)的下溢下限为-32768而上溢上限却是32767。
首先说吧,32767很容易理解,32767=2^15-1 (因为要有一个符号位),但为什么下溢的时候分明是15位来表示的数会出现32768呢??
首先从原码讲起,原码即为计算机中对数值的二进制表示,如 5用二进制表示为0000 0101 ;
其次就是反码,反码,顾名思义取反,对于正数来说,反码与原码相同;对于负数来说,反码为原码的各位取反(符号位除外),如(0011 0111)反= 0011 0111 (1101 0010)反= 1010 1101 ;
再次就是补码,计算机中,数值疑虑用补码表示和存储的,正数的补码与原码相同,负数的补码为其反码+1,如(0101 1101)补=0101 1101 (1101 0010)补=1010 1110
由于计算中的CPU只有加法器,没有减法器,所以在计算机采用原码做减法是会存在这样的问题:对于1-1=0
看做1+(-1)=0 二进制表示 0001+1001=1001 变成了十进制的负1而不是0。补码的出现很好的解决了这个问题,由于采用补码运算,则补码加法成为:[X+Y]补 = [X]补 + [Y]补同时,补码的减法变为:[X-Y]补 = [X]补 - [Y]补 = [X]补 + [-Y]补 补码的乘法变为:【X*Y】补=【X】补×【Y】补;在此我们以减法为例说明补码的优势,还以上述为题为例:(0001)补+(1001)补 = 0001 + 1111 = 0000 (最高位的进位省略),这样就顺利得到了0
另外,补码还解决了原码中存在两个0 的问题(即+0 和 -0),以8进制为例,int_8的取值范围应该是-127~ -0和+0~ 127 即存在正0 0000 0000 和 负 0 1000 0000 ,但是在两个0转换为补码后全部都变成了0000 0000,细心的你会说原来8位编码表示的256的二进制数在补码中不就少了一个吗?
对,当然是少了一个,对于1000 0000 ,任何原码都不能转换成补码后成为1000 0000的形式
但数是死的,人是活的,不能让XX憋死不是? 所以人为规定补码中 1000 0000 表示 -128 ,这就很好的解释了,为什么8位的整形变量的下溢下界会是-128而上溢上届是127了。
同样,对于16进制int,也是这样, 表示范围成了 - 32768 ~ 32767 (32768=2^16)
以上转载原文,下面补充:在计算机内存中,数据都是以补码形式存储的
-
原码 ->补码:(1)先看最高为,最高位是0,代表它是正数,则补码和反码都是它本身;(2)最高位也即符号位是1,则它是负数,就需要对原码按位取反再加一得到补码,注意这个按位取反是除开符号位的其他位,比如-0 即[1000 0000] 反码是[1111 1111] 补码[1111 1111]+1=10000 0000 高位1溢出,所以是[0000 0000]
-
补码-> 原码 同样分两步 先看首位是不是0 是就代表是整正数 直接补码等于原码 是一时,再按位取反得到原码
-
总结来说就是计算机中之所以出现补码,是因为cpu只能计算加法,x + -x 按理应该得到0,但是实现不是,所以作运算时,用补码运算解决这个问题。但是这又出现了0 和 -0的补码都一样,这样相当于就浪费了一个资源,八位补码表示中-0对应的1000 0000这个代号是没有一个原码与之对应的,也就是没有一个数的补码是1000 0000 ,就规定补码1000 0000 代表-128 所以8位数表示的范围是(-128 ~ 127)。