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),注意安全整数和整数不是一个概念哦,安全是指值唯一,不会造成精度损失,你超过这个安全整数,然后相加减,都会造成精度损失
浮点数存储公式:
为啥是 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
那有啥区别呢?
最大安全整数有唯一对应的浮点数,最大数则没有
还是用这公式,关于指数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;
}