在项目中做了一个计算统计值的部分,实现过程是通过 javaScript 进行累加的。在测试时出现了一个很乖的问题,在此记录一下。
1. 问题背景
项目中有一个表格字段,数据类型是float的,在数据库中均以Decimal(10,2)的格式保存(也即有效数字为10,小数点为两位)。现在,要对该字段进行累加统计。
2. JavaScript实现
考虑到JavaScript中的数据是无类型的自动判断,因此没有多想,直接将这个字段循环累加,代码如下:
// 统计UFP值
RequireCount.prototype.addUFP = function(value) {
this.count_ufp += value;
};
// 统计US总值
RequireCount.prototype.addUS = function(value) {
this.count_US += value;
};
数据库中的数据全部都是小数点为两位的数据,怎么会出现这么长的小数点位数(50.68000000000001),也不该出现.00000000001吧?
3. 查找资料
在JavaScript中,数值(浮点型)数据在存储时并不区分Number还是Float型,而是全部按照Float的类型来进行存储。存储方式为:整形部分的长度为10,小数部分位数为16;如果最后一位小数为0则用1代替(这个地方很难理解,为啥用1代替最后一位,具体解释是用1来代替有效数字位数)
4. 问题解释
在统计过程中,如果出现了两者相加出现整数(也即后面小数点部分都满整了,比如,3.45 + 2.55 = 6.00),这时,计算结果将代替为:6.0000000000000001而不是,6.00。因为根据以上资料的规定就是这么个意思。是不是很奇怪的规定,不知道为什么这么规定,需要以后深入了解。
5. 解决方式(根据目前情况,并非正确解决方案,请勿模仿)
因为,项目中的数据量不是很大,按照精度问题来说,后面的一大片零头不会影响前面的数据结果,故而使用了 toFixed方法截取所需要的部分:
<span style="font-size:18px;">var floatTest = 5.6849392174
var needFloat = loatTest.toFixed(2)</span>
虽然,不是正确的解决方案,但是针对目前情况来讲,是一个适用的“解决方案”
6. 问题延展及终极解决方案
加法会出现此问题,我们自然就会想到减法运算,然后就是乘除运算。
对于乘法,这不能这么“大意大胆”的使用上面的方式,毕竟乘法可不是一点点的累加了,可以说是:失之毫厘谬以千里啊。
6.1 加法:
function add(value1, value2) {
var r1, r2, m;
try {
r1 = value1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = value2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (value1 * m + value2 * m) / m;
}
6.2 减法:
function Subtr(value1, value2) {
var r1, r2, m, n;
try {
r1 = value1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = value2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
// 动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((value1 * m - value2 * m) / m).toFixed(n);
}
6.3 乘法:
function multiple(value1, value2) {
var m = 0, s1 = value1.toString(), s2 = value2.toString();
try {
m += s1.split(".")[1].length;
} catch (e) {
}
try {
m += s2.split(".")[1].length;
} catch (e) {
}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
6.4 除法:
function divider(value1, value2) {
var t1 = 0, t2 = 0, r1, r2;
try {
t1 = value1.toString().split(".")[1].length;
} catch (e) {
}
try {
t2 = value2.toString().split(".")[1].length;
} catch (e) {
}
with (Math) {
r1 = Number(value1.toString().replace(".", ""));
r2 = Number(value2.toString().replace(".", ""));
return (r1 / r2) * pow(10, t2 - t1);
}
}