所有计算机编程语言里浮点数计算会存在精度丢失问题(或称舍入误差),其根本原因是二进制和实现位数限制有些数无法有限表示

在我的前端工作中,整数的运算很少出现精度损失的问题,除非超过Math.pow(2,53)的超大整数,但是在电商项目中,计算商品价格的时候出现小数这个时候就出现问题了因此有了以下记录,如果你有幸看到,可以拿去使用,如果有改进的地方请给我留言 13663599793@163.com

加减乘除asdm以下两个都可以,思路都一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/*
* @Author: xuesong
* @Date: 2019-09-10 10:48:23
* @Last Modified by:
* @Last Modified time: 2019-11-21 19:30:13
*/
// 加减乘除避免精度问题封装

// 多个参数相加之和
function addAll() {
var r1, r2, m;
var arg1 = arguments[0];
var arg2 = arguments[1];
try {
r1 = arg1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
if (arguments.length < 2) {
return arguments[0] || 0;
} else if (arguments.length === 2) {
return (arg1 * m + arg2 * m) / m;
} else {
// Array.prototype.slice.call(argouments)是个类数组
return addAll(
(arg1 * m + arg2 * m) / m,
...Array.prototype.slice.call(arguments, 2, arguments.length)
);
}
}

function add(arg1, arg2) {
var r1, r2, m;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(10, Math.max(r1, r2));
return (arg1 * m + arg2 * m) / m;
}

function subtract(arg1, arg2) {
var r1, r2, m, n;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(10, Math.max(r1, r2));
//动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}

function multiply(arg1, arg2) {
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
try { m += s1.split(".")[1].length } catch (e) { void 0 }
try { m += s2.split(".")[1].length } catch (e) { void 0 }
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}

function divide(arg1, arg2) {
var t1 = 0, t2 = 0, r1, r2;
try { t1 = arg1.toString().split(".")[1].length } catch (e) { window.console.log('') }
try { t2 = arg2.toString().split(".")[1].length } catch (e) { window.console.log('') }

r1 = Number(arg1.toString().replace(".", ""));

r2 = Number(arg2.toString().replace(".", ""));
return (r1 / r2) * Math.pow(10, t2 - t1);
}
export { add, subtract, multiply, divide, addAll }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

var floatObj = function() {

/*
* 判断obj是否为一个整数
*/
function isInteger(obj) {
return Math.floor(obj) === obj
}

/*
* 将一个浮点数转成整数,返回整数和倍数。如 3.14 >> 314,倍数是 100
* @param floatNum {number} 小数
* @return {object}
* {times:100, num: 314}
*/
function toInteger(floatNum) {
var ret = {times: 1, num: 0}
var isNegative = floatNum < 0
if (isInteger(floatNum)) {
ret.num = floatNum
return ret
}
var strfi = floatNum + ''
var dotPos = strfi.indexOf('.')
var len = strfi.substr(dotPos+1).length
var times = Math.pow(10, len)
var intNum = parseInt(Math.abs(floatNum) * times + 0.5, 10)
ret.times = times
if (isNegative) {
intNum = -intNum
}
ret.num = intNum
return ret
}

/*
* 核心方法,实现加减乘除运算,确保不丢失精度
* 思路:把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
*
* @param a {number} 运算数1
* @param b {number} 运算数2
* @param digits {number} 精度,保留的小数点数,比如 2, 即保留为两位小数
* @param op {string} 运算类型,有加减乘除(add/subtract/multiply/divide)
*
*/
function operation(a, b, digits, op) {
var o1 = toInteger(a)
var o2 = toInteger(b)
var n1 = o1.num
var n2 = o2.num
var t1 = o1.times
var t2 = o2.times
var max = t1 > t2 ? t1 : t2
var result = null
switch (op) {
case 'add':
if (t1 === t2) { // 两个小数位数相同
result = n1 + n2
} else if (t1 > t2) { // o1 小数位 大于 o2
result = n1 + n2 * (t1 / t2)
} else { // o1 小数位 小于 o2
result = n1 * (t2 / t1) + n2
}
return result / max
case 'subtract':
if (t1 === t2) {
result = n1 - n2
} else if (t1 > t2) {
result = n1 - n2 * (t1 / t2)
} else {
result = n1 * (t2 / t1) - n2
}
return result / max
case 'multiply':
result = (n1 * n2) / (t1 * t2)
return result
case 'divide':
result = (n1 / n2) * (t2 / t1)
return result
}
}

// 加减乘除的四个接口
function add(a, b, digits) {
return operation(a, b, digits, 'add')
}
function subtract(a, b, digits) {
return operation(a, b, digits, 'subtract')
}
function multiply(a, b, digits) {
return operation(a, b, digits, 'multiply')
}
function divide(a, b, digits) {
return operation(a, b, digits, 'divide')
}

// exports
return {
add: add,
subtract: subtract,
multiply: multiply,
divide: divide
}
}();
1
2
3
4
5
6
7
// toFixed 修复
function toFixed(num, s) {
var times = Math.pow(10, s)
var des = num * times + 0.5
des = parseInt(des, 10) / times
return des + ''
}
相关文章
评论
分享
  • 城市三联

    123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263...

    城市三联
  • 老式浏览器是否支持字符串比较正则u修饰符字符模式

    检测当前引擎是否支持u修饰符12345678function hasRegExpU(){ try{ var pattern = new RegExp('.',u); return ...

    老式浏览器是否支持字符串比较正则u修饰符字符模式
  • 随机数的生成

    没有指定范围生成 Math.floor(Math.random()*Number) 从0开始到Number,但不包含Number指定范围随机数的生成 Math.floor(Math.random()*(max-min+1)+min) ...

    随机数的生成
  • 防抖和节流

    1 防抖 debouncedebounce防抖 :在事件被触发n秒后执行,如果在此时间再次触发事件,则重新开始计时。例如联想搜索 html1<input type="text" id="phone"> js1234567...

    防抖和节流
  • 随机十六进制颜色

    const randomHexColorCode => () { let n = (Math.random() * 0xfffff * 1000000).toString(16); ret...

    随机十六进制颜色
  • 各种去重的效率

    1. set方法去重123function unique(){ return Array.from(new Set(this))} 1234567const arr =[]for (let i = 0; i ...

    各种去重的效率
  • 手写bind和call及apply

    自定义bind和call及apply重要的是this和参数```bash js// 设置公共this方法let pubContext = funtion(){return this} Function.prototype._bind ...

    手写bind和call及apply
  • 全网灰暗效果

    css处理全网灰暗css12345678filter: grayscale(100%); -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -ms...

    全网灰暗效果
  • 校验工具类函数

    验证邮政编码1234/** * @param { string } value */export const isPostcode = value => /^(0[1-7]|1[0-356]|2[0-7]|3...

    校验工具类函数
  • 观察者模式和发布订阅模式

    观察者模式和发布订阅模式观察者模式:是指一个对象(subject)维持一个依赖列表Observer,当主题状态发生变化的时候,会通知观察者集合。发布和观察者都能看到对方都知道对方的存在。比如事件触发。发布订阅模式:观察者和订阅者关联。...

    观察者模式和发布订阅模式
Please check the comment setting in config.yml of hexo-theme-Annie!