8位有符号数的范围为-128 ~ +127 深入浅出探究
8位有符号数的范围为-128 ~ +127,为什么不是 -127 ~ 127,-127 ~ 128等等?
这是一个困惑了我几年的问题,它让我对现在的教科书和老师极其不满!
从我二三十年前开始摸电脑时,就几乎在每一本计算机基础、C语言、C++教科书上都说,8位有符号的取值范围是-128~+127,为什么不是-127~+127呢,后来的java int的聚值范围,在32位计算,-2^31 ~ +2^31-1,可是,却从来没有任何一本教科书或一个老师比我解释过这个问题。 后来在工作上或者是什么地方又没有直接遇到它,所以我也一直忽略它,但心里总是有一根刺,直到刚才,拔出来了!几经周折,终于把它搞清楚了:!
其实,-128不是规定的,它是计算机底层为了实现数值运算而决定的,涉及非常非常基础的原码,反码,补码知识,一般(99.9999%)都不会用得上. 那0.0001%,估计也就是计算机考试了
用最高位表示符号,0为+,1为-,那么,正常的理解就是 -127 至 +127 了.
于是乎,反码产生了,原因....略,反正,没过多久,反码就成为了过滤产物,也就是后来补码出现了.
补码( two's complement representation )
计算机中的有符号数有三种表示方法,即 原码、反码、补码
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理,用加法来替代减法。
整型数据在内存中的存放,是以补码表示的:
1)正数的补码,反码,都与其原码相同
2)负数的补码,将该数的绝对值得二进制形式按位取反再加1
-5 = 1000 0101[原] = 1111 1010[反] + 1 = 1111 1011[补] (计算机内存的都是补码)
根据上面的理论,-5的补码就是256-5=251[1111 1011],但问题是我们正在解决减法问题,现在计算机还不会做减法,计算机怎么得到[1111 1011],
于是先辈们发明了一种算法,先得出原码[1000 0101],然后符号位不变,其余位取反(逻辑非),得到反码[1111 1010],再加1,得到了[1111 1011]。就是说,原反补码可以跳过计算直接得出2进制码。
-128 和 +127
原因呢?没有一本书上有说,这也是我这么火的原因,但通过思考,google,再思考,很快找到答案:
正数,原码跟补码一样
+127, 0111 1111
+126, 0111 1110
+125, 0111 1101
+124, 0111 1100
+123, 0111 1011
+122, 0111 1010
...
下面是负数了,值,原码,符号位不变其它取反,+1
-10, 1000 1010, 1111 0101, 1111 0110
-11, 1000 1011, 1111 0100, 1111 0101
-12, 1000 1100, 1111 0011, 1111 0100
-13, 1000 1101, 1111 0010, 1111 0011
-14, 1000 1110, 1111 0001, 1111 0010
-15, 1000 1111, 1111 0000, 1111 0001
-16, 1001 0000, 1110 1111, 1111 0000
-17, 1001 0001, 1110 1110, 1110 1111
...
-24, 1001 1000, 1110 0111, 1110 1000
...
-99, 1110 0011, 1001 1100, 1110 0100
...
-124, 1111 1100, 1000 0011, 1111 1101
-125, 1111 1101, 1000 0010, 1000 0011
-126, 1111 1110, 1000 0001, 1000 0010
-127, 1111 1111, 1000 0000, 1000 0001
看出点什么了没有?
-128, 1111 1111, 1000 0000, 1000 0000
当然是
-128, 先略过,再略过, -128 = 1000 0000 ?
1000 0000,那么,它的原码是什么呢?
从补码求原码的方法跟原码求补码是一样的
先保留符号位其它求反:
对,用8位数的原码在这里已经无法表示了,最高位溢出,结果为:
-128 = 1 1000 0000[原] = 1 1111 1111[反] = 1 1000 0000[补] = -128
关键就在这里,补码 1000 0000 为 -128 是不用怀疑的(上面的穷举),
那么,回到原码处, -128 的原码和补码,二者都是 1000 0000(超出的自动丢失),
1000 0000 在原码表示什么呢? -0, 但补码却规定0没有正负之分
转换一下思路,看看计算机里,是怎么运算的:
对于负数,先取绝对值,然后求反,加1
-128 -> 128 -> 1000 0000 -> 0111 1111 -> 1000 0000
现在明确了吧.
所以, 8位有符号的整数取值范围的补码表示
1000 0000 到 0000 0000, 再到 0111 1111 即 -128 到 0, 再到 127 ,最终 -128 ~ +127
数值 , 补码(计算机真实存储的二进制是补码)
127 , 0111 1111
.....
5 , 0000 0101
4 , 0000 0100
3 , 0000 0011
2 , 0000 0010
1 , 0000 0001
0 , 0000 0000
-128 , 1000 0000
-127 , 1000 0001
-126 , 1000 0010
....
-3 , 1111 1101
-2 , 1111 1110
-1 , 1111 1111
补充说明
“一个n位有符号int型数值,其范围为-2^(n-1) ——2^(n-1) -1”,对于这个问题,很多人都是困惑不已。
其实,导致此情况的根本原因是“人们解决问题时,习惯以人的思维思考问题,但是,计算机本身确实以机器的思维进行处理的”。在这里,就表现为“计算机对数据的处理其实是以‘补码’的形式,而非日常生活中人们进行数学运算所采用的‘原码’的形式”,但是,人们在对“此数值范围”进行处理的时候,却习惯性的采用了“原码作为机器码”。
在历史上,针对“数值”计算,计算机先后采用过3种机器码——原码、反码、补码。
具体表示如下:
1.
2.
3.
其中,符号位“0为+,1为-”。
因为,计算机为数据类型分配了n位,超过n位的数值会被自动舍弃,那么, 就可以发现,现在计算机系统中采用的补码,克服了“原码中存在+0和-0”的情况,仅表现为一个0,(对于8位数据类型,即为8个0,具体推导见转载内容的穷举,-128计算补码时,产生的9位数据11000 0000--补码1000 0000)。
对应而言,8位有符号位数值的范围就成为了“-2^(8-1) ~ 2^(8-1) -1”,即“-128 ~ +127”。
问题的关键就在这里了,“计算机为有符号int型数值分配固定的位数n存储数据,当数据位数大于n时,大于n的位数被自动舍弃”。这就是导致数值范围为“-2^(n-1) ~ 2^(n-1) -1”的原因,从而,也导致了数据范围中“模”概念的产生。
原码、反码、补码
补码并不是必须从原码、反码推导出来的编码,只是恰好数值上有取反加一的对应关系。
补码是一种有利计算机实现减法的编码方案,和反码仅有部分的数值对应关系,并不是有原码、反码才能计算补码。
补码的意义在于:按照这样一种编码规则(也就是表示正数、负数和零的约定),我们就可以把减法运算变成正数加负数。
1、原码
机器数的一种简单的表示法,其符号位用0表示正号,用1表示负号,数值一般用二进制形式表示。
2、反码
可由原码得到。如果机器数是正数,则该机器数的反码与原码一样;
如果机器数是负数,则该机器数的反码是对它的原码(符号位除外)各位取反而得到的。
3、补码
可由反码直接得到,由原码间接得到。如果机器数是正数,则该机器数的补码与原码一样;如果机器数是负数,则该机器数的补码是对它的原码(除符号位外)各位取反,并在未位加1而得到的。
“除符号位外”的含义是,在整个运算过程中符号位始终不变,哪怕补码除符号位外全部是1,在末位加1以后,最高位溢出。
负数 = [ 1 + 非符号位 ]原 = [ 1 + 非符号位取反 ]反 = [反码+1]补 还是说 [ 1 + 反码非符号位+1 ]补
是否对于1个负数无论是补码还是反码,其符号位都是1 ?
128的原码是?
0的原码是 00000000, 127的原码是0111 1111, -1 的原码是 1 000 0001 , -2 的原码是 1000 0010 (形式上是-1的原码加1), -127的原码是 1 111 1111 (形式上是-126的原码加1);
-128的补码是10000000 这是怎么计算出来的? -128的源码要如何表示?
0的原码是 00000000(这个基本是肯定的), -128的原码是 1000 0000 ?
拓展知识
1. 模
例如,虽然时钟的模=12,但是在时钟的指针并不能真正指向“12点”,“12点”的位置和“0点”是重合的,即12点以0点表示。
换句话说,时钟的范围“0 ~ 11”,则模为12。
n位无符号数值的计量范围0 ~ 2^(n)-1,模=2^(n);
n位有符号数值,数值范围-2^(n-1) ~ 2^(n-1) -1,则模为2^(n-1)
举例说明,8位无符号数值,二进制模为2^8;8位有符号数值,表示的数值范围为0 ~ 2^8-1
2、补码的数值轴
以补码定义式为基础,沿数轴列出典型的真值、原码与补码表示,可清楚了解补码的有关性质
真值、原码、补码的关系,如下图:
(1) 在补码表示中,最高位X0 (符号位)表示数的正负,在形式上与原码相同,即 0正数,1负数。但补码的符号位是数值的一部分,由补码定义式计算而得。例如,负小数补码中X0为 1,这个 1是真值(负)加模 2后产生
(2) 在补码表示中,数 0只有一种表示,[+0]补 =[-0]补 =0.000……0
(3) 负数补码表示的范围比原码稍宽,多一种数码组合。对于定点数,若为纯小数,表示范围为:-1 ~ 1-2-n,若为纯整数,表示范围为:-2n ~ 2n-1
补码的意义
补码“模”概念的引入、负数补码的实质、以及补码和真值之间的关系所揭示的补码符号位所具有的数学特征,无不体现了补码在计算机中表示数值型数据的优势,和原码、反码等相比可表现在如下方面
(1) 解决了符号的表示的问题,方便机器码表达与计算,统一了计算
(2) 可以将减法运算转化为补码的加法运算来实现,克服了原码加减法运算繁杂的弊端,可有效简化运算器的设计
(3) 在计算机中,利用电子器件的特点实现补码和真值、原码之间的相互转换,非常容易
(4) 补码表示统一了符号位和数值位,使得符号位可以和数值位一起直接参与运算,这也为后面设计乘法器除法器等运算器件提供了极大的方便。
总之,补码概念的引入和当时运算器设计的背景不无关系,从设计者角度,既要考虑表示的数的类型(小数、整数、实数和复数)、数值范围和精确度,又要考虑数据存储和处理所需要的硬件代价。因此,使用补码来表示机器数并得到广泛的应用,也就不难理解了。
3. 补码的运算规则
[X+Y]补 = [X]补 + [Y]补
[X-Y]补 = [X]补 - [Y]补 = [X]补 + [-Y]补
参考推荐:
Byte 8位的范围为何是-128~127,而不是-127~128
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2020-07-18 05:15:26
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!