You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中如何准确计算精度达18位的字符串表达式?

高精度字符串表达式计算解决方案

问题原因

你遇到的精度丢失是因为JavaScript引擎的Number类型基于IEEE 754双精度浮点数,它只能精确表示±2^53范围内的整数。你的18位数字远超这个范围,JS解析时就已经丢失精度,后续即使转成BigDecimal也无法恢复原始精确值。

解决方案

方案一:改进ScriptEngine的表达式替换逻辑

如果坚持用ScriptEngine,必须确保BigDecimal用字符串参数构造(而非让JS先解析成数字),同时正确转换运算符为BigDecimal的方法调用。注意这个方法仅适用于简单表达式,复杂优先级场景需要额外处理。

代码示例:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.math.BigDecimal;

public class ScriptEngineBigDecimalEval {
    public static void main(String[] args) throws Exception {
        String formula = "333333333333333333+222222222222222222";
        
        // 将表达式转换为BigDecimal方法调用链
        // 先把所有数字替换为new BigDecimal('数字')
        String processed = formula.replaceAll("(\\d+)", "new java.math.BigDecimal('$1')");
        // 替换运算符为对应的BigDecimal方法
        processed = processed.replace("+", ".add(")
                             .replace("-", ".subtract(")
                             .replace("*", ".multiply(")
                             .replace("/", ".divide(");
        // 补充闭合括号(运算符数量等于括号需要补充的数量)
        int operatorCount = formula.split("[+\\-*/]").length - 1;
        StringBuilder finalExpr = new StringBuilder(processed);
        for (int i = 0; i < operatorCount; i++) {
            finalExpr.append(")");
        }
        
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        BigDecimal result = (BigDecimal) engine.eval(finalExpr.toString());
        System.out.println(result); // 输出555555555555555555
    }
}

方案二:使用第三方高精度表达式解析库(推荐)

无需自己处理解析和优先级,直接用支持BigDecimal的成熟库,比如EvalEx。它原生支持高精度计算和BODMAS规则,使用简单。

  1. 引入Maven依赖:
<dependency>
    <groupId>com.udojava</groupId>
    <artifactId>EvalEx</artifactId>
    <version>2.7</version>
</dependency>
  1. 代码示例:
import com.udojava.evalex.Expression;
import java.math.BigDecimal;

public class EvalExBigDecimalExample {
    public static void main(String[] args) {
        String formula = "333333333333333333+222222222222222222";
        Expression expression = new Expression(formula);
        BigDecimal result = expression.eval();
        System.out.println(result); // 输出555555555555555555
    }
}

方案三:手动实现支持BODMAS规则的解析器

如果不能引入第三方库,可以用调度场算法实现自己的表达式解析器,全程用BigDecimal运算保证精度。

代码示例(支持+、-、*、/和括号):

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

public class CustomBigDecimalParser {
    // 定义运算符优先级
    private static final Map<String, Integer> OP_PRECEDENCE = new HashMap<>();
    static {
        OP_PRECEDENCE.put("+", 1);
        OP_PRECEDENCE.put("-", 1);
        OP_PRECEDENCE.put("*", 2);
        OP_PRECEDENCE.put("/", 2);
    }

    // 词法分析:拆分表达式为数字、运算符、括号
    private static List<String> tokenize(String expr) {
        List<String> tokens = new ArrayList<>();
        StringBuilder numBuilder = new StringBuilder();
        for (char c : expr.toCharArray()) {
            if (Character.isDigit(c)) {
                numBuilder.append(c);
            } else if ("+-*/()".indexOf(c) != -1) {
                if (numBuilder.length() > 0) {
                    tokens.add(numBuilder.toString());
                    numBuilder.setLength(0);
                }
                tokens.add(String.valueOf(c));
            }
        }
        if (numBuilder.length() > 0) {
            tokens.add(numBuilder.toString());
        }
        return tokens;
    }

    // 中缀表达式转后缀表达式(调度场算法)
    private static List<String> infixToPostfix(List<String> tokens) {
        List<String> postfix = new ArrayList<>();
        Deque<String> opStack = new ArrayDeque<>();

        for (String token : tokens) {
            if (token.matches("\\d+")) {
                postfix.add(token);
            } else if (token.equals("(")) {
                opStack.push(token);
            } else if (token.equals(")")) {
                while (!opStack.isEmpty() && !opStack.peek().equals("(")) {
                    postfix.add(opStack.pop());
                }
                opStack.pop(); // 弹出左括号
            } else { // 处理运算符
                while (!opStack.isEmpty() && !opStack.peek().equals("(")
                        && OP_PRECEDENCE.get(opStack.peek()) >= OP_PRECEDENCE.get(token)) {
                    postfix.add(opStack.pop());
                }
                opStack.push(token);
            }
        }
        while (!opStack.isEmpty()) {
            postfix.add(opStack.pop());
        }
        return postfix;
    }

    // 计算后缀表达式
    private static BigDecimal calculatePostfix(List<String> postfix) {
        Deque<BigDecimal> numStack = new ArrayDeque<>();
        for (String token : postfix) {
            if (token.matches("\\d+")) {
                numStack.push(new BigDecimal(token));
            } else {
                BigDecimal b = numStack.pop();
                BigDecimal a = numStack.pop();
                switch (token) {
                    case "+":
                        numStack.push(a.add(b));
                        break;
                    case "-":
                        numStack.push(a.subtract(b));
                        break;
                    case "*":
                        numStack.push(a.multiply(b));
                        break;
                    case "/":
                        // 除法指定18位精度和四舍五入模式
                        numStack.push(a.divide(b, 18, RoundingMode.HALF_UP));
                        break;
                }
            }
        }
        return numStack.pop();
    }

    public static BigDecimal evaluate(String expr) {
        List<String> tokens = tokenize(expr);
        List<String> postfix = infixToPostfix(tokens);
        return calculatePostfix(postfix);
    }

    public static void main(String[] args) {
        String formula = "333333333333333333+222222222222222222";
        BigDecimal result = evaluate(formula);
        System.out.println(result); // 输出555555555555555555
    }
}

内容的提问来源于stack exchange,提问作者akaparadox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 05:37:37