Java实现字符串四则运算避免高精度下保留位精度丢失

server/2025/1/16 19:39:33/

背景介绍

在开发中,我们经常需要对用户输入的数学表达式进行实时计算。例如,在前端页面上展示动态计算结果。然而,由于前端通常采用JavaScript进行计算,而JavaScript的浮点运算存在精度问题,导致部分结果与后端基于BigDecimal的计算不一致。

为了彻底解决这一问题,我们可以在后台编写一个高精度的数学表达式计算工具类,并将其 API 暴露给前端调用,从而杜绝前后端计算结果不一致的问题。


实现思路

  1. 递归与替换结合

    • 创建一个基础方法 A,该方法仅支持两数之间的简单加减乘除运算。
    • 利用递归方法处理复杂的表达式,逐步拆解成简单运算。
  2. 处理优先级

    • 对于加减、乘除运算,遵循数学优先级规则,先处理乘除,后处理加减。
    • 对于带有括号的表达式,优先计算最内层括号的内容。
  3. 逐步简化表达式

    • 每次计算后,使用结果替换表达式中的对应部分,直到表达式被完全解析。

以下是一个具体的例子来演示这一过程:

表达式2+(6*2+18-2*4)/2

  • 第 1 步:计算括号内部的乘法运算,2+(12+18-2*4)/2
  • 第 2 步:继续计算,2+(12+18-8)/2
  • 第 3 步:进一步简化,2+(30-8)/2
  • 第 4 步:计算括号内部的减法,2+22/2
  • 第 5 步:处理除法,2+11
  • 第 6 步:最终结果,13

实际代码实现

java">import java.math.BigDecimal;public class BigDecimalExpressionEvaluator {public static void main(String[] args) {String expression = "33.241*3480/1.13";long startString = System.nanoTime();BigDecimal stringResult = calculate(expression);long endString = System.nanoTime();System.out.println("String Parsing Calculation Result: " + stringResult.stripTrailingZeros().toPlainString());System.out.println("String Parsing Calculation Time: " + (endString - startString) + " ns");}/*** 主计算入口,传入一个数学表达式返回计算结果*/public static BigDecimal calculate(String expression) {// 1. 预处理表达式,标准化字符expression = standardizeExpression(expression);// 2. 去除所有空格expression = expression.replaceAll(" ", "");// 3. 递归解析并计算结果return evaluate(expression);}/*** 标准化表达式,将非标准符号替换为标准符号。*/private static String standardizeExpression(String expression) {// 替换 Unicode 减号(U+2212)为 ASCII 减号(U+002D)expression = expression.replace('\u2212', '-');// 如果有其他类似非标准字符,也可以在这里扩展,比如替换全角数字/符号// 替换全角空格为半角空格expression = expression.replace('\u3000', ' ');// 替换全角加号(U+FF0B)为 ASCII 加号(U+002B)expression = expression.replace('\uFF0B', '+');// 替换全角乘号(U+00D7)为 ASCII 星号(U+002A)expression = expression.replace('\u00D7', '*');// 替换全角除号(U+00F7)为 ASCII 斜杠(U+002F)expression = expression.replace('\u00F7', '/');return expression;}/*** 递归解析和计算表达式*/private static BigDecimal evaluate(String expression) {// 处理括号,从内到外递归解析while (expression.contains("(")) {int openIndex = expression.lastIndexOf("("); // 找到最内层左括号int closeIndex = expression.indexOf(")", openIndex); // 找到与之匹配的右括号String innerExpression = expression.substring(openIndex + 1, closeIndex); // 括号内的表达式BigDecimal innerResult = evaluate(innerExpression); // 递归计算括号内的值expression = expression.substring(0, openIndex) + innerResult + expression.substring(closeIndex + 1); // 替换括号内容为结果}// 递归处理乘除(优先级高)while (expression.contains("*") || expression.contains("/")) {expression = handleOperators(expression, "*/");}// 递归处理加减(优先级低)while (expression.contains("+") || expression.contains("-")) {expression = handleOperators(expression, "+-");}return new BigDecimal(expression);}/*** 处理指定运算符优先级的简单表达式*/private static String handleOperators(String expression, String operators) {for (int i = 0; i < expression.length(); i++) {char ch = expression.charAt(i);if (operators.indexOf(ch) != -1) { // 当前字符是目标操作符// 找到左操作数int leftStart = i - 1;while (leftStart >= 0 && (Character.isDigit(expression.charAt(leftStart)) || expression.charAt(leftStart) == '.')) {leftStart--;}BigDecimal left = new BigDecimal(expression.substring(leftStart + 1, i));// 找到右操作数int rightEnd = i + 1;while (rightEnd < expression.length() && (Character.isDigit(expression.charAt(rightEnd)) || expression.charAt(rightEnd) == '.')) {rightEnd++;}BigDecimal right = new BigDecimal(expression.substring(i + 1, rightEnd));// 计算当前操作符的结果BigDecimal result = applyOperator(ch, left, right);// 替换表达式中对应的部分expression = expression.substring(0, leftStart + 1) + result + expression.substring(rightEnd);break; // 每次只处理一个操作符,递归继续}}return expression;}/*** 对两个数应用操作符*/private static BigDecimal applyOperator(char op, BigDecimal left, BigDecimal right) {switch (op) {case '+':return left.add(right);case '-':return left.subtract(right);case '*':return left.multiply(right);case '/':if (right.compareTo(BigDecimal.ZERO) == 0) {throw new ArithmeticException("Cannot divide by zero");}return left.divide(right, 8, BigDecimal.ROUND_HALF_UP); // 保留 8 位小数default:throw new UnsupportedOperationException("Unsupported operator: " + op);}}
}

