8位存储-128引发的思考

转自 http://blog.sina.com.cn/s/blog_9e1f85cd010136b9.html

学习中会遇到一些看似简单的问题,但是问题后面会引发多个疑问,一点一滴的积累才是最重要的;
不能够把简单问题彻底弄清楚,也就成不了高手,问大家两个简单问题:
计算机中为什么采用补码才存储数据?为什么8位二进制可以表示-128?
如果你能够在几秒钟内说出原因,那证明你的看书看的很仔细,如果不能够准确答出,那就要跟着我一起来回顾
刚上大学时学过的知识了,温故而知新这个道理其实很深奥。
原因:
1. 原码的不足:0的二异性,+0的原码 = 00000000(8位来算);-0的原码:10000000;
2. 用原码进行四则运算时,符号位需要单独处理,且运算规则复杂;
3. CPU中,一个最小的运算单元(三极管)就是一个与非门,所有的运算加、减、乘除都要转换成与非运算,
每次与非运算都要是耗时间的,采用补码,进行与非运算的次数会最小,提高运算效率;
使用原码会提高使用寄存器的次数,效率差;
4. 负数采用补码之后,可以使加减统一为加法运算;使符号位能与有效值部分一起参加运算,从而简化运算规则;
正是由于上述原码的不足,促使人们去寻找更好的存储方式:补码。

补码思想的一个好例子:
时钟问题:假如准确时间为6点,现在时间是8点,那么顺时针拨10个小时和逆时针拨3个小时的效果是一样的;
即(8 - 2) = (8 + 10) mod 12;在模运算中,一个数减去另一个数,可以转化为一个数加上另一个数的补数。
注:
正数的原码 = 正数的补码 = 正数的反码
负数的反码,除了最高位符号位不变,其他位取反;
负数的补码,除了最高位符号位不变,其他位取反,并在末位加1;
补码转为为原码:
如果补码为正数,则原码为其本身;
如果补码为负数,则除符号位,各位取反,末位加1;
错误例子:
1 + (-1) = 0;
但是从原码角度来计算:
 0000 0001
+1000 0001
----------
 1000 0010
结果为-2,显然不正确,证明用原码来计算是存在错误的。

其实,我们可以得到8位负数的补码,-1的补码为0xff,-2的补码为0xfe, ... ,-127的补码为1000 0001;

我们发现一个规律:补码随着负增值的变大,反而越来越小,到-127时变为1000 0001,

那么我们可以很快得出-128的补码为:1000 0000;即用-0来表示-128;

负数的补码随着负数的增长而变小这个规律是正确的,因为我们把值还原的话,正是在补码的基础上求反再加1;从而可以得到-1的补码0xff还原之后,除去符号位,变成了除了-0之外,最小的数1;而-128的补码0x00还原之后,得到的原码为:1 0000 0000,除去溢出的符号位,正好为0。

 

举例1(8位计算):
-1 + 3 = 2:
-1原码:1000 0001
-1反码:1111 1110
-1补码:1111 1111
即:0xff
3原码:0000 0011
3补码:0000 0011
-1 + 3表示:
 1111 1111
+0000 0011
-----------
10000 0010
高位溢出,最后结果为0000 0010,转化为原码为正数+2,结果正确。

举例2(8位计算)
-2 - 3 = -5:
-2补码:1111 1110
-3补码:1111 1101
 1111 1110
+1111 1101
-----------
11111 1011
高位溢出,得出结果为:1111 1011,再转化为原码为:10000101,结果为-5,正确。

深度挖掘:
char类型的范围为-128~127,占8位;
8位二进制数的原码和反码表示不了-128,只能表示-127~+127之间的数:
-127: 1111 1111
127: 0111 1111
只有补码才能表示-128:
-127 - 1 = -128
-127补码:1000 0001
-1补码:1111 1111
-127 - 1:
 1000 0001
+1111 1111
-----------
11000 0000
高位舍去得:1000 0000,转化为原码为:1111 1111 + 1 = 1 0000 0000;
因为+0和-0都是0,有重复,所以在八位二进制中就把-0当作最小数-128用;
即1000 0000;

记住:在八位二进制中,为了区分+0和-0的区别,就把-0当作最小数-128用,即1000 0000;
那么-129至少需要9位才能存下来,因为没有一个特定的像-0这种特例来表示它,所以-128很幸运
可以用8位来存储。
-128 - 1 = -129:
 1000 0000
+1111 1111
-----------
10111 1111
高位截取后变成了正数,很奇怪吧?结果为0x7f;

那首次溢出结果为0的数是哪一个呢?
答案是-256.
原因:
-255补码为:1 0000 0001
-1补码为:    1111 1111
相加结果为10 0000 0000,溢出后截断低8位得:0000 0000,原码为0.
很有意思吧?

有 另一种理解方式 我觉得更 靠谱一些。

http://blog.chinaunix.net/uid-16249993-id-2750042.html

今天看到一个问-128的补码为何就是 1000 0000的贴子.
问是如何计算出来?是不是还是按:取相应正数的原码,最高位即符号位,取1,其余各位取反后
整个得值 +1?

按我的理解(上学时学c语言就是这样理解的,老谭那本书上讲整数的取值范围那段就有)
16位int  -32768至32767 -2^(16-1)至 +2^(16-1)-1
:8位二进制的原值表达范围为:-127至127
共有256个组合序列 0000 0000 至1111 1111 。
+128的原值在8位中是表达不出来的。
我个人的理解是:
(1)、
应当从数学层面上去理解:
 

    从数学上计算 256-128=128,  
因为:256-128=256+(-128)的补码 
而    256-128=128 
所以   256+(-128)的补码=128 
所以   (-128)的补码=256-128 
                     =128 
而从数学上, 128=1000 0000 
故规定-128的补码为 1000 0000
所以说,128在8位内存中是表达不出来的。 
不存在+128 8位原码及反码的形式,按常规算法是不对的。

注意:只是规定而已,下面还有原因。
(2)、
 

8位二进制 的补码组合序列有
0000 0000 - 0111 1111 0  -  +127
1000 0000                   用来干啥好呢?(表示-0,不是的,-0的原码是1000 0000 补码是
                                          0000 0000  +0 的补码也是 0000 0000
                                        )
1000 0001-  1111 1111 -1 - -127
全部状态为256个
再看看这个规律表
   原码        补码   值
0111 1111   0111 1111 127
0111 1110   0111 1110 126
......补码不断-1........
0000 0000   0000 0000  0
1000 0001   1111 1111 -1
1000 0010   1111 1110 -2
1000 0011   1111 1101 -3
......补码不断-1........
1111 1111   1000 0001 -127
无法表达     1000 0000 -128
 
 

于是就有了规定 1000 0000 定为 -128的补码
这种定法和上面数学层面的表述是一致的。
这样规定后,负数的补码在机器中就好算了。
在约定的范围内(-128-+127,对16位32位64位等扩大范围)
先将该负数取绝对值,再用二进制表示出这个绝对值 (不管符号位)
对该二进制数进行取反加一操作就得到负数的补码了 
-128 绝对值是 128 
128的二进制表示为: 
1000 0000 
取反 
0111 1111 
加1 
1000 0000 
这就是-128的补码 
这种办法算出的结果符合“规定值”,规定而已。



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值