有趣且重要的JS知识合集(2)关于浮点数问题

本文深入探讨JavaScript中数值的存储机制,包括最大安全整数和最大数的区别,浮点数精度问题,以及解决大数相加的算法。通过理解指数位、尾数位和浮点数的存储格式,解释了0.1+0.2不等于0.3的原因,并给出了避免精度误差的加法实现。同时,文章还涉及32位浮点数的偏移量计算和大数相加的字符串处理方法。
摘要由CSDN通过智能技术生成

1、JS中最大和最小安全整数为啥是2^53-1和-(2^53 - 1) ?

JS采用的是64位双精度浮点数来表示的,其中 1位符号位,11位阶码位(也称指数位),52位尾数位,而之所以称"安全",是指这些整数在这52位尾数位里都有一一对应的值,那按理来说应该是2^52 - 1 和 -(2^52-1),但是重点来了,计算机存储时,尾数都会默认首位为1,但是在写的时候不会表示出来(double和float的尾数都有这个特征, float 为 1位符号位,8位阶码位,23位尾数位)。

所以这个范围就扩展成2^53-1和-(2^53 - 1),注意安全整数和整数不是一个概念哦,安全是指值唯一,不会造成精度损失,你超过这个安全整数,然后相加减,都会造成精度损失

浮点数存储公式:

99936c83b83daa3516f72ee0b5c5b278.png

 为啥是 E - 1023? 因为阶码位有11位,可表示数位 0 - (2^11 - 1), 也就是 0 - 2047,然后取中1023为中间位,向左为负,向右为正,  那为啥是 M + 1 ?因为尾数在存储时默认首位为1,所以存的时候,应该将首位真值去掉,在存储

2、Number.MAX_VALUE 和 Number.MAX_SAFE_INTEGER

最大数 Number.MAX_VALUE 1.7976931348623157e+308

最大安全整数 Number.MAX_SAFE_INTEGER 9007199254740991

那有啥区别呢?

最大安全整数有唯一对应的浮点数,最大数则没有

99936c83b83daa3516f72ee0b5c5b278.png

还是用这公式,关于指数E的取值范围,其实是 2^0 - 2 ^ 11 - 2,  那为啥要多减1呢,请看下图

 所以 MAX_VALUE  取值为 (2^53 - 1)*2^(2046-1023 - 52) 或者 1.1111111...* 2^(2046-2013)(小数点后52个1),等于 1.7976931348623157e+308

那最小安全整数和最小数呢?

最小安全整数很简单,符号位为1就行了

那最小数呢?我测出了 为 (2.^(-53) + 1)* 2^-1022 *  2^-52,具体为什么,我还在算。

3、为啥 0.1 + 0.2 !== 0.3 ?

js浮点数相加减都会有精度问题,因为都会先将十进制数转成二进制数,然后以二进制形式相加减后,在转成十进制数进行比较

十进制小数转二进制小数, 都是乘以2 ,有整数则赋1,无整数则赋0,

二进制小数转十进制小数,是小数点后每增加一位,就将该位乘以 2^(负第几位)

例: 0b(0.001) = 0 * 2^(-1) + 0 * 2^(-2) + 1 * 2^(-3) = 0.125

0.1 = 0b(0.00011001100110011001100110011001100110011001100110011010)

0.2 = 0b(0.0011001100110011001100110011001100110011001100110011010)

0.1 + 0.2 = 0b(0.0100110011001100110011001100110011001100110011001100111) => 0.30000000000000004

所以 0.1 + 0.2 !== 0.3

4、float精度下(32bit )浮点数偏移为什么是 127 ?

32位下阶码数位8位,可表示256位数,那么映射表如下:

-128 - 0 - 127 (真值)

|         |      |

0 - 128 - 255 (移码值)

可以看到偏移为128,那为啥要改成127呢?

首先256位数相对范围有两种

-127 ~ 128 和 -128 ~ 127

首先去除全 0 和 全 1 的情况 (最小和最大值)

那范围就成 -126 ~ 127 和 -127 ~ 126

反映到32bit上,具体值就为

min=1.175×10^(−38)
max=3.4×10^38

第二种表达范围为:

min=5.877×10^(−39)
max=1.7×10^38

明显第一种范围更合理(何为合理? 因为合理即是 正负区间基本一样大),那么真值范围就该为 -127 ~ 128,那么映射表应该替换成 

-127 - 0 - 128 (真值)

|         |      |

0 - 127 - 255 (移码值)

移码值改成127了,那么这又是如何改成127的呢?这问题我也理解了好一会,可以这么理解,-128 ~ 127,有128位负数,127位正数(0既不是正数也不是负数),所以取中间偏移值时,是 (最大移码数 + 1) / 2 ,即 (255 + 1) / 2 = 128, 那 -127 ~ 128,有127位负数,128位正数,

所以取中间偏移值时,是 (最大移码数 - 1) / 2 ,即 (255 - 1) / 2 = 127。

5、浮点数相加问题

// 1.23456 + 2.345 = 3.57956
function add (a: number, b: number) {
    let a1 = a.toString().split('.')[1].length
    let b1 = b.toString().split('.')[1].length
    let max = Math.pow(10, Math.max(a1, b1))
    return (a * max + b * max) / max   
}

6、超大数相加

const twoBigintSum = (a: string, b: string): string => {
    let i = a.length, j = b.length, res = '', bit = 0;
    while(i > 0 || j > 0) {
        const stra: string = i > 0 ? a.slice(i - 14 > 0 ? i - 14 : 0, i) : '';
        const strb: string = j > 0 ? b.slice(j - 14 > 0 ? j - 14 : 0, j) : '';
        const sum: string = String(Number(stra) + Number(strb));// 求和
        if (sum.length === 15) {
            res = (Number(sum.slice(1)) + bit) + res;
            bit = 1;
        } else {
            res = (Number(sum) + bit) + res;
            bit = 0;
        }
        i -= 14;
        j -= 14;
    }
    return bit === 1 ? 1 + res : res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值