由于对复杂算法没有要求,所以只是简单的实现了加减乘除的基本运算,需要的朋友可以自行扩展。改代码没有在生产环境实际使用,请自行测试


http://www.ppmy.cn/server/158897.html

相关文章

【机器学习:十五、神经网络的编译和训练】

1. TensorFlow实现代码 TensorFlow 是深度学习中最为广泛使用的框架之一&#xff0c;提供了灵活的接口来构建、编译和训练神经网络。以下是实现神经网络的一个完整代码示例&#xff0c;以“手写数字识别”为例&#xff1a; import tensorflow as tf from tensorflow.keras im…

C++建楼梯贪心算法

问题描述 有&#x1d441;个正方形从左到右排成一行。第&#x1d456;个正方形的高度是&#x1d43b;&#x1d456;。 对于每个正方形&#xff0c;你可以执行以下操作之一&#xff1a; • 将正方形的高度减少1。 • 什么也不做。 确定是否可以通过执行这些操作使得正方形的高…

STM32F1——CAN驱动代码

一、 CAN.H。 #ifndef __CAN_H #define __CAN_H #include "stm32f10x.h" #include "SysTick.h" //PA11--CANRX PA12--CANTX //CAN接收RX0中断使能 #define CAN_RX0_INT_ENABLE 0 //0,不使能;1,使能.u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 …

【Linux】10.Linux基础开发工具使用(3)

文章目录 使用 git 命令行&#xff08;初级&#xff09;Ubuntu安装 git注册gitee用户并创建gitee仓库Ubuntu下使用git 使用 git 命令行&#xff08;初级&#xff09; Ubuntu安装 git 首先更新软件源&#xff1a; sudo apt update然后再次尝试安装 git&#xff1a; sudo apt…

解锁“搭子小程序”开发新机遇,助力企业数字化转型

搭子作为一种新型的社交方式&#xff0c;逐渐进入到了年轻人的生活中&#xff0c;在日常旅游、学习、逛街等&#xff0c;年轻人都可以找到志同道合的“搭子”&#xff0c;提高生活的幸福指数。 随着搭子市场的发展&#xff0c;通过互联网寻找搭子已经成为了年轻人的必备方式。…

计算机网络八股文学习笔记

总结来自于javaguide,本文章仅供个人学习复习 javaguide计算机网络八股 文章目录 计算机网络基础网络分层模型OSI七层模型TCP/IP四层模型 HTTP从输入URL到页面展示到底发生了什么?(非常重要)HTTP状态码HTTP Header中常见的字段有哪些?HTTP和HTTPS有什么区别?(重要)HTTP/1.0和…

PMP–一、二、三模、冲刺–分类–7.成本管理

文章目录 技巧一模7.成本管理--4.控制成本--数据分析--挣值分析--进度绩效指数&#xff08;SPI&#xff09;是测量进度效率的一种指标&#xff0c;表示为挣值与计划价值之比&#xff0c;反映了项目团队完成工作的效率。 当 SPI小于 1.0 时&#xff0c;说明已完成的工作量未达到…

Unity3D仿星露谷物语开发21之添加更多道具

1、目标 截至目前&#xff0c;我们的道具有Corn&#xff0c;Parsnip&#xff0c;Pumpkin&#xff0c;Grass1&#xff0c;Grass2&#xff0c;PricklyCactus这6种&#xff0c;我们需要添加更多的道具到游戏场景中。 2、思路 当前Assets -> Prefabs -> Item下有一个Item预…