JS中0.1+0.2


js中0.1+0.2的问题(作者:老司机)

一、前提:

  • 0.1或0.2等一些小数,转换为二进制格式时,是一个无限循环的小数,因此最终得到的结果是一个近似值(保留52位小数),在计算过程中,有可能会发生位数的变化,在进行舍取的过程中,导致精度丢失

  • js的浮点数在计算机底层的存储格式为:符号位(1) + 阶码(11)+ 尾数(52)

  • 符号位:0表示正数,1表示负数

  • 阶码:指数+1023(最后转为二进制格式,若不足11位,则在前面补0)

  • 1023称为偏移量:2 ^ (位数-1) - 1,此处js浮点数的阶码位数为11,所以:2 ^ 10 - 1 = 1023

  • 以0.1为例:

    • 科学记数法格式(乘二取整,顺序排序):1.1 00110011 00110011 00110011 00110011 00110011 00110011 010 x 2 ^ -4
    • 二进制格式:0 01111111011 1001100110011001100110011001100110011001100110011010【省略了整数1,作为一个隐藏位】

二、实现 0.1+0.2===0.3

function binaryStrToNum(str) { 
    // 1、获取符号位
    let sign = str.charAt(0);
    // 2、获取阶码(1~11【包含11】)
    let exponent = str.slice(1, 12);
    // 3、获取小数位
    let m = str.slice(12);
    let sum = 0;

    // 注意:在尾数中,计算是从第一位开始的,一共有52位,其实还包含一个隐藏位(上面已经提了),这个隐藏位的计算结果始终是1(计算方式:1 * 2 ^ 0),这个也必须累加到计算结果中
    for (let i=0; i<m.length; i++) {
        let c = m.charAt(i);
        if (c == "1") {
            sum += Math.pow(2, -(i + 1));
        }
    }

    // 4、计算结果,公式为:-1 ^ 符号 * 2 ^ (阶码-1023) * sum
    return Math.pow(-1, +sign) * Math.pow(2, parseInt(exponent, 2) - 1023) * (1 + sum);
}

// 《0.1》
// const r1 = binaryStrToNum("0011111110111001100110011001100110011001100110011001100110011010");
// console.log(r1);

// 《0.2》
// 1.1 00110011 00110011 00110011 00110011 00110011 00110011 010 x 2 ^ -3 = 0 01111111100 1001100110011001100110011001100110011001100110011010
// const r2 = binaryStrToNum("0011111111001001100110011001100110011001100110011001100110011010");
// console.log(r2);

/**
 *《0.1 + 0.2》
 * 先对其进行计算(保留52位,第53位为0时,舍弃;为1时,进1):
 * 0.00011 00110011 00110011 00110011 00110011 00110011 00110011 010 + 0.00110 01100110 01100110 01100110 01100110 01100110 01100110 10
 * = 0.01001 10011001 10011001 10011001 10011001 10011001 10011001 110
 * = 1.001 10011001 10011001 10011001 10011001 10011001 10011001 110 x 2 ^ -2【在此次计算中,尾数变成了53位,因此要进行舍取】
 * = 1.001 10011001 10011001 10011001 10011001 10011001 10011010 0 x 2 ^ -2【取小数52位】
 * 转为二进制格式 =>:0 01111111101 0011001100110011001100110011001100110011001100110100
 */
const r3 = binaryStrToNum("0011111111010011001100110011001100110011001100110011001100110100");

console.log(r3); // 0.30000000000000004
console.log(r3 == (0.1 + 0.2)); // true

  目录