Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

二进制浮点数的存储解读 #35

Open
msforest opened this issue Jun 9, 2019 · 0 comments
Open

二进制浮点数的存储解读 #35

msforest opened this issue Jun 9, 2019 · 0 comments

Comments

@msforest
Copy link
Owner

msforest commented Jun 9, 2019

参考:

image

ECMAScript文档写明,数字类型遵循IEEE 754标准的64位双精度格式存储。这种类型用于存储整数和分数,等同于Java和C中的double数据类型。JavaScript的一些新开发人员没有意识到这一点,并且相信如果他们使用1,它将以64位存储为:

0000000000000000000000000000000000000000000000000000000000000001

实际上存储形式是这样的:

0011111111110000000000000000000000000000000000000000000000000000

继续深入,了解为什么存储的格式与想象的不一样。

科学计数法,小学的时候学过,忘的差不多,回顾一下。
根据wiki定义,有效位数取值范围为0<=|significant|<base,因此正确表示为
image

significant表示有效位数,也被称为mantissa或precision。base表示统计的基数。exponent表示小数点向左向右移多少位,也叫指数。

任何数都可以用科学计数法来表示。如十进制和二进制系统中的数2可以表示为:
image

然后看一个小数如何表示:
image

指数大于0,表示小数点右移;指数小于0,表示小数点左移。

科学计数法可以用来表示数字的浮点数。进一步理解为浮点数就是小数点的移位。

IEEE754定义了两种精度存储方式——单(32)精度/双(64)精度
image

Javascript使用64位存储数字,下面是它如何以JavaScript的Number类型使用的双精度格式(每个数字为64位)分配这些位:
image

符号位占1位,指数占11位,52位分配给尾数(有效数字)

1的浮点数

1的科学计数法表示如下:
image

有效位是1,指数是0,可以得出如下表示:
0 00000000000 0000000000000000000000000000000000000000000000000001

然而,我们如何计算实际存储的格式呢?网上搜到一种方法可以计算出浮点数的存储格式:

function to64bitFloat(number) {
    var i, result = "";
    var dv = new DataView(new ArrayBuffer(8));

    dv.setFloat64(0, number, false);

    for (i = 0; i < 8; i++) {
        var bits = dv.getUint8(i).toString(2);
        if (bits.length < 8) {
            bits = new Array(8 - bits.length).fill('0').join("") + bits;
        }
        result += bits;
    }
    return result;
}

打印结果得到:
0 01111111111 0000000000000000000000000000000000000000000000000000
尾数占位符没有数字,指数中存在1。大大的疑问??

二进制中最高有效位始终为1,所以不存储,在执行数学运算时,第一个数字1由硬件前置。数字1在标准化形式的小数点之后没有数字,并且没有存储小数点之前的第一个数字,因此我们没有任何东西可以放入尾数,因此它全部为零。
指数存储是通过位偏移计算,得到的结果:
image

因此就计算出了指数占位符中的数。

3的浮点数

3的二进制是11,科学计数法如下:
image

小数点后只有一个1需要存储,小数点前的1不存储;然后根据指数的位偏移计算指数占位符:
image

关于尾数的一件事要注意,数字按照它们以科学形式放置的确切顺序存储 - 从小数点开始从左到右。考虑到这一点,让我们将所有数字放在浮点表示中:
image

0.1+0.2 !== 0.3

现在再来看看0.1加0.2为什么不等于0.3

0.1和0.2的浮点数

根据二进制转换规则,0.1是一个无线循环的小数:
image

用科学计数法表示如下:
image

由于尾数只能有52位,我们需要在小数点后将无限数舍入为52位。
image
image

指数的计算根据位偏移得出:
image

因此0.1的存储形式如下:
image

同样的方法,0.2的存储形式为:
image

0.1/0.2的科学计数法表示如下:
image

计算两个数相加,需要使得指数相同。调整后的0.1如下:
image

然后两数相加:
image

现在,计算结果以浮点格式存储,因此我们需要对结果进行标准化,必要时进行舍入并计算偏移二进制的指数。
image

当转换为浮点格式进行存储时,它具有以下位模式:
image

这正是执行语句0.1 + 0.2时存储的位模式。为了得到它,计算机必须绕三次 - 每个数字一个,第三次总和。当存储简单的0.3时,计算机仅执行一次舍入。这种舍入操作导致存储0.1 + 0.2和独立0.3的不同位模式。当JavaScript执行比较语句0.1 + 0.2 === 0.3时,它们的这些位模式被比较,并且由于它们不同,返回的结果是假的。

NaN/Infinity

这两个指数是1024,不等于Number.MAX_VALUE(指数是1023)
NaN的存储形式为:
image

Infinity是浮点数的另一个特例,用于处理溢出和一些数学运算,如1/0。无穷大用指数中的所有1和尾数中的全零表示
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant