JavaScript小数在做四则运算时,精度会丢失,这会在项目中引起诸多不便。先看个具体的例子:

 //较小的数运算
console.log(0.09999999 + 0.00000001); //0.09999999999999999
console.log(-0.09999999 - 0.00000001); //-0.09999999999999999
console.log(0.012345 * 0.000001); //1.2344999999999999e-8
console.log(0.000001 / 0.0001); //0.009999999999999998 //较大的数运算
console.log( * ); //

从上面的结果可以看出,都不是正确的。为了解决浮点数运算不准确的问题,在运算前我们把参加运算的数先升级(10的X的次方)到整数,等运算完后再降级(0.1的X

的次方)。具体的操作如下:

/**
* 四则运算
*
* @param x
* @param y
* @param op 操作符,0:加;1:减;2:乘;3:除
* @param acc 保留小数位个数,进行四舍五入
*/
function execute(x, y, op, acc) {
var xx = Number(x == undefined ? : x);
var yy = Number(y == undefined ? : y); //
var a = science(xx);
var b = science(yy); var na = a.r1;
var nb = b.r1; var ta = a.r2;
var tb = b.r2;
var maxt = Math.max(ta, tb); //精度值处理
var result = ;
switch (parseInt(op, )) {
case : //加
result = (xx * maxt + yy * maxt) / maxt;
break;
case : //减
result = (xx * maxt - yy * maxt) / maxt;
break;
case : // 乘
result = na * nb / (ta * tb);
break;
case : // 除
result = na / nb * (tb / ta);
default:
} //小数位数处理
if (acc) {
return Number(Number(result).toFixed(parseInt(acc)));
} else {
return Number(result);
}
}
/**
* 将数值升级(10的X的次方)到整数
*/
function science(num) {
var re = {
r1: , //数字去掉小数点后的值,也就是 r1*r2 的结果
r2: //小数部分,10的长度次幂
};
if (isInteger(num)) { //整数直接返回
re.r1 = num;
return re;
}
var snum = scienceNum(num + ""); //处理0.123e-10类似问题
var dotPos = snum.indexOf("."); //小数点位置
var len = snum.substr(dotPos + ).length; //小数点长度
re.r2 = Math.pow(, len);
re.r1 = parseInt(snum.replace(".", ""));
return re;
}
/**
* 将数值转为字符串
*
* 通过移动小数点 扩大倍数或缩小倍数(解决出现e+、e-的问题)
*
* JavaScript在以下情景会自动将数值转换为科学计数法:
* 1)小数点前的数字多于21位。
* 2)小数点后的零多于5个
*/
function scienceNum(value) {
if (!value) {
return value;
}
if (typeof value === 'number') {
value = value + ""
};
let eIndex = value.indexOf('E');
if (eIndex == -) {
eIndex = value.indexOf('e')
};
if (eIndex != -) {
let doubleStr = value.substring(, eIndex); //e前面的值
let eStr = parseInt(value.substring(eIndex + , value.length)); //e后面的值
let doubleStrArr = doubleStr.split('.');
let doubleStr1 = doubleStrArr[] || "";
let doubleStr2 = doubleStrArr[] || ""; if (eStr < ) { //e- 很小的数
let str1Len = doubleStr1.length;
let eStrs = Math.abs(eStr);
if (str1Len > eStrs) {
let nums = doubleStr1.substring(, eStrs);
let nume = doubleStr1.substring(eStrs, str1Len);
doubleStr = nums + "." + nume + nume;
} else if (str1Len < eStrs) {
let indexNum = eStrs - str1Len;
let str = _makeZero(indexNum); //用0补齐
doubleStr = '0.' + str + doubleStr1 + doubleStr2;
} else {
doubleStr = '0.' + doubleStr1 + doubleStr2;
}
} else { //e+ 很大的数
let str2Len = doubleStr2.length;
if (str2Len > eStr) {
let _nums = doubleStr2.substring(, eStr);
let _nume = doubleStr2.substring(eStr, str2Len);
doubleStr = doubleStr1 + _nums + '.' + _nume;
} else if (str2Len < eStr) {
let _indexNum = eStr - str2Len;
let _str = _makeZero(_indexNum); //用0补齐
doubleStr = doubleStr1 + doubleStr2 + _str;
} else {
doubleStr = doubleStr1 + doubleStr2;
}
}
value = doubleStr;
}
return value;
}
//生成num个0的字符串
function _makeZero(num) {
var str = '';
for (var i = ; i < num; i++) {
str += '';
}
return str;
} /**
* 判断是否为整数,字符整数也返回true
*
* @param num
* @returns
*/
function isInteger(num) {
return Math.floor(num) === Number(num);
}

