认识
Number
值表示像 37
或 -9.25
这样的浮点数值。
一、存储结构
在JavaScript
的第一个版本中,单个值在栈中占据32
位的存储单元。存储单元分为两个部分:一部分是标记位,另一部分是数据。标记位为001
表示Integer
类型,标记位为010
表示double
类型
二、数值范围
安全整数: JavaScript
能表示并且能够精确算数运算的安全整数范围为[-2^53 -1, 2^53 - 1]
即 [-9007199254740991,9007199254740991]
,获取安全整数的方法如下:
-
Math.pow()计算得出:
Math.pow(2, 53) - 1 // 9007199254740991
-Math.pow(2, 53) - 1 // -9007199254740991 -
Number.MAX、Number.MIN 获取:
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
超出安全的整数: 超出安全的整数,不能表示或者不能精确的算数运算,需要使用BigInt
来计算,如下所示
console.log(9007199254740991 + 2) // 9007199254740992
console.log(BigInt(9007199254740991) + BigInt(2)) // 9007199254740993n
三、精度丢失
问题
为什么 0.1 + 0.2
的计算结果,不等于 0.3
原因
-
对于
Number
类型在计算机内存中通过64
(1+11+52
)位来表示一个数字-
第0位:
1 位
。符号位,0表示正数,1表示负数(s) -
第1位到第11位:
11 位
。储存指数部分(e) -
第12位到第63位:
52 位
。储存小数部分(即有效数字)f 。对于尾数部分(小数部分)而言,在规约形式下第一位默认为1,通常省略不写。所以,JavaScript
提供的有效数字最长为53
个二进制位(64
位浮点的后52
位+被省略的1
位),这也是JS
最大安全数是Number.MAX_SAFE_INTEGER == Math.pow(2,53) - 1
, 而不是Math.pow(2,52) - 1
的原因。
-
-
对于
0.1 + 0.2
, 计算机处理的过程为: 如果要计算0.1 + 0.2
那么计算机会分别把0.1
和0.2
转成二进制,然后进行对阶运算-
二进制转换:
0.1
和0.2
转化为二进制的过程: 浮点数转化为二进制会发生无限循环,由于有尾数位数的限制,需要将后面多余的位截掉, 这样在进制之间的转换中精度已经损失-
问题:
-
为什么
x=0.1
能得到0.1
?答: 标准中规定尾数
f
的固定长度是52
位,再加上省略的一位,这53
位是JS
精度范围。它最大可以表示2^53(9007199254740992)
, 长度是16
,所以可以使用toPrecision(16)
来做精度运算,超过的精度会自动做凑整处理, 这个就是为什么0.1
可以等于0.1
的原因
-
-
-
对阶运算: 由于指数位数不相同,运算时需要对阶运算 这部分也可能产生精度损失。
-
解决方案
精度丢失解决办法就是先将小数转换成整数,然后用整数进行计算得到结果后再转换为小数
解决方案实现
// 判断number是否为一个整数
const isInteger = function (number) {
return Math.floor(number) === number
}
// 四舍五入
const toFixed = function (number, decimalLength = 0) {
var times = Math.pow(10, decimalLength)
var fixed = number * times + 0.5
return parseInt(fixed) / times
}
// 将一个浮点数转成整数,返回整数和倍数
const toInteger = function (floatNumber) {
const numberInfo = { times: 1, number: 0 }
const isNegative = floatNumber < 0
if (isInteger(floatNumber)) {
numberInfo.number = floatNumber
return numberInfo
}
const stringFloatNumber = String(floatNumber)
const dotPosition = stringFloatNumber.indexOf('.')
const length = stringFloatNumber.substr(dotPosition + 1).length
numberInfo.times = Math.pow(10, length)
numberInfo.number = toFixed(Math.abs(floatNumber) * numberInfo.times)
if (isNegative) numberInfo.number = -numberInfo.number
return numberInfo
}
// 加
export const add = function (number1, number2, decimalLength = 0) {
const { number: num1, times: times1 } = toInteger(number1)
const { number: num2, times: times2 } = toInteger(number2)
const maxTimes = Math.max(times1, times2)
let result
if (times1 === times2) result = (num1 + num2) / maxTimes
if (times1 > times2) result = (num1 + num2 * (times1 / times2)) / maxTimes
if (times1 < times2) result = (num1 * (times2 / times1) + num2) / maxTimes
return toFixed(result, decimalLength)
}