讲真,以前都没有注意过浮点数运算中存在的问题,以为使用浮点数就可以很好解决精度问题,直到遇到下面这段简单的 JavaScript 代码:
var x = 0.3;
var y = 0.2;
var z = 0.1;
console.log((x - y) == (y - z)); \\ => false
console.log((y + z) == x); \\ => false
上面示例代码中的两个 false 输出让我这个新手有点难以接受,而一番搜索之后发现这个问题并不是只在 JavaScript 中存在,只要是采用 IEEE-754 表示浮点数的编程语言都会有这个问题,而几乎所有现代编程语言都采用这个标准。
所以找来 CSC231 An Introduction to Fixed- and Floating-Point Numbers 这篇文章深入研读一下,顺便尝试翻译留作笔记。当然,这篇文章是先从定点数开始的……
定点数
计算机处理整数和实数是完全不同的,在计算机系统的发展过程中,曾经提出过多种方法表达实数(常见实数如 π=3.14159265... 和 e=2.71828...),浮点数 (Floating Point Number) 和定点数 (Fixed Point Number) 就是其中两种。在定点数表达方式中,小数点固定的位于实数所有数字中间的某个位置。货币的表达就可以使用这种方式,比如 99.00 或者 00.99 可以用于表达具有四位精度 (Precision)、小数点后有两位数的货币值。由于小数点位置固定,所以可以直接用四位数值来表达相应的数值。
定点数表示法的缺点在于其形式过于僵硬,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数。最终,绝大多数现代的计算机系统采纳了所谓的浮点数表达方式。这种表达方式利用科学计数法来表达实数,即用一个尾数 (Mantissa),一个基数 (Base),一个指数 (Exponent) 以及一个表示正负的符号来表达实数。
十进制表示
先来看看实数在我们熟悉的十进制系统下是怎么表示的吧,例如实数 123.45:
123.45=1×102+2×101+3×100+4×10−1+5×10−2小数点之前,10 的幂每一位减小 1,而小数点后也一样,每一位的权重是前一位的 101。
二进制数表示
与十进制的表示方式类似,在二进制中,将十进制中的底数 10 换作 2 即可表示无符号的二进制数,例如 1101.11:
1101.11=1×23+1×22+0×21+1×20+1×2−1+1×2−2将其换算为十进制数即是:8+4+1+0.5+0.25=13.75。
所以,假设我们知道一个二进制数中小数点的位置,就可以很容易得到其表达的真实数值。
当固定二进制数中的小数点的位置,那么该点左侧的每一位则可以通过 2k 来权量 (k 为正),而右侧的每一位同样可用 2n 权量 (n 为负)。那么,一个 16 位的数就可以采用下面的方式表示(注意其中小数点的位置):
b7b6b5b4b3b2b1b0.b−1b−2b−3b−4b−5b−6b−7b−8定义
通过上面的介绍,我们就可以定义一个无符号的二进制定点数:有 a 个整数位(小数点左边的位数),b 个小数位(小数点右边的位数),则可以使用 U(a,b) 来表示这个数。
例如,一个隐含小数点位于中间的 16 位数就可以用 U(8,8) 表示。
同时,对于一个 N 位的二进制数 U(a,b) 其真实值可通过下式计算:
x=(1/2b)n=0∑N−12nxn来看个简单的例子,一个 8 位无符号定点数 U(a,b) = U(4,4),比如说 x = 1011.1111 = 0xBF (hex) = 191d (dec),其真实值便是:
x=1011.1111=8+2+1+0.5+0.25+0.125+0.0625=11.9375另一种得到真实值的方法就是用其无小数点十进制值 (191d) 除以 2b。1011.1111 中,b 为 4,所以也可以这样表示:
x=191/24=191/16=11.9375再如下面这些 16 位无符号数:
- 0000000100000000 = 00000001 . 00000000 = 1d (1 decimal)
- 0000001000000000 = 00000010 . 00000000 = 2d
- 0000001010000000 = 00000010 . 10000000 = 2.5d
有符号定点数
对于有符号数,其最高位 (most significant bit, MSB) 的权值是 2N−1,补码则是 −2N−1。所以处理 N 位有符号数时,我们会单独取一个符号位,然后是 a 个整数位,b 个小数位,用 A(a,b) 来表示。在 U(a,b) 中,N = a+b,而在 A(a,b) 中,N = 1+a+b 。
N 位 A(a,b) 有符号二进制数的真实值可以通过下式表示:
x=(1/2b)[−2N−1xN−1+n=0∑N−22nxn]例如下列 16 位有符号定点数 A(7,8):
- 00000000100000000 = 00000001 . 00000000 = 1d
- 10000000100000000 = 10000001 . 00000000 = -128 + 1 = -127d
- 00000001000000000 = 00000010 . 00000000 = 2d
- 10000001000000000 = 10000010 . 00000000 = -128 + 2 = -126d
- 00000001010000000 = 00000010 . 10000000 = 2.5d
- 10000001010000000 = 10000010 . 10000000 = -128 + 2.5 = -125.5d
特性
先大致看一下:
- 无符号数 U(a,b) 的取值:0⩽x⩽2a−2−b
- 有符号数 A(a,b) 的取值:−2a⩽x⩽2a−2−b
- 两个不同格式的数相加时,需要将小数点位对齐再执行加法
- 两个 A(a,b) 相加结果形如 A(a+1,b),而 U(a,b) 相加结果形如 U(a+1,b)
- U(a,b) 和 U(c,d) 相乘结果形如 U(a+c,b+d)
- A(a,b) 和 A(c,d) 相乘结果形如 A(a+c+1,b+d)
精度
精度 (precision) 有时有不同定义,根据 Randy Yates 的定义,定点数的精度就是它的字长,例如 A(13,2) 就是 16 位精度(我比较倾向于这种定义)。
而根据 Wikibooks 上的定义,定点数的精度为小数位的长度,也就是 U(a,b) 和 A(a,b) 中的 b。
值域
值域 (range,暂且用这个词吧) 就是可取最大值与最小值之差。例如 A(13,2) 的取值在 -8192 到 +8191.75 之间,则其值域就是 16383.75。
同样地,U(8,8) 取值在 2−8 到 28−2−8间,则其值域可以这样表示:
---+-----------+-----------+-----------+-----------+---------- ----------+--------------------
0 2^-8 2.2^-8 3.2^-8 4.2^-8 ... 2^8-2^-8
| |
smallest number representable largest one
分辨率
分辨率 (resolution) 就是可表示的最小非零数的大小,例如 A(13,2) 的分辨率为 1/22=0.25。这个值也是两个值间的间隔,对 U(8,8) 可表示为:
---+-----------+-----------+-----------+-----------+---------- ----------+--------------------
0 2^-8 2.2^-8 3.2^-8 4.2^-8 ... 2^8-2^-8
|<--------->| |<--------->|
resolution resolution
准确度
看到这里我是有点懵*的,都不知道用什么词翻译比较准确了,反正我就这么理解了,以后看到准确翻译再 refresh 吧,自己就是这么弱……
这里的准确度 (accuracy) 呢就是定点数与其表示的真实值之间的最大差值(这样看来像是一个误差的感觉)。例如,A(13,2) 的准确度是 1/8。它与精度的关系为:
Accuracy(F)=Resolution(F)/2其中的 F 表示数据格式。对 U(8,8) 来说,其准确度可表示为:
---+-----------+-----------+-----|-----+-----------+---------- ----------+--------------------
0 2^-8 2.2^-8 | 3.2^-8 4.2^-8 ... 2^8-2^-8
|
| real value we need to represent
<--->
Accuracy is the largest such difference
所以,这个 accuracy 是针对具体要表示的数来说的,是这个实数与计算机能表示的数之间的最大差值。
定点数部分就到这里吧。
浮点数
UC Berkeley 有个网页介绍 IEEE 浮点数标准的历史,有兴趣可以去看看,我暂且不看了,有时间当小说阅读吧。
先来看看常见的一些浮点数(十进制):6.02×1023、−0.000001、1.23456789×10−19、−1.0。
所谓浮点数,也就是那个小数点的位置可以浮动咯,比如说这个:
1.23456789×10−19=12.3456789×10−20=0.000000000000000000123456789×100由于是 10 的指数,小数点可以方便的移动。
IEEE 标准浮点数
由于现代编程语言大多采用 IEEE 标准的浮点数,所以我们这里也主要集中于 IEEE 标准。
格式
正如前面所说,浮点数利用科学计数法来表达实数,即用一个尾数 (Mantissa),一个基数 (Base),一个指数 (Exponent) 以及一个表示正负的符号来表达实数。
对于 IEEE 浮点数,有不同的字长,32 位、64 位、80 位等等,但都可以通过以下形式将一个实数转为 IEEE 浮点数形式:
x=±1.bbbbbb...bbb×2bbb...bb- b(s) 表示各个位
- ± 是符号位,通常由第一位表示,0 标志该数为正,1 标志该数为负
- 1.bbbbbb…bbb 即为尾数
- 2 为基数
- 指数上的 bbb…bb 即为指数
- 这是规范化的数,小数点左侧只有一位
- 由于第一位总是 1,所以计算机中不需要存储这个位,它是一个隐含位
位编码
拿到任意一个二进制表示的实数时,将其转为 IEEE 格式需要
- 先将其规范化
- 然后调整其指数
例如数
y=+1000.100111先将其规范化为:
y=+1.000100111×23上面的指数 3 表示了我们将小数点位置向左移动了三位,在二进制中也就是将原来的尾数 1000.100111 除以了 8,因此在指数部分再把 8 乘回来,也就是 ×23。
如果要把上面的数 y 完全用二进制表示,就要把 2 转换成二进制的 10、3 转换成二进制的 11,最后得到
y=+1.000100111×1011所以,在计算机世界中,我们只需要存储三部分的信息就可以表示这个实数:
- 0 表示符号位 +
- 000100111 表示尾数(小数点左侧的 1 上面已经说过是隐含位,不用存储)
- 然后需要存储指数 11
用 32 位表示上面的数 y,各位的分布就是这个样子的:
31 30 23 22 0
+---+----------+-----------------------+
| s | exponent | mantissa |
| 1 | 8 | 23 |
+---+----------+-----------------------+
- MSB 是符号位,占 1 位
- 接下来的 8 位存储指数信息
- 接下来的 23 位存储尾数
所以 32 位的 y 就长这个样子:
y = 0 bbbbbbbb 0001001110000000000000
哈哈,你可能注意到了上面的指数信息还没有用二进制表示出来。那是因为 IEEE 起草的标准不使用 2 的补码来表示指数部分的正负信息,而是采用一个 bias 来表示,参见下面表格(弄表格其实我内心是拒绝的,markdown 表格好麻烦呀)。
real exponent | stored exponent | comments |
---|
-126 | 0 | special case #1 |
-126 | 1 | |
-125 | 2 | |
-124 | 3 | |
-123 | 4 | |
. | . | |
. | . | |
. | . | |
-1 | 126 | |
0 | 127 | |
1 | 128 | |
2 | 129 | |
3 | 130 | |
. | . | |
. | . | |
. | . | |
127 | 254 | |
128 | 255 | special case #2 |
所以,数 y 的实数指数是 3,然后加上 127 得到 130 (参考上表),转为二进制则为 1000 0010,这就是实际存储的指数部分的值。也就是说 y 最终的 32 位 IEEE 表示为:
y = 0 10000010 0001001110000000000000
上面说的这个 bias 我也不知道怎么翻译好,但它的用处就是确定指数部分的正负。对于上面的 32 位浮点数来说,这个 bias 就是 127(111 1111),实数指数加上这个 bias 转换为无符号二进制数来表示指数部分。当所存储的指数大于 127 时,这个指数就为正,而小于就为负,等于就为 0。而为什么 IEEE 要这样安排呢?我在 StackOverflow 上找到一个解答,简要来说就是让这个指数部分更容易区分大小,有兴趣可以去仔细看看(下文也会展开讨论)。
特例
正如上面表格中的 comments,这里有几个特例需要注意。
零值
由于 0 这个数的二进制表示中不存在 1,我们将不能将其规范化,按上面的介绍也不能将其转成 IEEE 格式。所以我们的第一个特例就是这个 0,其 32 位 IEEE 格式表示为:
0.0 = 0 00000000 0000000000000000000000
极小值
按上面表格,当存储的指数部分为 0 时,对应的实数指数为 -126,也就是尾数需要乘以 2−126,这是一个非常小的值。这种情况下,IEEE 约定尾数部分就不包含隐含的那位 1。
也就是说,当指数部分存储的是 0,而尾数部分又不为 0 的时候(例如 0001000…0),实际的尾数就变成 0.0001000…0,小数点左侧不再为 1,这样就可以用来存储更小的实数了。
举个例子就更清晰了,以 IEEE 浮点数 0 00000000 00100000000000000000000 为例:
- 指数部分为 0,也就是对应的实数指数为 -126
- 尾数部分不再包含隐含的 1,而变为 0.001000…00,对应的十进制数为 0.125
- 这个二进制浮点数对应的实数则为 0.125×2−126=1.4693679e−39
极大值
极大值又有两种情况:无穷大和 NaN。
先来看无穷的情况,当存储的指数信息为 255 时,对应的实数指数就是 127,尾数就需要乘以这个最大的 2127。这种情况下,当尾数为 0 时,IEEE 就规定这个数表示的是无穷大(infinity)。所以当采用浮点数计算并得到的值超过所用位数所能存储的值时,IEEE 就提供了一个特殊值 ‘infinity’ 或 ‘∞’ 来表示。因为符号位可能为 0 可能为 1,所以也就有 ‘+∞’ 和 ‘-∞’:
+∞ = 0 11111111 00000000000000000000000
-∞ = 1 11111111 00000000000000000000000
另外一种情况是 NaN (Not a Number)。当指数部分存储的是 255,而尾数部分不全为 0 时, IEEE 规范给出的值就是 NaN。之前在很多程序语言的教程中都看到过 NaN,现在终于看到它的庐山真面目了。
下面这段 java 代码来自 StackOverflow.com,看看 java 中有些什么情况会得到 NaN:
import java.util.*;
import static java.lang.Double.NaN;
import static java.lang.Double.POSITIVE_INFINITY;
import static java.lang.Double.NEGATIVE_INFINITY;
public class NaN {
public static void main(String args[]) {
double[] allNaNs = {
0D/0D,
POSITIVE_INFINITY / POSITIVE_INFINITY,
POSITIVE_INFINITY / NEGATIVE_INFINITY,
NEGATIVE_INFINITY / POSITIVE_INFINITY,
NEGATIVE_INFINITY / NEGATIVE_INFINITY,
0 * POSITIVE_INFINITY,
0 * NEGATIVE_INFINITY,
Math.pow(1, POSITIVE_INFINITY),
POSITIVE_INFINITY + NEGATIVE_INFINITY,
NEGATIVE_INFINITY + POSITIVE_INFINITY,
POSITIVE_INFINITY - POSITIVE_INFINITY,
NEGATIVE_INFINITY - NEGATIVE_INFINITY,
Math.sqrt(-1),
Math.log(-1),
Math.asin(-2),
Math.acos(+2),
};
System.out.println(Arrays.toString(allNaNs));
// prints "[NaN, NaN...]"
System.out.println(NaN == NaN); // prints "false"
System.out.println(Double.isNaN(NaN)); // prints "true"
}
}
浮点数值域
值域,也就是从 -infinity 到 +infinity 之间的“空间大小”,下表列出了非规范化和规范化单精度(32 位)及双精度(64 位)的值域:
| denormalized | normalized | approximate decimal |
---|
single precision | ±2−149 to (1−2−23)×2−126 | ±2−126 to (2−2−23)×2127 | ±∼10−44.85 to ∼1038.53 |
double precision | ±2−1074 to (1−2−52)×2−1022 | ±2−1022 to (2−2−52)×21023 | ±∼10−323.3 to ∼10308.3 |
上面这个表格数字比较难记住,如果想简单确认一下可用的值,可记下这个简化的表格:
| denormalized | normalized |
---|
single precision | ±(2−2−23)×2127 | ∼±1038.53 |
double precision | ±(2−2−52)×21023 | ∼±10308.25 |
间隔
与定点数不同,浮点数有个指数项,这个指数越大,那么这个浮点数与相邻两数间的差距越大。
下面以一个 8 位浮点数为例,1 位是其符号位,3 位存储指数值(因此能存储的最大指数为 7,bias 为 3),另外 4 位存储尾数。
下面这个表格列出了所有采用这种格式能表示的实数值(哈哈,这次这个表格不用手动输入了,用这个 fakeFloatingPoint.py gist 直接输出 markdown 表格,😁,当然要稍作修改)。
可以看到,随着数值变大,相继两个数的差值也变大,例如 -15.5 和 -15.0,相差 0.5,这两个数之间的其他任何实数,想用这样的 8 位浮点数表示都是无能为力的。而随着数值变小,例如 0 附近的 0.0078125 和 0.015625,差值变得很小,能表示的实数也越多。
real value | byte integer | stored [sign exp mantissa] | floating point |
---|
-inf | 240 | 1 111 0000 | -inf |
-15.5 | 239 | 1 110 1111 | - 1.9375 * 2^ 3 |
-15.0 | 238 | 1 110 1110 | - 1.875 * 2^ 3 |
-14.5 | 237 | 1 110 1101 | - 1.8125 * 2^ 3 |
-14.0 | 236 | 1 110 1100 | - 1.75 * 2^ 3 |
-13.5 | 235 | 1 110 1011 | - 1.6875 * 2^ 3 |
-13.0 | 234 | 1 110 1010 | - 1.625 * 2^ 3 |
-12.5 | 233 | 1 110 1001 | - 1.5625 * 2^ 3 |
-12.0 | 232 | 1 110 1000 | - 1.5 * 2^ 3 |
-11.5 | 231 | 1 110 0111 | - 1.4375 * 2^ 3 |
-11.0 | 230 | 1 110 0110 | - 1.375 * 2^ 3 |
-10.5 | 229 | 1 110 0101 | - 1.3125 * 2^ 3 |
-10.0 | 228 | 1 110 0100 | - 1.25 * 2^ 3 |
-9.5 | 227 | 1 110 0011 | - 1.1875 * 2^ 3 |
-9.0 | 226 | 1 110 0010 | - 1.125 * 2^ 3 |
-8.5 | 225 | 1 110 0001 | - 1.0625 * 2^ 3 |
-8.0 | 224 | 1 110 0000 | - 1.0 * 2^ 3 |
-7.75 | 223 | 1 101 1111 | - 1.9375 * 2^ 2 |
-7.5 | 222 | 1 101 1110 | - 1.875 * 2^ 2 |
-7.25 | 221 | 1 101 1101 | - 1.8125 * 2^ 2 |
-7.0 | 220 | 1 101 1100 | - 1.75 * 2^ 2 |
-6.75 | 219 | 1 101 1011 | - 1.6875 * 2^ 2 |
-6.5 | 218 | 1 101 1010 | - 1.625 * 2^ 2 |
-6.25 | 217 | 1 101 1001 | - 1.5625 * 2^ 2 |
-6.0 | 216 | 1 101 1000 | - 1.5 * 2^ 2 |
-5.75 | 215 | 1 101 0111 | - 1.4375 * 2^ 2 |
-5.5 | 214 | 1 101 0110 | - 1.375 * 2^ 2 |
-5.25 | 213 | 1 101 0101 | - 1.3125 * 2^ 2 |
-5.0 | 212 | 1 101 0100 | - 1.25 * 2^ 2 |
-4.75 | 211 | 1 101 0011 | - 1.1875 * 2^ 2 |
-4.5 | 210 | 1 101 0010 | - 1.125 * 2^ 2 |
-4.25 | 209 | 1 101 0001 | - 1.0625 * 2^ 2 |
-4.0 | 208 | 1 101 0000 | - 1.0 * 2^ 2 |
-3.875 | 207 | 1 100 1111 | - 1.9375 * 2^ 1 |
-3.75 | 206 | 1 100 1110 | - 1.875 * 2^ 1 |
-3.625 | 205 | 1 100 1101 | - 1.8125 * 2^ 1 |
-3.5 | 204 | 1 100 1100 | - 1.75 * 2^ 1 |
-3.375 | 203 | 1 100 1011 | - 1.6875 * 2^ 1 |
-3.25 | 202 | 1 100 1010 | - 1.625 * 2^ 1 |
-3.125 | 201 | 1 100 1001 | - 1.5625 * 2^ 1 |
-3.0 | 200 | 1 100 1000 | - 1.5 * 2^ 1 |
-2.875 | 199 | 1 100 0111 | - 1.4375 * 2^ 1 |
-2.75 | 198 | 1 100 0110 | - 1.375 * 2^ 1 |
-2.625 | 197 | 1 100 0101 | - 1.3125 * 2^ 1 |
-2.5 | 196 | 1 100 0100 | - 1.25 * 2^ 1 |
-2.375 | 195 | 1 100 0011 | - 1.1875 * 2^ 1 |
-2.25 | 194 | 1 100 0010 | - 1.125 * 2^ 1 |
-2.125 | 193 | 1 100 0001 | - 1.0625 * 2^ 1 |
-2.0 | 192 | 1 100 0000 | - 1.0 * 2^ 1 |
-1.9375 | 191 | 1 011 1111 | - 1.9375 * 2^ 0 |
-1.875 | 190 | 1 011 1110 | - 1.875 * 2^ 0 |
-1.8125 | 189 | 1 011 1101 | - 1.8125 * 2^ 0 |
-1.75 | 188 | 1 011 1100 | - 1.75 * 2^ 0 |
-1.6875 | 187 | 1 011 1011 | - 1.6875 * 2^ 0 |
-1.625 | 186 | 1 011 1010 | - 1.625 * 2^ 0 |
-1.5625 | 185 | 1 011 1001 | - 1.5625 * 2^ 0 |
-1.5 | 184 | 1 011 1000 | - 1.5 * 2^ 0 |
-1.4375 | 183 | 1 011 0111 | - 1.4375 * 2^ 0 |
-1.375 | 182 | 1 011 0110 | - 1.375 * 2^ 0 |
-1.3125 | 181 | 1 011 0101 | - 1.3125 * 2^ 0 |
-1.25 | 180 | 1 011 0100 | - 1.25 * 2^ 0 |
-1.1875 | 179 | 1 011 0011 | - 1.1875 * 2^ 0 |
-1.125 | 178 | 1 011 0010 | - 1.125 * 2^ 0 |
-1.0625 | 177 | 1 011 0001 | - 1.0625 * 2^ 0 |
-1.0 | 176 | 1 011 0000 | - 1.0 * 2^ 0 |
-0.96875 | 175 | 1 010 1111 | - 1.9375 * 2^ -1 |
-0.9375 | 174 | 1 010 1110 | - 1.875 * 2^ -1 |
-0.90625 | 173 | 1 010 1101 | - 1.8125 * 2^ -1 |
-0.875 | 172 | 1 010 1100 | - 1.75 * 2^ -1 |
-0.84375 | 171 | 1 010 1011 | - 1.6875 * 2^ -1 |
-0.8125 | 170 | 1 010 1010 | - 1.625 * 2^ -1 |
-0.78125 | 169 | 1 010 1001 | - 1.5625 * 2^ -1 |
-0.75 | 168 | 1 010 1000 | - 1.5 * 2^ -1 |
-0.71875 | 167 | 1 010 0111 | - 1.4375 * 2^ -1 |
-0.6875 | 166 | 1 010 0110 | - 1.375 * 2^ -1 |
-0.65625 | 165 | 1 010 0101 | - 1.3125 * 2^ -1 |
-0.625 | 164 | 1 010 0100 | - 1.25 * 2^ -1 |
-0.59375 | 163 | 1 010 0011 | - 1.1875 * 2^ -1 |
-0.5625 | 162 | 1 010 0010 | - 1.125 * 2^ -1 |
-0.53125 | 161 | 1 010 0001 | - 1.0625 * 2^ -1 |
-0.5 | 160 | 1 010 0000 | - 1.0 * 2^ -1 |
-0.484375 | 159 | 1 001 1111 | - 1.9375 * 2^ -2 |
-0.46875 | 158 | 1 001 1110 | - 1.875 * 2^ -2 |
-0.453125 | 157 | 1 001 1101 | - 1.8125 * 2^ -2 |
-0.4375 | 156 | 1 001 1100 | - 1.75 * 2^ -2 |
-0.421875 | 155 | 1 001 1011 | - 1.6875 * 2^ -2 |
-0.40625 | 154 | 1 001 1010 | - 1.625 * 2^ -2 |
-0.390625 | 153 | 1 001 1001 | - 1.5625 * 2^ -2 |
-0.375 | 152 | 1 001 1000 | - 1.5 * 2^ -2 |
-0.359375 | 151 | 1 001 0111 | - 1.4375 * 2^ -2 |
-0.34375 | 150 | 1 001 0110 | - 1.375 * 2^ -2 |
-0.328125 | 149 | 1 001 0101 | - 1.3125 * 2^ -2 |
-0.3125 | 148 | 1 001 0100 | - 1.25 * 2^ -2 |
-0.296875 | 147 | 1 001 0011 | - 1.1875 * 2^ -2 |
-0.28125 | 146 | 1 001 0010 | - 1.125 * 2^ -2 |
-0.265625 | 145 | 1 001 0001 | - 1.0625 * 2^ -2 |
-0.25 | 144 | 1 001 0000 | - 1.0 * 2^ -2 |
-0.1171875 | 143 | 1 000 1111 | - 0.9375 * 2^ -3 |
-0.109375 | 142 | 1 000 1110 | - 0.875 * 2^ -3 |
-0.1015625 | 141 | 1 000 1101 | - 0.8125 * 2^ -3 |
-0.09375 | 140 | 1 000 1100 | - 0.75 * 2^ -3 |
-0.0859375 | 139 | 1 000 1011 | - 0.6875 * 2^ -3 |
-0.078125 | 138 | 1 000 1010 | - 0.625 * 2^ -3 |
-0.0703125 | 137 | 1 000 1001 | - 0.5625 * 2^ -3 |
-0.0625 | 136 | 1 000 1000 | - 0.5 * 2^ -3 |
-0.0546875 | 135 | 1 000 0111 | - 0.4375 * 2^ -3 |
-0.046875 | 134 | 1 000 0110 | - 0.375 * 2^ -3 |
-0.0390625 | 133 | 1 000 0101 | - 0.3125 * 2^ -3 |
-0.03125 | 132 | 1 000 0100 | - 0.25 * 2^ -3 |
-0.0234375 | 131 | 1 000 0011 | - 0.1875 * 2^ -3 |
-0.015625 | 130 | 1 000 0010 | - 0.125 * 2^ -3 |
-0.0078125 | 129 | 1 000 0001 | - 0.0625 * 2^ -3 |
0.0 | 0 | 0 000 0000 | 0.0 |
0.0078125 | 1 | 0 000 0001 | + 0.0625 * 2^ -3 |
0.015625 | 2 | 0 000 0010 | + 0.125 * 2^ -3 |
0.0234375 | 3 | 0 000 0011 | + 0.1875 * 2^ -3 |
0.03125 | 4 | 0 000 0100 | + 0.25 * 2^ -3 |
0.0390625 | 5 | 0 000 0101 | + 0.3125 * 2^ -3 |
0.046875 | 6 | 0 000 0110 | + 0.375 * 2^ -3 |
0.0546875 | 7 | 0 000 0111 | + 0.4375 * 2^ -3 |
0.0625 | 8 | 0 000 1000 | + 0.5 * 2^ -3 |
0.0703125 | 9 | 0 000 1001 | + 0.5625 * 2^ -3 |
0.078125 | 10 | 0 000 1010 | + 0.625 * 2^ -3 |
0.0859375 | 11 | 0 000 1011 | + 0.6875 * 2^ -3 |
0.09375 | 12 | 0 000 1100 | + 0.75 * 2^ -3 |
0.1015625 | 13 | 0 000 1101 | + 0.8125 * 2^ -3 |
0.109375 | 14 | 0 000 1110 | + 0.875 * 2^ -3 |
0.1171875 | 15 | 0 000 1111 | + 0.9375 * 2^ -3 |
0.25 | 16 | 0 001 0000 | + 1.0 * 2^ -2 |
0.265625 | 17 | 0 001 0001 | + 1.0625 * 2^ -2 |
0.28125 | 18 | 0 001 0010 | + 1.125 * 2^ -2 |
0.296875 | 19 | 0 001 0011 | + 1.1875 * 2^ -2 |
0.3125 | 20 | 0 001 0100 | + 1.25 * 2^ -2 |
0.328125 | 21 | 0 001 0101 | + 1.3125 * 2^ -2 |
0.34375 | 22 | 0 001 0110 | + 1.375 * 2^ -2 |
0.359375 | 23 | 0 001 0111 | + 1.4375 * 2^ -2 |
0.375 | 24 | 0 001 1000 | + 1.5 * 2^ -2 |
0.390625 | 25 | 0 001 1001 | + 1.5625 * 2^ -2 |
0.40625 | 26 | 0 001 1010 | + 1.625 * 2^ -2 |
0.421875 | 27 | 0 001 1011 | + 1.6875 * 2^ -2 |
0.4375 | 28 | 0 001 1100 | + 1.75 * 2^ -2 |
0.453125 | 29 | 0 001 1101 | + 1.8125 * 2^ -2 |
0.46875 | 30 | 0 001 1110 | + 1.875 * 2^ -2 |
0.484375 | 31 | 0 001 1111 | + 1.9375 * 2^ -2 |
0.5 | 32 | 0 010 0000 | + 1.0 * 2^ -1 |
0.53125 | 33 | 0 010 0001 | + 1.0625 * 2^ -1 |
0.5625 | 34 | 0 010 0010 | + 1.125 * 2^ -1 |
0.59375 | 35 | 0 010 0011 | + 1.1875 * 2^ -1 |
0.625 | 36 | 0 010 0100 | + 1.25 * 2^ -1 |
0.65625 | 37 | 0 010 0101 | + 1.3125 * 2^ -1 |
0.6875 | 38 | 0 010 0110 | + 1.375 * 2^ -1 |
0.71875 | 39 | 0 010 0111 | + 1.4375 * 2^ -1 |
0.75 | 40 | 0 010 1000 | + 1.5 * 2^ -1 |
0.78125 | 41 | 0 010 1001 | + 1.5625 * 2^ -1 |
0.8125 | 42 | 0 010 1010 | + 1.625 * 2^ -1 |
0.84375 | 43 | 0 010 1011 | + 1.6875 * 2^ -1 |
0.875 | 44 | 0 010 1100 | + 1.75 * 2^ -1 |
0.90625 | 45 | 0 010 1101 | + 1.8125 * 2^ -1 |
0.9375 | 46 | 0 010 1110 | + 1.875 * 2^ -1 |
0.96875 | 47 | 0 010 1111 | + 1.9375 * 2^ -1 |
1.0 | 48 | 0 011 0000 | + 1.0 * 2^ 0 |
1.0625 | 49 | 0 011 0001 | + 1.0625 * 2^ 0 |
1.125 | 50 | 0 011 0010 | + 1.125 * 2^ 0 |
1.1875 | 51 | 0 011 0011 | + 1.1875 * 2^ 0 |
1.25 | 52 | 0 011 0100 | + 1.25 * 2^ 0 |
1.3125 | 53 | 0 011 0101 | + 1.3125 * 2^ 0 |
1.375 | 54 | 0 011 0110 | + 1.375 * 2^ 0 |
1.4375 | 55 | 0 011 0111 | + 1.4375 * 2^ 0 |
1.5 | 56 | 0 011 1000 | + 1.5 * 2^ 0 |
1.5625 | 57 | 0 011 1001 | + 1.5625 * 2^ 0 |
1.625 | 58 | 0 011 1010 | + 1.625 * 2^ 0 |
1.6875 | 59 | 0 011 1011 | + 1.6875 * 2^ 0 |
1.75 | 60 | 0 011 1100 | + 1.75 * 2^ 0 |
1.8125 | 61 | 0 011 1101 | + 1.8125 * 2^ 0 |
1.875 | 62 | 0 011 1110 | + 1.875 * 2^ 0 |
1.9375 | 63 | 0 011 1111 | + 1.9375 * 2^ 0 |
2.0 | 64 | 0 100 0000 | + 1.0 * 2^ 1 |
2.125 | 65 | 0 100 0001 | + 1.0625 * 2^ 1 |
2.25 | 66 | 0 100 0010 | + 1.125 * 2^ 1 |
2.375 | 67 | 0 100 0011 | + 1.1875 * 2^ 1 |
2.5 | 68 | 0 100 0100 | + 1.25 * 2^ 1 |
2.625 | 69 | 0 100 0101 | + 1.3125 * 2^ 1 |
2.75 | 70 | 0 100 0110 | + 1.375 * 2^ 1 |
2.875 | 71 | 0 100 0111 | + 1.4375 * 2^ 1 |
3.0 | 72 | 0 100 1000 | + 1.5 * 2^ 1 |
3.125 | 73 | 0 100 1001 | + 1.5625 * 2^ 1 |
3.25 | 74 | 0 100 1010 | + 1.625 * 2^ 1 |
3.375 | 75 | 0 100 1011 | + 1.6875 * 2^ 1 |
3.5 | 76 | 0 100 1100 | + 1.75 * 2^ 1 |
3.625 | 77 | 0 100 1101 | + 1.8125 * 2^ 1 |
3.75 | 78 | 0 100 1110 | + 1.875 * 2^ 1 |
3.875 | 79 | 0 100 1111 | + 1.9375 * 2^ 1 |
4.0 | 80 | 0 101 0000 | + 1.0 * 2^ 2 |
4.25 | 81 | 0 101 0001 | + 1.0625 * 2^ 2 |
4.5 | 82 | 0 101 0010 | + 1.125 * 2^ 2 |
4.75 | 83 | 0 101 0011 | + 1.1875 * 2^ 2 |
5.0 | 84 | 0 101 0100 | + 1.25 * 2^ 2 |
5.25 | 85 | 0 101 0101 | + 1.3125 * 2^ 2 |
5.5 | 86 | 0 101 0110 | + 1.375 * 2^ 2 |
5.75 | 87 | 0 101 0111 | + 1.4375 * 2^ 2 |
6.0 | 88 | 0 101 1000 | + 1.5 * 2^ 2 |
6.25 | 89 | 0 101 1001 | + 1.5625 * 2^ 2 |
6.5 | 90 | 0 101 1010 | + 1.625 * 2^ 2 |
6.75 | 91 | 0 101 1011 | + 1.6875 * 2^ 2 |
7.0 | 92 | 0 101 1100 | + 1.75 * 2^ 2 |
7.25 | 93 | 0 101 1101 | + 1.8125 * 2^ 2 |
7.5 | 94 | 0 101 1110 | + 1.875 * 2^ 2 |
7.75 | 95 | 0 101 1111 | + 1.9375 * 2^ 2 |
8.0 | 96 | 0 110 0000 | + 1.0 * 2^ 3 |
8.5 | 97 | 0 110 0001 | + 1.0625 * 2^ 3 |
9.0 | 98 | 0 110 0010 | + 1.125 * 2^ 3 |
9.5 | 99 | 0 110 0011 | + 1.1875 * 2^ 3 |
10.0 | 100 | 0 110 0100 | + 1.25 * 2^ 3 |
10.5 | 101 | 0 110 0101 | + 1.3125 * 2^ 3 |
11.0 | 102 | 0 110 0110 | + 1.375 * 2^ 3 |
11.5 | 103 | 0 110 0111 | + 1.4375 * 2^ 3 |
12.0 | 104 | 0 110 1000 | + 1.5 * 2^ 3 |
12.5 | 105 | 0 110 1001 | + 1.5625 * 2^ 3 |
13.0 | 106 | 0 110 1010 | + 1.625 * 2^ 3 |
13.5 | 107 | 0 110 1011 | + 1.6875 * 2^ 3 |
14.0 | 108 | 0 110 1100 | + 1.75 * 2^ 3 |
14.5 | 109 | 0 110 1101 | + 1.8125 * 2^ 3 |
15.0 | 110 | 0 110 1110 | + 1.875 * 2^ 3 |
15.5 | 111 | 0 110 1111 | + 1.9375 * 2^ 3 |
inf | 112 | 0 111 0000 | + inf |
通过下面这张图也能清楚看到这个 8 位浮点数能表示的实数。

另外,这张图表示了更宽范围的浮点数的误差。

bias
上面其实走神了,没看到这部分就先去查了查为什么要用 bias 而不用 2 的补码来表示指数部分。也好,增加了阅历,这里就来看个例子加深一下印象(直接拷贝过来的,懒):
0.00000005 | = | 0 01100110 10101101011111110010101 |
1 | = | 0 01111111 00000000000000000000000 |
65536.5 | = | 0 10001111 00000000000000001000000 |
这三个数是按大小排列的,观察其指数部分(蓝色),也是从小到大的。也就是说,如果你有两个正浮点数,简单比较一下其指数部分就能直观得到孰大孰小。同样的,两个负浮点数,除开符号位,其指数部分数值越大,这个数越负。甚至可以用来比较正负浮点数的绝对值大小!:-)
到这里我想已经很清楚文章开头的问题了,JavaScript 采用浮点数类型,表示实数的时候自然会存在那样的问题。
题外话:最近看了一个关于自我表达的一个视频,我觉得现在社会就需要有用的自我表达,写博客就是其中一个呀……