在这里我们分享WEB前端的相关技术文章、学习资源、热点资讯、面试难点等内容。期待你的建议和指正。 期待和你在前端的世界中一起学习,获得更多成长!
公众号
续上篇 为何 0.1 加 0.2 不等于 0.3 ??? 我们知道了为何0.1+0.2≠0.3,发现了问题,那么这次我们来解决问题。
分析问题
在解决问题之前,我们先来弄清楚问题的根源。
问题的根源,整数是因为数据太大;小数是因为小数在转换成二进制时出现了无效循环的情况,由于存储位数限制因此存在“舍去”,精度丢失就发生了。
详细请看上一篇文章 JavaScript的数字存储。
解决方案
对于整数,前端出现问题的几率可能比较低,毕竟很少有业务需要需要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。
对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)。
例子
计算小数:0.1 + 0.02
计算方案:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)。
计算过程
第一步把小数扩大成整数
0.1 扩大10倍 变成 1
0.02 扩大100倍 变成 2
第二步计算
1 * 100 ÷10 + 2 = 12
第三步缩小倍数(除以最大倍数)
12 ÷ 100 = 0.12
计算过程我们大概已经理清了,现在我们使用代码实现整个过程
代码实现
分析以上三个步骤,我们的代码实现分为两步。
第一步
为通过两个数字获取两个整数值,两个倍数值,以及最大倍数值。
例如:
入参为0.1 和0.02
则返回值应该是0.1的整数值1 ,倍数为10;0.02的整数值2,倍数值100。
代码实现如下
/**
* 判断num是否为一个整数
* @param {number|string|null} value
*/
isInteger = (value) => {
if (isNaN(value)) return false;
const num = +value;
return Math.floor(num) === num;
};
/**
* 将一个浮点数转成整数,返回整数和倍数
* @param {number|string|null} floatNum 如:3.14
* @returns {object} 如:{times:100, num: 314}
*/
toInteger = (floatNum) => {
const ret = { times: 1, num: 0 };
if (this.isInteger(floatNum)) {
ret.num = floatNum;
return ret;
}
const strfi = `${floatNum}`;
const dotPos = strfi.indexOf('.');
const len = strfi.substr(dotPos + 1).length;
const times = 10 ** len;
ret.times = times;
ret.num = +strfi.replace('.', '');
return ret;
};
第二步,使用整数值以及倍数计算结果
代码如下
add = (prevValue, nextValue) => {
let result = null;
//初始化
const objectNumberA = this.toInteger(prevValue);
const objectNumberB = this.toInteger(nextValue);
const numberA = objectNumberA.num;
const numberB = objectNumberB.num;
const timesA = objectNumberA.times;
const timesB = objectNumberB.times;
const timesMax = timesA > timesB ? timesA : timesB;
//计算加法
if (timesA === timesB) {
// 两个小数位长度相同
result = numberA + numberB;
} else if (timesA > timesB) {
// numberA 小数位长度 大于 numberB
result = numberA + numberB * (timesA / timesB);
} else {
// numberA 小数位长度 小于 numberB
result = numberA * (timesB / timesA) + numberB;
}
//缩小并返回值
return result / timesMax;
};
代码测试结果
这样我们就小数加法丢失精度的问题。
同理我们可以实现减法,乘法,除法。具体实现代码入下,建议在有加法的基础上试试自己实现。
减法
subtract = (prevValue, nextValue) => {
let result = null;
//初始化
const objectNumberA = this.toInteger(prevValue);
const objectNumberB = this.toInteger(nextValue);
const numberA = objectNumberA.num;
const numberB = objectNumberB.num;
const timesA = objectNumberA.times;
const timesB = objectNumberB.times;
const timesMax = timesA > timesB ? timesA : timesB;
//计算减法
const { numberA, numberB, timesA, timesB, timesMax } = calcuNumber;
if (timesA === timesB) {
result = numberA - numberB;
} else if (timesA > timesB) {
result = numberA - numberB * (timesA / timesB);
} else {
result = numberA * (timesB / timesA) - numberB;
}
//缩小并输出
return result / timesMax;
};
乘法
multiply = (prevValue, nextValue) => {
//初始化
const objectNumberA = this.toInteger(prevValue);
const objectNumberB = this.toInteger(nextValue);
const numberA = objectNumberA.num;
const numberB = objectNumberB.num;
const timesA = objectNumberA.times;
const timesB = objectNumberB.times;
const timesMax = timesA > timesB ? timesA : timesB;
//计算乘法,缩小并输出
return (numberA * numberB) / (timesA * timesB);
};
除法
divide = (prevValue, nextValue) => {
//初始化
const objectNumberA = this.toInteger(prevValue);
const objectNumberB = this.toInteger(nextValue);
const numberA = objectNumberA.num;
const numberB = objectNumberB.num;
const timesA = objectNumberA.times;
const timesB = objectNumberB.times;
const timesMax = timesA > timesB ? timesA : timesB;
//计算除法,缩小并输出
return (numberA / numberB) * (timesB / timesA);
};
我们发现初始化代码会有重复,我们可以抽出来做成共用方法,这一步交给你们了。
为你推荐
最后听一首悦耳的歌放松放松,回忆学到的东西。
点击下面播放音乐
助力宝贝回家
微信公众号回复 加群 一起学习。
全部代码
/**
* 判断num是否为一个整数
* @param {number|string|null} value
*/
isInteger = (value: any) => {
if (isNaN(value)) return false;
const num = +value;
return Math.floor(num) === num;
};
/**
* 将一个浮点数转成整数,返回整数和倍数
* @param {number|string|null} floatNum 如: 3.14
* @returns {object} 如:{times:100, num: 314}
*/
toInteger = (floatNum: any) => {
const ret = { times: 1, num: 0 };
if (this.isInteger(floatNum)) {
ret.num = floatNum;
return ret;
}
const strfi = `${floatNum}`;
const dotPos = strfi.indexOf('.');
const len = strfi.substr(dotPos + 1).length;
const times = 10 ** len;
ret.times = times;
ret.num = +strfi.replace('.', '');
return ret;
};
/**
* 初始化算术计算数值
* @param {number|string|null} prevValue
* @param {number|string|null} nextValue
*/
initCalcuNumber = (prevValue: any, nextValue: any) => {
const numA = Number(prevValue);
const numB = Number(nextValue);
if (isNaN(numA) || isNaN(numB)) {
return null;
// throw Error(`method of initCalcuNumber's param is illigal`);
}
const objectNumberA = this.toInteger(numA);
const objectNumberB = this.toInteger(numB);
const numberA = objectNumberA.num;
const numberB = objectNumberB.num;
const timesA = objectNumberA.times;
const timesB = objectNumberB.times;
const timesMax = timesA > timesB ? timesA : timesB;
return { numberA, numberB, timesA, timesB, timesMax };
};
/**
* 加
* @param {number|string|null} prevValue
* @param {number|string|null} nextValue
*/
add = (prevValue: number, nextValue: number): number => {
let result = null;
const calcuNumber = this.initCalcuNumber(prevValue, nextValue);
if (calcuNumber === null) {
return 0;
}
const { numberA, numberB, timesA, timesB, timesMax } = calcuNumber;
if (timesA === timesB) {
// 两个小数位长度相同
result = numberA + numberB;
} else if (timesA > timesB) {
// numberA 小数位长度 大于 numberB
result = numberA + numberB * (timesA / timesB);
} else {
// numberA 小数位长度 小于 numberB
result = numberA * (timesB / timesA) + numberB;
}
return result / timesMax;
};
/**
* 减
* @param {number|string|null} prevValue
* @param {number|string|null} nextValue
*/
subtract = (prevValue: number, nextValue: number): number => {
let result = null;
const calcuNumber = this.initCalcuNumber(prevValue, nextValue);
if (calcuNumber === null) {
return 0;
}
const { numberA, numberB, timesA, timesB, timesMax } = calcuNumber;
if (timesA === timesB) {
result = numberA - numberB;
} else if (timesA > timesB) {
result = numberA - numberB * (timesA / timesB);
} else {
result = numberA * (timesB / timesA) - numberB;
}
return result / timesMax;
};
/**
* 乘
* @param {number|string|null} prevValue
* @param {number|string|null} nextValue
*/
multiply = (prevValue: number, nextValue: number): number => {
const calcuNumber = this.initCalcuNumber(prevValue, nextValue);
if (calcuNumber === null) {
return 0;
}
const { numberA, numberB, timesA, timesB } = calcuNumber;
return (numberA * numberB) / (timesA * timesB);
};
/**
* 除
* @param {number|string|null} prevValue
* @param {number|string|null} nextValue
*/
divide = (prevValue: number, nextValue: number): number => {
const calcuNumber = this.initCalcuNumber(prevValue, nextValue);
if (calcuNumber === null) {
return 0;
}
const { numberA, numberB, timesA, timesB } = calcuNumber;
return (numberA / numberB) * (timesB / timesA);
};