前言

在看到<深入理解计算机系统>的浮点数时, 第一想法是:

 

零值的比较

很多面试题都会考一道浮点数零值比较的题(一般是单精度, 双精度太长了)

我觉得答案应该是:

这个题的核心在于 float 什么时候缺失精度

这里我没有使用等于, 因为我认为 0.000001f 和 -0.000001f 并不算缺失了精度

(百度上的答案是有等于的, 我很怀疑这个答案, 甚至有人还用的是 0.00001 (눈_눈) )

(而google上我好像没有找到类似的答案, 再根据编译器给我的结果, 我只能如此推断)

下面是我推断的依据:

你觉得上面会打印什么呢? 输出结果是:

这也就是我认为 0.000001 它并未损失精度的原因, 既然未损失, 那么就不能当做 0 值来对待

(再次看不起百度上的解答(눈_눈), 不过... 万一是cas错了呢?)

(损失精度还有更精确的 0.00000055f, 这个数字也被认为是 0.000001)

 

数字的精度取决于有多少位表示

上面看到了6为精度的情况, 他准确表示了0.1 (虽然它把 0.0000006f 当做了0.1...)

我们来看看其他的结果, 比如:

你觉得这次又会输出什么呢?

(输出结果我做了缩减, 不然太长了)

也就是说, 除了

之外, 编译器认为它们都是相等的, 为什么呢?

精度再次缺失(我只能如此猜测), 因为整数的数字过大, 剩下的留给小数的位数不足以达到6位精度

所以这次的精度缩减到了5位, 而因为四舍五入(我只能再次如此猜测 (눈_눈))的关系

(其实说四舍五入有点不对, 应该是: 数字的二进制表示刚好进入了有效的区间)

一些能达到 254.20001 的数字被判段为不等, 而一些 254.19999 的数字又可四舍五入的关系被判断为相等

所以, 整数数字的大小会影响小数的精度 (我感觉我在说废话 (눈_눈)), 而当整数过大时, 比如 0x7fffffffff

所有的小数精度全都会缺失(unsigned float 可能是例外, 不过不影响结论)

下面我又做了一次比较, 我将254换成了126, 输出结果是

emmm... 其实这次的有效精度还是接近5位

不过能够在5位之外, 能判断更多的数字了

(这个数字并未完全达到6位, 也许 0.000005 能判断到, 0.000004 却不能, 就像上面那样)

 

底层到底对我们的代码做了什么

又到了喜闻乐见的看汇编环节 ┑( ̄Д  ̄)┍

它在汇编中的样子:

关键点在于 .LC1 和 .LC2, 他们的数字, 不过一点数字看不出什么, 需要多一些数据

其中 0.1f 和 0.2f 相差 800000

0.1f 和 0.11f 相差 147ae1

emmmm... 想不出来, 或许我该再看看书

嗯 好的, 看完了 ( ̄ˇ ̄)

大概是这样的, 根据不同的位数安排, 计算的结果也有相应的不同

一个浮点数, 1位符号位S, 8位阶码E, 23位小数位M

其中又分为4种情况: 规格, 非规格, NaN(not a number?), 无穷大

(具体的细节请参考书中的介绍)

总之, 我们用书中的算法来检验一下这几个数字

首先 0.1f, 它的数字是 3dcccccd, 它是一个规格化数字

E = 123 - 127 = -4 , M = 5033165 / 8388735 +1

2的E次方 x M = 0.0999994337644472

emmm... 没错, 这是一个非常接近 0.1 的数字

(书中说到了小数的舍入, 简单来说是四舍五入, 同时向偶数舍入, 比如 1.245 它会向 1.24 舍入)

(再次很好奇一些需要极其精确的小数运算是如何做到的 (ー_ー?))

(像存储金额这样的小数精度, 特别是银行, 损失一个精度都很严重啊)

规格化用于表示一些比较大的数字, 而非规格化用于表示一些相对较小的数字

这里顺便再看一下失去精度的结果, 看看他是怎么计算的

奇怪的是, 它有两个数字

可惜计算不出来, 这种格式是无穷大(不太明白 (눈_눈))

 

summary

简单来说, 其实也没有做笔记的必要 ┑( ̄Д  ̄)┍, 书上已经给了你答案

不过还好, 沉浸在思考的海洋中挺不错的(其实都快被淹死了 (눈_눈))

最后, 若无必要, 或者非常确信浮点数的范围, 否则不要使用单精度浮点数

如你所见, 单精度浮点数的范围很小, 一不小心还要失去精度

(这可能也是默认小数是双精度的原因)