我想用|我想用 JS 实现 0.1 + 0.2 输出 0.3
起因
昨天被人问到了一个问题:
因为 JS 精度问题这简单,变成整数,然后再除回去。或者取整,保留小数。0.1 + 0.2 == 0.30000000000000004
,可以不可以得出一个正确的值。0.1 + 0.2 == 0.3
((0.1 + 0.2) * 10).toFixed()/10
(0.1 * 10 + 0.2 * 10)/10
这样问题就变了,变成了 JS 是否能存下这样一个数字。
我一想,这不就是大数计算吗,我刷过。为了简单我找了个库 bignumber 来实现这部分逻辑
new BigNumber(0.1).plus(0.2)
简单的一批。但是这里我自己发现了问题:需要按照特定的写法,这不合理。我又想到,我之前刷过计算器,里面给你输入一个
'1+2+3*4'
然后让你输出计算结果(虽然有偷懒的 eval
方案,但是架不住有题解呀)说整就整,一查 :郁闷
- 题一,只支持加减
- 题二,支持括号,不支持乘除
- 题三,逐渐变态。根本就不是数学。是一套自己的逻辑。
- 题四,加密,需要开会员。
表达式 转换为 逆波兰式
这就不得不吐槽,网上的垃圾资源了
- 居然不支持小数
- 居然只支持个位
- 输出结果也是有问题的
- 居然搜索结果在第一屏
- 居然前一页都是同一篇文章
function calculator(str) {
let n = 0, charStack = [], numStack = [], reducerStr =[], leftIndex = -1
const op = {
'+' : 1,
'-' : 1,
'*' : 2,
'/' : 2,
'(' : 3,
')' : 3
}
while(n < str.length) {
const byte = str[n]
// 数字
// if(/\d/.test(byte)) {
if(/[\d\.]+/.test(byte)) {
// reducerStr.push(byte)
let result = byte;
let _str = str[n+1]
while(/[\d\.]+/.test(_str)){
result+=_str;
n++;
_str = str[n+1]
}
reducerStr.push(result)
} else if(/\(|\)/.test(byte)) {
// 左括号入栈
if(byte === '(') {
charStack.push(byte)
leftIndex = n
// console.log('左括号', byte)
// 右括号出栈
} else {
let nowChar = charStack.pop()
while(nowChar && nowChar !== '(') {
reducerStr.push(nowChar)
nowChar = charStack.pop()
}
}
// 符号
} else {
// 字符栈顶元素
let nowChar = charStack[charStack.length - 1]
while(nowChar && op[byte] < op[nowChar] && nowChar !== '(') {
charStack.pop()
reducerStr.push(nowChar)
nowChar = charStack[charStack.length - 1]
}
charStack.push(byte)
}
n++
}
while(charStack.length) {
reducerStr.push(charStack.pop())
}
return reducerStr
}
解析逆波兰式计算结果
这个也要吐槽,应该是个题解,他把小数取整了 凸(艹皿艹 )。
把取整干掉。然后把计算位置换成我们的库。
var evalRPN = function(tokens) {
const stack = [];
const n = tokens.length;
for (let i = 0;
i < n;
i++) {
const token = tokens[i];
if (isNumber(token)) {
stack.push((token));
// stack.push(parseInt(token));
} else {
const num2 = stack.pop();
const num1 = stack.pop();
if (token === '+') {
stack.push(new BigNumber(num1).plus(num2));
} else if (token === '-') {
stack.push(new BigNumber(num1).minus(num2));
} else if (token === '*') {
stack.push(new BigNumber(num1).times(num2));
} else if (token === '/') {
stack.push(new BigNumber(num1).dividedBy(num2));
// stack.push(num1 / num2 > 0 ? Math.floor(num1 / num2) : Math.ceil(num1 / num2));
}
}
}
return stack.pop();
};
const isNumber = (token) => {
return !('+' === token || '-' === token || '*' === token || '/' === token );
}
便捷测试
这里当然是上 vue 了,加上计算属性,咔咔好用。
文章图片
相关资源
- 前端 BUG 录 - 科学计数法是什么? 这里有一些精度相关的资料,可以点进去看看
- 测试地址:http://jsrun.net/9f9Kp/edit
推荐阅读
- 我要做大厨
- 一个小故事,我的思考。
- 家乡的那条小河
- 第三节|第三节 快乐和幸福(12)
- 这辈子我们都不要再联系了
- 死结。
- 跌跌撞撞奔向你|跌跌撞撞奔向你 第四章(你补英语,我补物理)
- 我从来不做坏事
- 喂,你结婚我给你随了个红包
- 祖母走了