Web Notes
2016.08.20
Using Liquid in Jekyll - Live with Demos
Liquid is a simple template language that Jekyll uses to process pages for your site. With Liquid you can output complex contents without additional plugins.
讲真,以前都没有注意过浮点数运算中存在的问题,以为使用浮点数就可以很好解决精度问题,直到遇到下面这段简单的 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 这篇文章深入研读一下,顺便尝试翻译留作笔记。当然,这篇文章是先从定点数开始的……
计算机处理整数和实数是完全不同的,在计算机系统的发展过程中,曾经提出过多种方法表达实数(常见实数如 和 ),浮点数 (Floating Point Number) 和定点数 (Fixed Point Number) 就是其中两种。在定点数表达方式中,小数点固定的位于实数所有数字中间的某个位置。货币的表达就可以使用这种方式,比如 99.00 或者 00.99 可以用于表达具有四位精度 (Precision)、小数点后有两位数的货币值。由于小数点位置固定,所以可以直接用四位数值来表达相应的数值。
定点数表示法的缺点在于其形式过于僵硬,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数。最终,绝大多数现代的计算机系统采纳了所谓的浮点数表达方式。这种表达方式利用科学计数法来表达实数,即用一个尾数 (Mantissa),一个基数 (Base),一个指数 (Exponent) 以及一个表示正负的符号来表达实数。
先来看看实数在我们熟悉的十进制系统下是怎么表示的吧,例如实数 123.45:
小数点之前,10 的幂每一位减小 1,而小数点后也一样,每一位的权重是前一位的 。
与十进制的表示方式类似,在二进制中,将十进制中的底数 10 换作 2 即可表示无符号的二进制数,例如 1101.11:
将其换算为十进制数即是:。
所以,假设我们知道一个二进制数中小数点的位置,就可以很容易得到其表达的真实数值。
当固定二进制数中的小数点的位置,那么该点左侧的每一位则可以通过 来权量 (k 为正),而右侧的每一位同样可用 权量 (n 为负)。那么,一个 16 位的数就可以采用下面的方式表示(注意其中小数点的位置):
通过上面的介绍,我们就可以定义一个无符号的二进制定点数:有 a 个整数位(小数点左边的位数),b 个小数位(小数点右边的位数),则可以使用 U(a,b) 来表示这个数。
例如,一个隐含小数点位于中间的 16 位数就可以用 U(8,8) 表示。
同时,对于一个 N 位的二进制数 U(a,b) 其真实值可通过下式计算:
来看个简单的例子,一个 8 位无符号定点数 U(a,b) = U(4,4),比如说 x = 1011.1111 = 0xBF (hex) = 191d (dec),其真实值便是:
另一种得到真实值的方法就是用其无小数点十进制值 (191d) 除以 。1011.1111 中,b 为 4,所以也可以这样表示:
再如下面这些 16 位无符号数:
对于有符号数,其最高位 (most significant bit, MSB) 的权值是 ,补码则是 。所以处理 N 位有符号数时,我们会单独取一个符号位,然后是 a 个整数位,b 个小数位,用 A(a,b) 来表示。在 U(a,b) 中,N = a+b,而在 A(a,b) 中,N = 1+a+b 。
N 位 A(a,b) 有符号二进制数的真实值可以通过下式表示:
例如下列 16 位有符号定点数 A(7,8):
先大致看一下:
精度 (precision) 有时有不同定义,根据 Randy Yates1 的定义,定点数的精度就是它的字长,例如 A(13,2) 就是 16 位精度(我比较倾向于这种定义)。
而根据 Wikibooks2 上的定义,定点数的精度为小数位的长度,也就是 U(a,b) 和 A(a,b) 中的 b。
值域 (range,暂且用这个词吧) 就是可取最大值与最小值之差。例如 A(13,2) 的取值在 -8192 到 +8191.75 之间,则其值域就是 16383.75。
同样地,U(8,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) 的分辨率为 。这个值也是两个值间的间隔,对 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。它与精度的关系为:
其中的 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 浮点数标准的历史3,有兴趣可以去看看,我暂且不看了,有时间当小说阅读吧。
先来看看常见的一些浮点数(十进制):、、、。
所谓浮点数,也就是那个小数点的位置可以浮动咯,比如说这个:
由于是 10 的指数,小数点可以方便的移动。
由于现代编程语言大多采用 IEEE 标准的浮点数,所以我们这里也主要集中于 IEEE 标准。
正如前面所说,浮点数利用科学计数法来表达实数,即用一个尾数 (Mantissa),一个基数 (Base),一个指数 (Exponent) 以及一个表示正负的符号来表达实数。
对于 IEEE 浮点数,有不同的字长,32 位、64 位、80 位等等,但都可以通过以下形式将一个实数转为 IEEE 浮点数形式:
拿到任意一个二进制表示的实数时,将其转为 IEEE 格式需要
例如数
先将其规范化为:
上面的指数 3 表示了我们将小数点位置向左移动了三位,在二进制中也就是将原来的尾数 1000.100111 除以了 8,因此在指数部分再把 8 乘回来,也就是 。
如果要把上面的数 完全用二进制表示,就要把 2 转换成二进制的 10、3 转换成二进制的 11,最后得到
所以,在计算机世界中,我们只需要存储三部分的信息就可以表示这个实数:
用 32 位表示上面的数 ,各位的分布就是这个样子的:
31 30 23 22 0
+---+----------+-----------------------+
| s | exponent | mantissa |
| 1 | 8 | 23 |
+---+----------+-----------------------+
所以 32 位的 就长这个样子:
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 |
所以,数 的实数指数是 3,然后加上 127 得到 130 (参考上表),转为二进制则为 1000 0010,这就是实际存储的指数部分的值。也就是说 最终的 32 位 IEEE 表示为:
y = 0 10000010 0001001110000000000000
上面说的这个 bias 我也不知道怎么翻译好,但它的用处就是确定指数部分的正负。对于上面的 32 位浮点数来说,这个 bias 就是 127(111 1111),实数指数加上这个 bias 转换为无符号二进制数来表示指数部分。当所存储的指数大于 127 时,这个指数就为正,而小于就为负,等于就为 0。而为什么 IEEE 要这样安排呢?我在 StackOverflow4 上找到一个解答,简要来说就是让这个指数部分更容易区分大小,有兴趣可以去仔细看看(下文也会展开讨论)。
正如上面表格中的 comments,这里有几个特例需要注意。
零值
由于 0 这个数的二进制表示中不存在 1,我们将不能将其规范化,按上面的介绍也不能将其转成 IEEE 格式。所以我们的第一个特例就是这个 0,其 32 位 IEEE 格式表示为:
0.0 = 0 00000000 0000000000000000000000
极小值
按上面表格,当存储的指数部分为 0 时,对应的实数指数为 -126,也就是尾数需要乘以 ,这是一个非常小的值。这种情况下,IEEE 约定尾数部分就不包含隐含的那位 1。
也就是说,当指数部分存储的是 0,而尾数部分又不为 0 的时候(例如 0001000…0),实际的尾数就变成 0.0001000…0,小数点左侧不再为 1,这样就可以用来存储更小的实数了。
举个例子就更清晰了,以 IEEE 浮点数 0 00000000 00100000000000000000000 为例:
极大值
极大值又有两种情况:无穷大和 NaN。
先来看无穷的情况,当存储的指数信息为 255 时,对应的实数指数就是 127,尾数就需要乘以这个最大的 。这种情况下,当尾数为 0 时,IEEE 就规定这个数表示的是无穷大(infinity)。所以当采用浮点数计算并得到的值超过所用位数所能存储的值时,IEEE 就提供了一个特殊值 ‘infinity’ 或 ‘∞’ 来表示。因为符号位可能为 0 可能为 1,所以也就有 ‘+∞’ 和 ‘-∞’:
+∞ = 0 11111111 00000000000000000000000
-∞ = 1 11111111 00000000000000000000000
另外一种情况是 NaN (Not a Number)5。当指数部分存储的是 255,而尾数部分不全为 0 时, IEEE 规范给出的值就是 NaN。之前在很多程序语言的教程中都看到过 NaN,现在终于看到它的庐山真面目了。
下面这段 java 代码来自 StackOverflow.com6,看看 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 | to | to | to |
double precision | to | to | to |
上面这个表格数字比较难记住,如果想简单确认一下可用的值,可记下这个简化的表格:
denormalized | normalized | |
---|---|---|
single precision | ||
double precision |
与定点数不同,浮点数有个指数项,这个指数越大,那么这个浮点数与相邻两数间的差距越大。
下面以一个 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 位浮点数能表示的实数。
另外,这张图表示了更宽范围的浮点数的误差7。
上面其实走神了,没看到这部分就先去查了查为什么要用 bias 而不用 2 的补码来表示指数部分。也好,增加了阅历,这里就来看个例子加深一下印象(直接拷贝过来的,懒):
0.00000005 | = | 0 01100110 10101101011111110010101 |
1 | = | 0 01111111 00000000000000000000000 |
65536.5 | = | 0 10001111 00000000000000001000000 |
这三个数是按大小排列的,观察其指数部分(蓝色),也是从小到大的。也就是说,如果你有两个正浮点数,简单比较一下其指数部分就能直观得到孰大孰小。同样的,两个负浮点数,除开符号位,其指数部分数值越大,这个数越负。甚至可以用来比较正负浮点数的绝对值大小!:-)
到这里我想已经很清楚文章开头的问题了,JavaScript 采用浮点数类型,表示实数的时候自然会存在那样的问题。
题外话:最近看了一个关于自我表达的一个视频,我觉得现在社会就需要有用的自我表达,写博客就是其中一个呀……
Frank Lin
Web Notes
2016.08.20
Liquid is a simple template language that Jekyll uses to process pages for your site. With Liquid you can output complex contents without additional plugins.
JavaScript Notes
2018.12.17
JavaScript is a very function-oriented language. As we know, functions are first class objects and can be easily assigned to variables, passed as arguments, returned from another function invocation, or stored into data structures. A function can access variable outside of it. But what happens when an outer variable changes? Does a function get the most recent value or the one that existed when the function was created? Also, what happens when a function invoked in another place - does it get access to the outer variables of the new place?
JavaScript Notes
2018.03.08
虽然 ES8 已经发布,但是以我的基础来说还是从 ES6 (ECMAScript 2015) 开始,此番就跟着 JavaScript by Example 这本书来学习,随手记点笔记,以期长点记性。