byte占8个字节,最高位为符号位,最大值是[0111111],最小值是[1111111],转成10进制,所以byte取值范围是[-127,127]?
在开始这个问题的时候,我从没想过里面的逻辑跟我之前的认识完全不同,例如我以为先有反码而后有补码,或者补码等于反码加1之类的说法。查询了一些网上的资料大都雷同,但是当我深入了解时,却发现更我想的完全不一样。
计算机是怎样运算的?
计算机只会加法。事实是计算机的减法和乘除都是借用加法和位移来实现的,例如:3 - 5 = 3 - (-5);3 x 5 相当于将3重复加上5次,或许有的处理器有一些位移的运算;而3/5,除法又可以转为减法,如3一直减去5,等于0,余3。但是不管怎么说,计算机必须引入正负号来实现运算。
正数的符号位为0,负数的符号位为1
回到原码:
3 + 5 = 00000011 + 00000101 = 00001000 = 8 这个是对的。
-3 - 5 = -3 + (-5) = 3 + (-5) = 10000011 + 10000101 = 00001000 = 8 这个有点问题,但是问题不大,符号出错。
3 - 5 = 3 + (-5) = 00000011 + 10000101 = 10001000 = -8 出错!
当原码来做正负数加法时,正正相加没有问题;负负相加问题也不大,它的数值方面是正确的,在符号位我们可以强行把符号置为1来解决它;但是一正一负相加时,数值出错。
补码
补码的思想很平易近人
补码的思想来自时钟。
如果当前是三点,那么什么时候时针会指向一点,很简单,把时针往回转两个小时,或者把时针往前转10个小时,因为时钟一圈只有12个小时。
那么我们就可以得出,在一圈12个小时的情况下:3 - 2 = 1 = 3 + 10 = 13,13点在时针上跟1点指的是同一个点,也就是说减2和加10等价,而 2 + 10 刚刚好等于12。
这个12被称为模。在模为12的情况下,-2的运算,统统可以用+10来代替,-2和10则互为补数,也就是说互为补数的两个不同数绝对值之和等于模的值,并且模范围内的数,它的自身也是自身的补数,因为在运算中,你自己当然可以替代自己,虽然没人这么做。
那么在模确定的情况下,我们永远可以找到一个正数代替负数来进行加法运算,但是这里有一个小问题,如下:
3 - 2 = 3 + 10 = 13 ,但是13终究!= 1,但是在12为模的情况下13和1确实是又是同一个点,这里:
- 1 = 13 = 25 = 37 = 1 + 12* n;
12是模,那么假设X<0, X的正补数= (模 + X)(mod 模)
补码的概念由补数而来。
一个正数虽然可以找到它的负补数,但是在计算机运算中没有意义,所以正数的补码就是它自身
一个负数的补码就是它的正补数
到这里,我们终于引出了补码的概念。
对于一个整数x(注意是整数,小数这篇文章不涉及),它的补码的定义:
虽然这是从计算机组成原理书上截取下来的,但是读到这里,应该水到渠成了。
减法运算的解决方案似乎(注意这个似乎!!!)已经找到了,就是找负数的补码代替运算。
实例
以byte数做运算,一个byte等于8bit,得出模为2^(8+1),那么在byte范围内进行减法运算,从之前的补码概念中,很容易就可以实现减法,例如:
3-2 = [00000011] + [10000101] = 3 + (2^(8+1) - 2) = [00000011] + ([1000000000] - [00000010]) =[00000001] = 1;
不要关注溢出的高位,byte8位,高于8位直接舍弃,就像13点,溢出高位12点,结果就等于1点,这是补码的核心
上面的3-2的运算,实际上没有完全解决减法的问题,因为补码的获得如上是通过2^(8+1) - 2,出现了减2的操作
这就尴尬了,补码就是为了避免减法,使用补码来做加法代替减法,但是获取补码的过程中又出现了减法,怎么能不使用减法来获取一个负数的补码呢,像2^(8+1) - 2的结果能通过别的方法拿到吗,反码终于派上用场了!
反码
还是通过byte举例:
假设一个负数 X = 1X1X2X3X4X5X6X7,求他的补码,根据补码的定义得出模 28+1 = 1000000000;
所以: 28+1 + (X)= 1000000000 + 1X1X2X3X4X5X6X7 = 111111111 - 0X1X2X3X4X5X6X7 + 00000001 = 11234567 + 00000001;这一步的计算很重要,可以自己算一下。
Xi 和 i的关系互为0,1,意思是X如果为0,就为1,反之亦然,很容易得出结果,当拿1去减Xi时,当Xi等于1,结果等于0,当Xi等于0,结果就等于1,总是和Xi相反。
这里我们得出了结论,一个负数的补码可以这样获得,即符号位不变,其他位置按位取反,然后再加1.
而我们把符号位不变,其他位置按位取反之后的结果称为反码
反码就是获取补码中间的一个过渡,它既不重要也不关键
到这里我们就可以完全避开减法了。
这里可以总结减法:
A - B = A[补] + (-B)[补] = 结果;这里有一个结果转换,补码加法运算完的结果=>
如果是负数要转成原码,与获取补码的过程相反,就是先减去一,然后符号位不变,其他位按位取反。
如果是正数,因为正数的补码就是自己,所以不用转换
这里就回到开头提出的问题,一个byte数,最大值和最小值的问题,最大值就是01111111,而最小数本该是11111111?
但是由于计算机里存储的和运算的都是补码
那么就存在一个问题 => 在原码中,8位bit所能表示的数来说,有这样两个数:00000000和10000000,表示+0和-0,没有问题,在原码的理解上10000000就是代表-0,在反码的理解上它代表-127,但是计算机理解10000000这个数时,它表示补码,为了不浪费编码位置,因为+0和-0都代表0,所以人为规定了10000000这个数代表-128
我不想创造逻辑去解释为啥补码10000000代表-128,在我看来,这就是人为规定的编码,因为从逻辑上看,一个8位有符号数,补码为10000000的原码还是10000000,原码中这就是-0,为了不浪费,规定它代表-128.
同样,32的int数,10000000 00000000 00000000 00000000 代表 -232,其他位也一样。
总结
没想到竟然是人为规定的结果,但是如果你不懂补码的来历,不懂补码的逻辑,你或许就不知道为啥可以强制规定了。
网友评论