Open sunhengzhe opened 7 years ago
这个问题是在一个前端群里讨论的,放在 这里。后续可以去那里讨论
感觉写得不好
function add() {
const result = [...arguments].reduce((x, y) => {
y = y.toString();
// 兼容整数
y = y.indexOf('.') == -1 ? y = y + '.0' : y;
let [intX, floatX] = x.split('.');
let [intY, floatY] = y.split('.');
let floatRet = addStr(floatX, floatY);
floatRet = floatRet.indexOf('.') != -1 ? floatRet.split('.') : (floatRet + '.0').split('.') ;
let intRet = parseInt(intX) + parseInt(intY) + parseInt(floatRet[0]);
return intRet + '.' + floatRet[1];
}, '0.0');
return result;
}
const addStr = (x, y) => {
const xLength = x.length,
yLength = y.length,
maxLength = Math.max(xLength, yLength);
// 填充长度
x = x + Array(maxLength - xLength).fill(0).join('');
y = y + Array(maxLength - yLength).fill(0).join('');
let result = [],
j = 0,
flag = 0;
for(let i = maxLength - 1; i >= 0; i--) {
let r = 0;
r = parseInt(x[i]) + parseInt(y[i]) + flag;
flag = Math.floor(r / 10);
r = r < 10 ? r : r % 10;
result[j++] = r;
}
flag ? result.push(flag) : '';
let ret = '';
if (result.length - maxLength === 0) {
result.reverse().splice(result.length - maxLength, 0, '0.');
} else {
result.reverse().splice(result.length - maxLength, 0, '.');
}
return result.join('');
}
@sunshumin
x = x + Array(maxLength - xLength).fill(0).join('');
这里可以使用 es6 的 padEnd()
方法代替:x = x.padEnd(maxLength, 0)
@sunshumin
if (result.length - maxLength === 0) { result.reverse().splice(result.length - maxLength, 0, '0.'); } else { result.reverse().splice(result.length - maxLength, 0, '.'); }
这里的判断实际上就是有无进位,我感觉这个方法返回
是否进位
和小数部分
两个东西比较好。然后在add
方法里,根据你的做法是把整数部分加起来(其实这个地方可能溢出,最好是像处理小数一样处理成字符串)。把你代码简化了一下:
function add(/* number1, number2, number3, ... */) {
const result = [...arguments].reduce((x, y) => {
// ...
const { isCarry, decimals } = addStr(floatX, floatY);
const intRet = parseInt(intX) + parseInt(intY) + isCarry; // 自动转型
return intRet + '.' + decimals;
}, '0.0');
return result;
}
const addStr = (x, y) => {
//...
flag ? result.push(flag) : '';
const isCarry = result.length !== maxLength;
if (isCarry) {
result.pop();
}
return {
isCarry,
decimals: result.reverse().join('')
};
}
还有两个精度有关的补充:
x = 0.2;
y = 0.3;
z = 0.1;
equal = (Math.abs(x - y + z) < Number.EPSILON);
parseInt(0.0000008) === 8
结果为 true
因为 0.0000008
会先被转成 8e-7
EMM 我又碰到了 不过是四舍五入 https://juejin.im/post/5ad5c104518825558002bdc0
* 四舍五入
* @param number 要四舍五入的数字
* @param precision 精度 保留小数点位数
* @returns {*}
*/
function round(number,precision) {
const enlargeDigits = function enlargeDigits(times) {
return function (number) {
return +(String(number) + "e" + String(times));
};
};
const toFixed = function toFixed(precision) {
return function (number) {
return number.toFixed(precision);
};
};
const compose = function compose() {
for (var _len = arguments.length, functions = Array(_len), _key = 0; _key < _len; _key++) {
functions[_key] = arguments[_key];
}
var nonFunctionTypeLength = functions.filter(function (item) {
return typeof item !== 'function';
}).length;
if (nonFunctionTypeLength > 0) {
throw new Error("compose's params must be functions");
}
if (functions.length === 0) {
return function (arg) {
return arg;
};
}
if (functions.length === 1) {
return functions[0];
}
return functions.reduce(function (a, b) {
return function () {
return a(b.apply(undefined, arguments));
};
});
};
var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
if (Number.isNaN(+number)) {
throw new Error("number's type must be Number");
}
if (Number.isNaN(+precision)) {
throw new Error("precision's type must be Number");
}
return compose(toFixed(precision), enlargeDigits(-precision), Math.round, enlargeDigits(precision))(number)
}```
用 js 实现高精度运算,这个问题是老生常谈了。如果真的要前端做一个高精度运算(比如钱),还是要注意这个问题的。
更一般地,实现四则运算的精度运算