为了调用方便,我们也可以增加如下几个方法:

//加法运算
function add(x, y, acc) {
return execute(x, y, , acc);
} //减法运算
function subtract(x, y, acc) {
return execute(x, y, , acc);
} //乘法运算
function multiply(x, y, acc) {
return execute(x, y, , acc);
} //除法运算
function divide(x, y, acc) {
return execute(x, y, , acc);
}

在上面的计算中,toFixed 方法默认采用四舍六入五成双算法。看个具体的例子:

    console.log(Number(9.8350).toFixed()); //9.84
console.log(Number(9.8351).toFixed()); //9.84
console.log(Number(9.8250).toFixed()); //9.82
console.log(Number(9.82501).toFixed()); //9.83

如果想改成四舍五入,可以重写该方法:

/**
* 默认toFixed方法为四舍六入五成双算法
* 重写toFixed方法调整为四舍五入算法
*/
Number.prototype.toFixed = function (d) {
var s = this + "";
if (!d) d = ;
if (typeof d == 'string') {
d = Number(d);
};
if (s.indexOf(".") == -) {
s += ".";
};
s = scienceNum(s); //处理e+、e-情况
s += new Array(d + ).join("");
if (new RegExp("^(-|\\+)?(\\d+(\\.\\d{0," + (d + ) + "})?)\\d*$").test(s)) {
var _s = "" + RegExp.$,
pm = RegExp.$,
a = RegExp.$.length,
b = true;
if (a == d + ) {
a = _s.match(/\d/g);
if (parseInt(a[a.length - ]) > ) {
for (var i = a.length - ; i >= ; i--) {
a[i] = parseInt(a[i]) + ;
if (a[i] == ) {
a[i] = ;
b = i != ;
} else break;
}
}
_s = a.join("").replace(new RegExp("(\\d+)(\\d{" + d + "})\\d$"), "$1.$2");
}
if (b) {
_s = _s.substr();
};
return (pm + _s).replace(/\.$/, "");
}
return this + "";
};

测试一下:

    console.log(Number(9.8350).toFixed()); //9.84
console.log(Number(9.8351).toFixed()); //9.84
console.log(Number(9.8250).toFixed()); //9.83
console.log(Number(9.82501).toFixed()); //9.83

