本文针对JavaScript中浮点数运算存在的精度丢失问题,分析了常见现象并提供两种解决方案。重点介绍通过JSMath库将小数转换为整数进行运算的方法,涵盖加减乘除及多目运算的实现细节,旨在提升前端数值计算的准确性。
问题描述
一、问题描述
近期开发项目中涉及JavaScript浮点数计算,发现存在精度缺陷。例如:
0.1+0.2 == 0.30000000000000004
0.1 + 0.7 == 0.7999999999999999
7*0.8 == 5.6000000000000005
5.6/7 == 0.7999999999999999
解决方案
二、解决方案
JavaScript运算结果常存在微小误差,精度不如.NET或Java。鉴于JavaScript非为高精度计算设计,但项目必需,经思考主要有两种解决思路。
方案一:保留小数位
A、方案一:
对运算结果保留2至3位小数。前端界面运算较少且精度要求不高,取2位小数即可满足需求。
方案二:整数转换运算
B、方案二:
将小数转换为整数进行运算。例如:
0.1+0.2 =》 (1+2)/10 == 0.3
0.1 + 0.7 =》 (1+7)/10 == 0.8
7*0.8 == (7*8)/10 == 5.6
5.6/7 == (56/7)/10 == 0.1
为便于调用,可提取公共方法。如下JSMath库重写了加减乘除,先将参数转为整数再运算,调用格式为JSMath(参数1).操作(参数2)。
参数1和参数2分别为运算的第一个和第二个Number,计算后通过Value属性获取结果。
(function() {
var JSMath = function() {
return this;
}
JSMath.prototype.from = function(value) {
// 支持JSMath参数传递主要是用于嵌套的调用
if ((typeof(value) == 'object') && (value.value != undefined)) {
this.value = value.value;
} else {
this.value = value;
}
return this;
}
// 加法
JSMath.prototype.add = function(value) {
var thisValueString = this.value.toString();
var valueString = value.toString();
var timesCount1 = 0;
var timesCount2 = 0;
if (thisValueString.indexOf('.') > 0) {
timesCount1 = thisValueString.split('.')[1].length;
}
if (valueString.indexOf('.') > 0) {
timesCount2 = valueString.split('.')[1].length;
}
var maxtimeCount = timesCount1 > timesCount2 ? timesCount1 : timesCount2;
this.value = (Math.pow(10, maxtimeCount) * this.value + Math.pow(10, maxtimeCount) * value) / Math.pow(10, maxtimeCount);
return this;
}
// 减法
JSMath.prototype.sub = function(value) {
var thisValueString = this.value.toString();
var valueString = value.toString();
var timesCount1 = 0;
var timesCount2 = 0;
if (thisValueString.indexOf('.') > 0) {
timesCount1 = thisValueString.split('.')[1].length;
}
if (valueString.indexOf('.') > 0) {
timesCount2 = valueString.split('.')[1].length;
}
var maxtimeCount = timesCount1 > timesCount2 ? timesCount1 : timesCount2;
this.value = (Math.pow(10, maxtimeCount) * this.value - Math.pow(10, maxtimeCount) * value) / Math.pow(10, maxtimeCount);
return this;
}
// 除法
JSMath.prototype.div = function(value) {
var thisValueString = this.value.toString();
var valueString = value.toString();
var timesCount1 = 0;
var timesCount2 = 0;
if (thisValueString.indexOf('.') > 0) {
timesCount1 = thisValueString.split('.')[1].length;
}
if (valueString.indexOf('.') > 0) {
timesCount2 = valueString.split('.')[1].length;
}
var maxtimeCount = timesCount1 > timesCount2 ? timesCount1 : timesCount2;
this.value = ((Math.pow(10, maxtimeCount) * this.value) / (Math.pow(10, maxtimeCount) * value));
return this;
}
// 乘法
JSMath.prototype.times = function(value) {
var thisValueString = this.value.toString();
var valueString = value.toString();
var timesCount1 = 0;
var timesCount2 = 0;
if (thisValueString.indexOf('.') > 0) {
timesCount1 = thisValueString.split('.')[1].length;
}
if (valueString.indexOf('.') > 0) {
timesCount2 = valueString.split('.')[1].length;
}
var maxtimeCount = timesCount1 > timesCount2 ? timesCount1 : timesCount2;
this.value = (Math.pow(10, maxtimeCount) * this.value * Math.pow(10, maxtimeCount) * value) / Math.pow(10, maxtimeCount * 2);
return this;
}
if (window.JSMath == undefined) {
window.JSMath = function(value) {
var result = new JSMath();
result.from(value);
return result;
}
}
})()
JSMath基本运算
B1、基本运算
0.1+0.2
=> JSMath(0.1).add(0.2).value == 0.3
7+0.8
=> JSMath(7).times(0.8).value == 5.6
5.6/7
=> JSMath(5.6).div(7).value = 0.8
JSMath多目运算
B2、多目运算
0.05 + 0.05 + 0.2
=> JSMath(JSMath(0.05).add(0.05)).add(0.2).value == 0.3
(5+0.6)/7
=> JSMath(JSMath(5).add(0.6)).div(7).value == 0.8
总结
三、小结
以上为当前已知的解决方案,尚不清楚是否有更可靠的开源三方库。本文内容希望对学习有所帮助,感谢支持本站。