为什么int8的范围是[-128,127]

今天这篇文章非常基础。

前几天看到的 go 一道题目,其实和 go 本身并没有多大关系。

func main() {

问b的值是多少?如果直接说 128,那可能还需要再去补补,毕竟 int8 的范围在 『-128,127』。

这道题的正确答案是 -128。那么问题来了:

  • 为什么 int8 的范围是 『-128,127』?

  • 答案为什么是 -128?

在开始之前,我们先来个简单介绍。

一个数在计算机中的二进制表示方式,叫做这个数的机器数。机器数是带符号的,计算机用最高数位存放符号,正数为0,负数为1。

打个比方

  var number int8 = 3

我们定义一个 number 的变量,它的类型是 int8,转换成二进制就是 00000011,如果是 -3,那么二进制就是 10000011。这里的00000011 和 10000011 就是机器数。

因为机器数的第一位是符号位,所以机器数的形式值就不等于真正的数值,比如上面的 10000011 最高位 1 表示负,真正的值是 -3 ,而不是 131(10000011 二进制转为十进制等于131)。所以,为了区分,就把带符号位的机器数真正对应的数值称为机器数的真值。

0000 0001 的真值是 +000 0001 = +1

我们接着去了解原码,反码以及补码。

原码

原码就是符号位加上真值的绝对值。比如下面这个

[+1]= 0000 0001

原码是最容易理解的。因为第一位是符号位,所以 8 位的二进制原码的取值范围是

[11111 1111,0111 1111][-127,127]

反码

正数的反码是它本身,负数的反码是在其原码的基础上,符号位不变,其余各个位取反

[+1] = [0000 0001]= [0000 0001]

这样的话,如果一个反码表示的是负数,你无法直观的看出它的数值,通常需要转化成原码再进行计算。

补码

正数的补码就是它本身。负数的补码是在其原码的基础上,符号位不变,其余位取反,最后 +1。也就是在反码的基础上 +1。

同理,补码表示形式也是人脑无法直观看出数值的,也需要转化成原码再计算其数值。

从上面可以看出,原码,反码和补码是完全不同的,只有原码才是被人脑直接识别并用于计算表达方式的。为什么还需要反码和补码?

对于计算机来说,加减乘除已经是基础的运算了,要设计的尽量简单,计算机识别 “符号位”显然会让计算机的基础电路设计变得复杂。于是想到把符号位也参与到运算中。我们知道,根据运算法则,减去一个数等于加上一个负数。1-1 = 1+(-1) = 0 因此计算机可以只有加法没有减法。

我们先看原码,十进制的表达式:1-1=0

如果用原码表示,让符号位也参与运算,显然对于减法来说,结果不是正确的。这也就是为何计算机内部不使用原码表示一个数。

接着,为了解决原码做减法的问题,出现了反码:

可以发现,如果使用反码计算减法,结果的真值的部分是正确的,但是引发了新的问题,虽然在理解上 +0 和 -0 是一样的,但是 0 带符号是没有任何意义的。而且会有 [0000 0000] 和 [1000 0000] 两个编码表示0。

补码终于要闪亮登场了。

这样,0用[0000 0000] 表示,之前的 -0 问题就不存在了,而且可以用[1000,0000] 表示-128:

在用补码运算的结果中,[1000 0000]补 的值就是 -128。实际上是使用之前的 -0的补码来表示 -128,所以 -128 并没有原码和反码的表示。这也是为什么 int8 使用原码或者反码表示的范围为[-127,127]。使用补码,不仅仅修复了 0 的符号以及存在两个编码的问题,而且还能多表示一个最低数。

好了,我们再回到问题的本身。因为 var b int8 = -128 / a 不是常量表达式,因此 untyped 常量 -128 隐式转换为 int 8类型(和a一样),所以 -128 / a 的结果是 int8 类型,值是 128。超出了 int8 的范围,因为结果不是常量,允许溢出,128的二进制表达式是[1000 0000],正好是 -128 的补码,因此答案是 -128。

参考文章

本作品采用《CC 协议》,转载必须注明作者和本文链接
吴亲库里
讨论数量: 1
Dennis_Ritchie

好久没看到兄弟了

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
46
粉丝
117
喜欢
493
收藏
604
排名:176
访问:5.5 万
私信
所有博文
社区赞助商