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

简介: 为什么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。


1668568180442.jpg


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


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


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


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


1668568201197.jpg


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


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


1668568257896.jpg


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


补码终于要闪亮登场了。


1668568265573.jpg


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


1668568272333.jpg


在用补码运算的结果中,[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。

相关文章
|
SQL HIVE
hive中的 lateral view
hive中的 lateral view
302 0
解决办法:defined but not used [-Werror=unused-variable]
解决办法:defined but not used [-Werror=unused-variable]
2435 0
|
机器学习/深度学习 PyTorch 算法框架/工具
详解三种常用标准化Batch Norm & Layer Norm & RMSNorm
通过本文的介绍,希望您能够深入理解Batch Norm、Layer Norm和RMSNorm的原理和实现,并在实际应用中灵活选择和使用,提升深度学习模型的性能和稳定性。
2959 5
|
10月前
|
Java C# C++
如何简单地理解Python中的if __name__ == '__main__'
本文介绍了Python中`__name__ == '__main__'`的作用和原理,解释了它如何作为程序入口控制代码执行。当.py文件直接运行时,`if __name__ == '__main__'`下的代码块会被执行;而当文件作为模块被导入时,该代码块不会执行。此外,文章还探讨了`__name__`变量在包结构中的作用,以及`__main__.py`文件与`python -m`命令的关系,详细说明了不同运行方式对模块路径的影响。
818 18
Python编程实战:如何将列表组装成一棵树结构
本文介绍了如何在Python中将列表转换为树结构。首先定义`TreeNode`类表示节点,包含值和子节点列表。然后,通过`list_to_tree`函数递归地将列表转为树。此外,还提供了添加和删除节点的方法。文章旨在帮助读者理解和操作树结构,以解决实际编程问题。
Python编程实战:如何将列表组装成一棵树结构
|
机器学习/深度学习 存储 人工智能
【AI系统】流水并行
在大模型训练中,单个设备难以满足计算和存储需求,分布式训练成为必要。模型并行是其中关键技术之一,通过将模型计算任务拆分至不同设备上执行,提高训练效率。模型并行主要包括朴素模型并行、张量并行和流水线并行。流水线并行通过将模型的不同层分配到不同设备上,采用微批次处理,提高设备利用率。Gpipe和PipeDream是两种流行的流水线并行方案,前者通过重叠前向和反向传播提升效率,后者则通过1F1B策略实现交错执行,最大化利用计算资源。
505 15
|
编译器 C语言
C语言中的浮点数:深入探索与应用
C语言中的浮点数:深入探索与应用
2419 1
|
网络协议 Shell Linux
推荐一款开源跨平台的超级终端工具-windterm
一款风格独特、开源、且完全免费的SSH/Telnet/Serial/Shell/Sftp 客户端
推荐一款开源跨平台的超级终端工具-windterm
|
存储 Go 开发者
在Go中对切片/数组求和的多种方法
在Go中对切片/数组求和的多种方法
|
存储 安全 算法
【C++ 包装器类 std::atomic 】全面入门指南:深入理解并掌握C++ std::atomic 原子操作 的实用技巧与应用
【C++ 包装器类 std::atomic 】全面入门指南:深入理解并掌握C++ std::atomic 原子操作 的实用技巧与应用
1499 1