IEEE 754(IEEE Standard for Floating-Point Arithmetic,即IEEE二进制浮点数算数标准),定义了表示浮点数的格式(包括-0)与反常值(denormal number),无穷Infinity与非数值(NaN,Not a Number)等数值表示规则和方法 。
二进制浮点数表示法

JS使用IEEE 754双精度64位二进制格式来表示数值类型 。在IEEE 754标准中,一个双精度64位数字包含以下几个部分(从左往右):
sign(符号位,占用1bit),表示这个数是正/负数,0表示正数;1表示负数
exponent(指数位,占用11bit),表示将这个浮点数以科学计数法形式展示的指数
mantissa(有效数字位,占用52bit),表示这个浮点数以科学计数法形式展示的底数
二进制、十进制互相转换
回顾一下,二进制和十进制互相转换的方法:
二进制转十进制:例如一个二进制整数:01101101,转换为十进制的运算过程如下:
0×27+1×26+1×25+0×24+1×23+1×22+0×21+1×20=109
得到 (1101101)2=(109)10。
十进制转二进制:例如十进制数109,转换为二进制的运算过程如下:
109÷254÷227÷213÷26÷23÷21÷2=54=27=13=6=3=1=0余 1余 0余 1余 1余 0余 1余 1
得到 (109)10=(1101101)2。
十进制转二进制:对于小数例如0.8125,使用“乘2取整法”:
0.8125×20.625×20.25×20.5×2=1.625=1.25=0.5=1.0⇒⇒⇒⇒取整 1取整 1取整 0取整 1
得到 (0.8125)10=(0.1101)2
二进制转十进制:对于二进制小数如0.1101:
1×2−1+1×2−2+0×2−3+1×2−4=0.8125
得到 (0.1101)2=(0.8125)10
双精度64位浮点数表示
如果使用IEEE 754标准来表示双精度浮点数数值0.1,可按照如下步骤来手动计算推导:
首先,可以比较简单地知道符号位(Sign)的位的值为0;
S =0
接下来,利用“乘2取整法”,将0.1转换为二进制:
0.1×20.2×20.4×20.8×20.6×20.2×20.4×20.8×20.6×2=0.2=0.4=0.8=1.6=1.2=0.4=0.8=1.6=1.2 ...⇒⇒⇒⇒⇒⇒⇒⇒⇒....取整 0取整 0取整 0取整 1取整 1取整 0取整 0取整 1取整 1
得到 (0.1)10=(0.00011)2,是一个无限循环小数,循环节为 0011。
然后使用科学计数法,将这串二进制小数写成科学计数法形式:
(0.000110011...)2=(1.110011001...)2×2−4
可以知道,指数位为-4,因为指数部分(Exponent)采用“偏移编码”(目的是为了在二进制中能同时表达正指数和负指数,并且避免使用额外的符号位,还可以保留特殊值),所以实际的指数 E =−4+1023(为什么是+1023?因为指数位占用11bit,211=2048,中点是1023)。
故:
E =−4+1023
1019转换为11位的二进制:
(1019)10=(01111111011)2
接下来是有效数字位(mantissa),即二进制科学计数法的底数 (1.110011001...)2,取出小数点后52位(为什么只取小数点后的值?因为在科学计数法下,只保留小数点前1位,而在二进制形式下小数点前的那一位永远会是1,所以可以直接直接忽略)。
所以:
M =1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001
最终,我们得到0.1的双精度64位浮点数每位的值:
| S |
E |
M |
| 0 |
01111111011 |
1001100110011001100110011001100110011001100110011001 |
封装函数doubleToBinaryString和binaryStringToDouble
/**
* 数字转 IEEE 754 双精度二进制字符串
* @param {Number} num 任意数字
*/
function doubleToBinaryString(num) {
const buffer = new ArrayBuffer(8); // 64 位 = 8 字节
const view = new DataView(buffer);
// 把数字写入 buffer(使用大端模式)
view.setFloat64(0, num, false); // false 表示使用 big-endian
let binaryStr = "";
for (let i = 0; i < 8; i++) {
const byte = view.getUint8(i);
binaryStr += byte.toString(2).padStart(8, '0');
}
return binaryStr;
}
/**
* IEEE 754 双精度二进制字符串转数字
* @param {String} binaryString 任意表示双精度二进制的字符串
*/
function binaryStringToDouble(binaryString) {
const bstr = binaryString.replaceAll(/[^\d]/g, '') // 仅保留数字
if (bstr.length !== 64) {
throw new Error("输入必须是 64 位二进制字符串");
}
// 将每 8 位转成字节,填入 Uint8Array
const bytes = new Uint8Array(8);
for (let i = 0; i < 8; i++) {
const byteStr = bstr.slice(i * 8, (i + 1) * 8);
bytes[i] = parseInt(byteStr, 2);
}
// 用 DataView 读取 float64(双精度)数字
const view = new DataView(bytes.buffer);
return view.getFloat64(0, false); // false 表示 big-endian(高位在前)
}
浮点数的基础运算原理
此处,以0.1 + 0.2为例,介绍一下双精度数值两者+运算的运算过程:
首先,使用封装好的doubleToBinaryString函数,得到两者的IEEE 754的结构:
| Number |
S |
E |
M |
| 0.1 |
0 |
011 1111 1011 |
1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 |
| 0.2 |
0 |
011 1111 1100 |
1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 |
然后,对齐指数(E):
0.1的指数位为011 1111 1011,(01111111011)2=(1019)10,故,指数为 1019−1023=−4
继续阅读…