小数与浮点数
本章为进阶内容,零基础读者可以跳过,不影响后续阅读。
复习
- 第三章:了解了二进制,以及怎样用位权展开一个数
TL;DR
- 小数点右边也遵循“逢二进一“,只是位权变成了 1/2、1/4、1/8……
- 小数点位置固定不动的叫定点数,简单但范围小
- 浮点数用“科学计数法“的思想:符号 + 阶码 + 尾数,范围大但精度有限
- 很多十进制小数(比如 0.1)在二进制里是无限循环的,只能近似存储
正文
二进制里的小数
前面我们表示的都是整数。小数点右边怎么办?
回忆一下位权:十进制里,0.1 表示 1/10,0.01 表示 1/100。二进制也一样,只是底数换成 2:
0.12 = 1/2 = 0.5100.012 = 1/4 = 0.25100.112 = 1/2 + 1/4 = 0.7510
所以二进制小数同样可以“按位权相加“来还原。
定点数
最简单的小数表示叫定点数:约定小数点固定在某个位置,小数点前后各占多少位是定死的。
- 比如约定“高 8 位是整数部分,低 8 位是小数部分“
它简单、快,但缺点明显:能表示的范围和精度都很有限。想要更大范围,就得多给整数位;想要更精细,就得多给小数位,而总位数是固定的,两边打架。
浮点数:用科学计数法
科学家记很大的数或很小的数时,会用科学计数法,比如:
1.23 × 10^5
它把“数值“拆成三部分:正负号、有效数字(1.23)、指数(5)。这样,同一个表示法既能记下 1.23 × 10^5,也能记 1.23 × 10^-5,范围一下子大了很多。
计算机借用了这个思路,做成浮点数:小数点不再固定,而是“浮动“的。
- 符号位:正还是负
- 阶码:相当于指数,决定小数点的位置
- 尾数:相当于有效数字,决定精度
现代计算机普遍遵循 IEEE 754 标准来分配这几位。具体位数分配属于细节,这里不展开,只要记住“符号 + 阶码 + 尾数“这个结构即可。
精度问题
浮点数换来大范围,代价是精度。
一个让人抓狂的例子:在大多数编程语言里,
0.1 + 0.2 == 0.3 // 结果是 false!
为什么会这样?因为 0.1 在二进制里是无限循环小数(就像十进制的 1/3 = 0.333… 永远写不完)。计算机只能用有限的位去近似它,两个近似值相加,结果就差了那么一点点,不等于精确的 0.3。
这不是计算机算错了,而是“用有限位表示无限小数“的必然结果。所以,涉及钱、精确数值时,程序员通常改用整数或专门的十进制类型,而不是浮点数。
思考题
十进制里,1/3 = 0.333… 写不完,所以只能近似。请类比说明:为什么 0.1 在二进制里也“写不完“?这和浮点数算不准有什么关系?
小结
知识点
- 二进制小数与位权
- 定点数及其局限
- 浮点数:符号 + 阶码 + 尾数
- IEEE 754
- 浮点数的精度问题
参考资料
- Wikipedia(zh):浮点数:浮点数的基本概念
- Wikipedia(zh):IEEE 754:浮点数标准
思考题答案(仅供参考)
十进制里分母含 3 的分数写不完,是因为 10 的因子是 2 和 5,除不尽。同理,二进制只认因子 2,而 0.110 = 1/10 的分母含着因子 5,用二进制除不尽,所以是无限循环小数。计算机只能用有限多位去截断、近似它,于是 0.1、0.2 都不是精确值,相加自然就凑不出精确的 0.3。根本原因:进制和分母“对不上“。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