出现的原因
0.1 + 0.2 = 0.30000000000000004
计算机内部转成二进制去计算
十进制整数转成二进制 ,除以2取余数,倒叙输出 十进制小数转成二进制, 乘以2取整数,一直乘以输出结果是整数,正序输出
如 十进制 10 转二进制
10/2 = 5 ....0
5/2 = 2 .....1
2/2 = 1 ....0
结果就是10>1010 ,反过来转换就是1x2^3+0x2^2+1x2^1+0x2^0 = 8+2 = 10
十进制小数0.125 转二进制
0.125*2 = 0.25....0
0.25*2 = 0.5 ...0
0.5*2 = 1 ....1
结果就是0.125->0.001,反过来转换就是 0x2^-1+ 0x^-2 + 1x2^-3= 0.125
而十进制0.1 转换成二进制
而十进制0.2 转换成二进制
进制相加得(对阶运算,如1+1=>10)
0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1111
无限循环,只能取一个近似值,接下来就由IEE 754标准(后面会提到)
转换成10进制
IEE754 标准
根据IEEE 754标准,单精度float类型使用32bit储,其中1位表示符号,8位表示指数,23位表示尾数;双精度double类型使用64bit存储,1位符号位,11位指数位,52位尾数位。
解决方法
乘以10除以10 并不是万能的,比如场景满足不了,数字我是随便举的,我没有不知道有啥规律
(18.9910 + 0.210)/10 // 19.189999999999998
(1.2110-1.110)/10 // 0.10999999999999996
推荐库
number-precision
下面我选一个number-precision,看下到底是怎么写的
其根本原理是挪动小数点,使其变成整数,然后再进行运算
- 加法
function plus(num1,num2){
// 求出两个数字之间小数点最长的长度
const len = Math.max(digitLength(num1),digitLength(num2))
console.log("len",len)
const baseNum = Math.pow(10,len)
return (times(num1,baseNum) + times(num2,baseNum)) / baseNum
}
function times(num,baseNum){
const num1Changed = float2Fixed(num1)
const baseNumChanged = float2Fixed(baseNum)
// 变成整数之后,求出小数点的长度,也就是说小数点移动了多少位,然后除以多少位,
// 举例18.99->1899,移动了两位,1899*100/10^2 = 1899
const len = digitLength(num)
return (num1Changed * baseNumChanged) / Math.pow(10,len)
}
function float2Fixed(num){
// 这个方法很关键,就是将小数点去掉,变成整数运算
return num.toString().replace('.','')
}
function digitLength(num){
const nSplit = num.toString().split('.')
const len = nSplit[1] ? nSplit[1].length : 0
return len
}
console.log(plus(18.99,0.2))
- 减法
1.21-1.1 = 0.10999999999999988
function minus(num1,num2){
// 求出两个数字之间小数点最长的长度
const len = Math.max(digitLength(num1),digitLength(num2))
console.log("len",len)
const baseNum = Math.pow(10,len)
return (times(num1,baseNum) - times(num2,baseNum)) / baseNum
// 最后能正确输出结果
}
- 乘法
0.9*1.1 ->0.9900000000000001
在函数times改动下代码
function times(num1,num2){
const num1Changed = float2Fixed(num1)
const num2Changed = float2Fixed(num2)
// 变成整数之后,求出小数点的长度,也就是说小数点移动了多少位,然后除以多少位,
const len = digitLength(num1) + digitLength(num2)
return (num1Changed * num2Changed) / Math.pow(10,len)
}
- 除法
1.21/1.1 = 1.0999999999999999
function divide(num1, num2) {
const num1Changed = float2Fixed(num1);
const num2Changed = float2Fixed(num2);
return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
}
修正数字
function strip(num){
return +praseFloat(Number(num).toPrecision(15))
}
toPrecision()与toFixed()
let d = 12.1356
d.toFixed(3) -> 2.136
d.toPrecision(3)->2.14
toPrecision() 包括了整数位
toFixed() 只是小数位
整数边界问题
JS支持的整数的有效范围是-2^53~2^53次方如果超出这个值返回的数值会发生混乱
function checkBoundary(num) {
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`);
}
}
其他
java用BigDecimal这个