原文链接:What Are Floating-point Numbers?
前言
浮点数是一种以二进制形式存储数字的方法, 它允许我们使用固定数量的存储空间来表示”大范围的值”
为什么要了解浮点数?
了解了浮点数,以下两个问题你就能马上明白
- 为什么 0.1 + 0.2 在某些情况下并不等于 0.3
- 如何用二进制的形式存储小数( non-integer)
5分钟带你了解浮点数
首先,我们来看看二进制是怎么存储整数的。从低到高(右到左)每一位都表示 2 的幂 , 通过按权展开求和,我们能得到想要的整数,示例如下:

二进制表示整数,完美。但是怎么用二进制表示小数呢?比如:2.5
聪明的你肯定想到了,把二进制位分为两部分。左边部分表示小数点前面的数字(示例中的2),右边的部分表示小数点后面的数字(示例中的0.5),于是上图变为如下:

看似解决,但仍然还是有很多小数没法表示,比如: 2.36 就没法表示,最接近的小数是2.375(用计算机术语就是:缺失精度),甚至我们都无法用它表示16.0( 因为左边只有四位,最大值只能表示15)
我们确实可以通过扩展位数用来增加所表示的小数的“大小”和“精度”,但是这样做不够灵活。有时候我们想存储值很大的小数,我们就希望左侧有更多位数;有时候我们想存储尾数(小数点后的数字)很大的小数,就肯定希望右边有更多的位数。那有没有一种方法,能动态满足我们的需求呢?它就是浮点数
浮点数的国际标准是:IEEE 754 ,它定义了32位和64位浮点值;我们用32位浮点值来举例,它的二进制结构如下(从左到右):
- 第一位代表”符号(Sign)”; 0 表示整数,1表示负数
- 接下来的8位代表”指数(Exponent)”
- 最后的23位代表”尾数(Mantissa)”
我们在一个公式中,用到这三个值,公式计算的结果就是实际表示的数字:

你不用理解这个公式的具体运作方式,只要知道这种方式可以让我们更灵活更自由的用二进制表达小数。这也是为什么称之为:”浮点”, 它不像我们上面的示例,小数点在中间,相反它能通过调节指数从而移动小数点的位置,是不是很神奇?
舍入误差
有些分数,其实我们也无法用小数来准确表示。比如: ⅓ = 0.33333333333333…
同理,有些小数二进制也没法精确表示。比如:让我们尝试用二进制表示 0.1,大概如下:

我们可以通过减小指数让它无限接近0.1,但始终没法精确到刚好是0.1。这也是为什么你用chrome浏览器(按F12),在Console 输入: 0.1+0.2 回车,得出的结果是: 0.30000000000000004

因此但是考虑到可能会有这种结果,我们在比较浮点数是否相等的时候,方式2 比较合理:
float result1, result2; #define RoundingValue = 0.0001f //方式1:这种写法非常不推荐 if(result1 == result2) { //do some thing } //方式2:推荐写法 if(Math.abs(result1 - result2) < RoundingValue ) { //do some thing }
拓展链接: