Java BigDecimal解决double精度丢失

我们在日常开发中, 有很多时候会遇到小数(double类型)精确计算, 由于 double 类型在计算机中的特殊存储方式, 某些情况下会导致精度丢失, 特别是跟钱有关的计算, 后台校验的时候, 那得是分毫不差.

场景

  • 商品a价格是 0.1, 商品b 价格是 0.2, 用户买了这两个商品, 付款时, 订单总金额是 0.3, 如果用 double 类型做计算, 将会得到结果 0.30000000000000004, 前端如果拿来展示总金额, 用户肯定就蒙圈了, 这显然不是我们想要的.
  • 那么如何解决呢? Java 给我们提供了专门解决这个问题的类 BigDecimal.
ini 复制代码
double a = 0.1;
double b = 0.2;
double c = a + b;
System.out.println(c); // 0.30000000000000004

BigDecimal 使用

创建 BigDecimal 对象

创建对象一共有3种方式, 两个构造器, 一个静态方法, 实际静态方法是对入参 String 的构造器的封装, 即 2, 3 两种方式本质上是一样的.

ini 复制代码
 // 1 入参是 double
 BigDecimal a = new BigDecimal(0.3);
 // 2 入参是 String
 BigDecimal b = new BigDecimal("0.3");
 // 3 静态方法, 入参是 double
 BigDecimal c =  BigDecimal.valueOf(0.3);

计算操作

  • 公用代码
ini 复制代码
double a = 0.1;
double b = 0.2;
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = new BigDecimal(Double.toString(b));
1. 加法 add
  • 这就很好的解决了上面场景中出现的问题
ini 复制代码
BigDecimal c1 = a1.add(b1);  
System.out.println(c1); // 0.3
2. 减法 subtract
ini 复制代码
BigDecimal c2 = a1.subtract(b1);  // 减法
System.out.println(c2); // -0.1
3. 乘法
ini 复制代码
BigDecimal c3 = a1.multiply(b1);  // 乘法
System.out.println(c3); // 0.02
4. 除法
ini 复制代码
BigDecimal c4 = a1.divide(b1);  // 除法  0.1 / 0.3, 无法除尽, 报错.
System.out.println(c4); // 0.5

BigDecimal c5 = a1.divide(c1);  // 除法  
System.out.println(c5); // 0.1 / 0.3, 无法除尽, 报错.

c5 = a1.divide(c1, 2, RoundingMode.HALF_UP);  // 除法  0.1 / 0.3  四舍五入
System.out.println(c5); // 0.5

注意:

  • BigDecimal 进行除法计算时, 强烈建议使用指定保留小数倍数和舍入模式这个方法, 这样可以避免除不尽造成的异常.
arduino 复制代码
/**
* 参数1: 除数
* 参数2: 保留小数的位数
* 参数3: 舍入模式
*/
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
5. BigDecimal 转化为 double
ini 复制代码
double value = c4.doubleValue();

总结

使用 BigDecimal 计算时要注意以下几点:

  1. 不建议使用入参是 double 的构造器, 因为它仍然存在精度问题.
  2. 强烈建议使用入参是 string 的构造器, 或者使用静态方法 valueOf 创建对象, 才能保证精度.
  3. 使用 BigDecimal除法 时, 必须要使用 divide(BigDecimal divisor, int scale, RoundingMode roundingMode), 防止除不尽造成异常.

如有写的不对或者没写清楚的地方, 欢迎大家拍砖, 千万不要留情😄. 你拍的砖将成为我进步的垫脚石😁😁😁😁, 能看到这里的小伙伴, 必须得感谢一波.

相关推荐
诗人不写诗1 分钟前
spring是如何组织切面的
java·后端·spring
_饭团11 分钟前
字符串函数全解析:12 种核心函数的使用与底层模拟实现
c语言·开发语言·学习·考研·面试·蓝桥杯
小杨同学4920 分钟前
STM32 进阶封神之路(二十二):DMA 实战全攻略 ——ADC 采集 + 串口收发 + 内存复制(库函数 + 代码落地)
后端·单片机·嵌入式
前端摸鱼匠24 分钟前
面试题4:多头注意力(MHA)相比单头注意力的优势是什么?Head数如何影响模型?
人工智能·ai·面试·职场和发展·求职招聘
呆瑜nuage41 分钟前
【复习系列】高频C/C++库函数手写实现指南与自定义类型的理解指南
c语言·c++·面试
天下无贼!42 分钟前
【Python】2026版——FastAPI 框架快速搭建后端服务
开发语言·前端·后端·python·aigc·fastapi
大傻^1 小时前
Spring AI Alibaba Agent开发:基于ChatClient的智能体构建模式
java·数据库·人工智能·后端·spring·springaialibaba
li星野1 小时前
C++面试真题分享20260320
java·c++·面试
Irissgwe1 小时前
c++特殊类设计
java·开发语言·c++
大傻^1 小时前
Spring AI Alibaba ChatClient实战:流式输出与多轮对话管理
java·人工智能·后端·spring·springai·springaialibaba