三亚最新通告文昌最新通告,上海seo网站排名优化公司,国家军事,html中文网页模板目录
3.3 数字编码
3.3.1 原码、反码和补码
3.3.2 浮点数编码 3.3 数字编码 Tip 在本书中#xff0c;标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难#xff0c;可以先跳过#xff0c;等学完必读章节后再单独攻克。 3.3.1 原码、反码和补码
在…目录
3.3 数字编码
3.3.1 原码、反码和补码
3.3.2 浮点数编码 3.3 数字编码 Tip 在本书中标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难可以先跳过等学完必读章节后再单独攻克。 3.3.1 原码、反码和补码
在上一节的表格中我们发现所有整数类型能够表示的负数都比正数多一个例如 byte 的取值范围是 [−128,127] 。这个现象比较反直觉它的内在原因涉及原码、反码、补码的相关知识。
首先需要指出数字是以“补码”的形式存储在计算机中的。在分析这样做的原因之前首先给出三者的定义。
原码我们将数字的二进制表示的最高位视为符号位其中 0 表示正数1 表示负数其余位表示数字的值。反码正数的反码与其原码相同负数的反码是对其原码除符号位外的所有位取反。补码正数的补码与其原码相同负数的补码是在其反码的基础上加 1 。
图 3-4 展示了原码、反码和补码之间的转换方法。 图 3-4 原码、反码与补码之间的相互转换
原码sign-magnitude虽然最直观但存在一些局限性。一方面负数的原码不能直接用于运算。例如在原码下计算 1(−2) 得到的结果是 −3 这显然是不对的。
1(−2)→000000011000001010000011→−3
为了解决此问题计算机引入了反码1s complement。如果我们先将原码转换为反码并在反码下计算 1(−2) 最后将结果从反码转换回原码则可得到正确结果 −1 。
原码原码反码反码反码原码原码原码反码反码反码原码1(−2)→00000001(原码)10000010(原码)00000001(反码)11111101(反码)11111110(反码)10000001(原码)→−1
另一方面数字零的原码有 0 和 −0 两种表示方式。这意味着数字零对应两个不同的二进制编码这可能会带来歧义。比如在条件判断中如果没有区分正零和负零则可能会导致判断结果出错。而如果我们想处理正零和负零歧义则需要引入额外的判断操作这可能会降低计算机的运算效率。
0→00000000−0→10000000
与原码一样反码也存在正负零歧义问题因此计算机进一步引入了补码2s complement。我们先来观察一下负零的原码、反码、补码的转换过程
原码反码补码原码反码补码−0→10000000(原码)11111111(反码)100000000(补码)
在负零的反码基础上加 1 会产生进位但 byte 类型的长度只有 8 位因此溢出到第 9 位的 1 会被舍弃。也就是说负零的补码为 00000000 与正零的补码相同。这意味着在补码表示中只存在一个零正负零歧义从而得到解决。
还剩最后一个疑惑byte 类型的取值范围是 [−128,127] 多出来的一个负数 −128 是如何得到的呢我们注意到区间 [−127,127] 内的所有整数都有对应的原码、反码和补码并且原码和补码之间可以互相转换。
然而补码 10000000 是一个例外它并没有对应的原码。根据转换方法我们得到该补码的原码为 00000000 。这显然是矛盾的因为该原码表示数字 0 它的补码应该是自身。计算机规定这个特殊的补码 10000000 代表 −128 。实际上(−1)(−127) 在补码下的计算结果就是 −128 。
原码原码反码反码补码补码补码原码原码反码反码补码补码补码(−127)(−1)→11111111(原码)10000001(原码)10000000(反码)11111110(反码)10000001(补码)11111111(补码)10000000(补码)→−128
你可能已经发现了上述所有计算都是加法运算。这暗示着一个重要事实计算机内部的硬件电路主要是基于加法运算设计的。这是因为加法运算相对于其他运算比如乘法、除法和减法来说硬件实现起来更简单更容易进行并行化处理运算速度更快。
请注意这并不意味着计算机只能做加法。通过将加法与一些基本逻辑运算结合计算机能够实现各种其他的数学运算。例如计算减法 − 可以转换为计算加法 (−) 计算乘法和除法可以转换为计算多次加法或减法。
现在我们可以总结出计算机使用补码的原因基于补码表示计算机可以用同样的电路和操作来处理正数和负数的加法不需要设计特殊的硬件电路来处理减法并且无须特别处理正负零的歧义问题。这大大简化了硬件设计提高了运算效率。
补码的设计非常精妙因篇幅关系我们就先介绍到这里建议有兴趣的读者进一步深入了解。
3.3.2 浮点数编码
细心的你可能会发现int 和 float 长度相同都是 4 字节 但为什么 float 的取值范围远大于 int 这非常反直觉因为按理说 float 需要表示小数取值范围应该变小才对。
实际上这是因为浮点数 float 采用了不同的表示方式。记一个 32 比特长度的二进制数为
313029…210
根据 IEEE 754 标准32-bit 长度的 float 由以下三个部分构成。
符号位 S 占 1 位 对应 31 。指数位 E 占 8 位 对应 3029…23 。分数位 N 占 23 位 对应 2221…0 。
二进制数 float 对应值的计算方法为
val(−1)31×2(3029…23)2−127×(1.2221…0)2
转化到十进制下的计算公式为
val(−1)S×2E−127×(1N)
其中各项的取值范围为
S∈{0,1},E∈{1,2,…,254}(1N)(1∑12323−2−)⊂[1,2−2−23] 图 3-5 IEEE 754 标准下的 float 的计算示例
观察图 3-5 给定一个示例数据 S0 E124 N2−22−30.375 则有 val (−1)0×2124−127×(10.375)0.171875
现在我们可以回答最初的问题float 的表示方式包含指数位导致其取值范围远大于 int 。根据以上计算float 可表示的最大正数为 2254−127×(2−2−23)≈3.4×1038 切换符号位便可得到最小负数。
尽管浮点数 float 扩展了取值范围但其副作用是牺牲了精度。整数类型 int 将全部 32 比特用于表示数字数字是均匀分布的而由于指数位的存在浮点数 float 的数值越大相邻两个数字之间的差值就会趋向越大。
如表 3-2 所示指数位 E0 和 E255 具有特殊含义用于表示零、无穷大、NaN 等。
表 3-2 指数位含义
指数位 E分数位 N0分数位 N≠0计算公式0±0次正规数(−1)S×2−126×(0.N)1,2,…,254正规数正规数(−1)S×2(E−127)×(1.N)255±∞NaN
值得说明的是次正规数显著提升了浮点数的精度。最小正正规数为 2−126 最小正次正规数为 2−126×2−23 。
双精度 double 也采用类似于 float 的表示方法在此不做赘述。