Js四则运算精度问题处理的更多相关文章

  1. Long类型转json时前端js丢失精度解决方案

    一.问题背景 Java后端开发过程中,尤其是id字段,因数值太大,通过json形式传输到前端后,在js解析时,会丢失精度. 如果对精度丢失没有什么概念,可以看一个知乎的帖子,来感受一下:https:/ ...

  2. js double 精度损失 bugs

    js double 精度损失 bugs const arr = [ 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 ]; // [ ...

  3. vue js计算精度问题处理,兼容XP系统

    js计算精度问题(浮点数误差,大数计算出错) JavaScript 运算时经常遇到会 0.000000001 和 0.999999999 这样奇怪的结果. 网上教程一大篇,原理请百度,抄作业往下看!! ...

  4. JS浮点计算精度问题分析与解决

    问题描述 在JS计算四则运算时会遇到精度丢失的问题,会引起诸多问题,看看以下例子: 例如:在chrome控制台输入 0.1 + 0.7 输出结果是 0.7999999999999999 例如:0.1+ ...

  5. JS浮点数精度运算

    一般来讲,我们在项目中必不可少的需要进行各种数值的计算,但是这种计算全部放在服务端会给服务器带来很大的压力,所以势必要客户端来 分担一些计算的压力. 从客户端来说,JavaScript是一门弱类型语言 ...

  6. js浮点数精度丢失问题及如何解决js中浮点数计算不精准

    js中进行数字计算时候,会出现精度误差的问题.先来看一个实例: console.log(0.1+0.2===0.3);//false console.log(0.1+0.1===0.2);//true ...

  7. javascript(js)小数精度丢失的解决方案

    原因:js按照2进制来处理小数的加减乘除,在arg1的基础上 将arg2的精度进行扩展或逆扩展匹配,所以会出现如下情况. javascript(js)的小数点加减乘除问题,是一个js的bug如0.3* ...

  8. js浮点数精度问题

    大多数语言在处理浮点数的时候都会遇到精度问题,但是在JS里似乎特别严重,来看一个例子 alert(45.6*13); 结果居然是592.800000000001,当然加法之类的也会有这个问题 那这是j ...

  9. mathjs,math.js解决js运算精度问题

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

随机推荐

  1. 知识记录:ASP.NET 应用程序生命周期概述及Global.asax文件中的事件

    IIS7 ASP.NET 应用程序生命周期概述 https://msdn.microsoft.com/zh-cn/library/bb470252(v=vs.100).aspx HttpApplica ...

  2. Netty 源码中对 Redis 协议的实现

    原文地址: haifeiWu的博客 博客地址:www.hchstudio.cn 欢迎转载,转载请注明作者及出处,谢谢! 近期一直在做网络协议相关的工作,所以博客也就与之相关的比较多,今天楼主结合 Re ...

  3. Kettle 使用入门

    Kettle是一款国外开源的ETL工具,纯java编写,可以在Window.Linux.Unix上运行,数据抽取高效稳定. 本文介绍一个入门例子:使用Kettle从动态指定的文件名对应的文本文件里抽取 ...

  4. 程序媛计划——mysql基本操作

    本文适用于mac 在官网上下载community 版mysql,选择dmy这种.在终端中安装好mysql. #进入mysql /usr/local/mysql/bin/mysql -uroot -p ...

  5. 程序媛计划——SQLite初级

    数据库简介 数据库定义: 指的是以一定方式储存在一起.能为多个用户共享.具有尽可能小的冗余度.与应用程序彼此独立的数据集合.是带有相关数据的表的集合. 数据库是由行和列组成的二维表. 字段: 数据库表 ...

  6. react.js学习之路二

    看react.js对我来说真的不是难不难的问题,问题是我的思路太难转变了,真是坑死我了,react里面的坑也很多.算了,这些抱怨没啥用,记录一下今天学习的内容. 今天看了to-do-list经典示例 ...

  7. django模型中, 外键字段使用to_filed属性 指定到所关联主表的某个字段

    在django项目的开发过程中,在设计模型时一开始将主键设置成了一个自定义的字段,但是在创建搜索索引时却发现必须要存在一个id的字段,并且为主键(不知道是否是项目一开始就这样配置的原因), 但此时表结 ...

  8. Bootstrap框架(二)

    day58 巨幕 这是一个轻量.灵活的组件,它能延伸至整个浏览器视口来展示网站上的关键内容. Hello, world! This is a simple hero unit, a simple ju ...

  9. Jmeter做并发测试(设置集合点)

    集合点:让所有请求在不满足条件的时候处于等待状态. 如:我集合点设置为50,那么不满足50个请求的时候,这些请求都会集合在一起,处于等待状态,当达到50的时候,就一起执行.从而达到并发的效果. 那么J ...

  10. struts+spring+hibernate两张表字段名一样处理方法

    在利用struts2+spring+hibernate(利用Hibernate进行分页查询)三大框架进行开发项目的时候,出现一个问题:居然要进行关联查询的十几张表中有两张表的字段一样,并且这两张表中的 ...