news 2026/7/4 9:12:20

为什么不该用 Double 表示金额及解决方案合集 - Java(45)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
为什么不该用 Double 表示金额及解决方案合集 - Java(45)

比如 0.1 + 0.2 并不等于 0.3,而是等于 0.30000000000000004——这也一度成为程序员圈子里的经典梗。所以用浮点数表示金额这种需要精确计算的数值,是会出现精度丢失问题的。

double a = 0.1; double b = 0.2; System.out.println(a + b); // 输出: 0.30000000000000004 System.out.println(a + b == 0.3); // 输出: false

再看一个更实际的例子,假设你在做一个电商系统的金额计算:

double price = 2.0; double discount = 0.9; System.out.println(price * discount); // 输出: 1.7999999999999998

你看,原本应该是 1.8 的结果,却变成了 1.7999999999999998。如果这是真实的订单金额,那可就出大问题了

为什么会精度丢失

为什么会有这种精度丢失呢?因为计算机底层都是用二进制存储的,但并不是所有十进制数都能用二进制精确表示。各位有兴趣的话可以试着算一下 0.1 的二进制是多少,算出来可以在评论区分享一下。

算了一会你可能会发现:这怎么算不完?没错,出现了无限循环的情况——(0.1)₁₀ = (0.000110011001100...)₂ 像这种情况,计算机就没办法用二进制精确表示 0.1 了。

而 double 类型在 Java 中占 64 位,按照 IEEE 754 标准,其中 1 位是符号位,11 位是指数位,52 位是尾数位。当遇到无限循环的二进制小数时,只能截断保存,这就导致了精度丢失。

BigDecimal

在 Java 中,无论是单精度还是双精度,表示的都是近似值。

为了表示精确的小数值,Java 提供了BigDecimal类型。BigDecimal由两个部分组成:无标度值(unscaled value)和标度(scale)。无标度值是一个整数,表示实际的数值;标度也是一个整数,表示小数点后的位数。

举个例子,数字 123.45 在 BigDecimal 中:

  • 无标度值是 12345
  • 标度是 2

实际值就是:12345 × 10⁻² = 123.45

用 BigDecimal 来处理刚才的金额计算:

BigDecimal price = new BigDecimal("2.0"); BigDecimal discount = new BigDecimal("0.9"); BigDecimal result = price.multiply(discount); System.out.println(result); // 输出: 1.80

这下结果就对了

equals 的坑

在 BigDecimal 中不能用 equals 方法做等值比较,因为 equals 会同时比较无标度值和标度这两个内容。

BigDecimal a = new BigDecimal("0.1"); BigDecimal b = new BigDecimal("0.10"); System.out.println(a.equals(b)); // 输出: false

我们都知道 0.1 和 0.10 在数值上是相等的,但 equals 的结果却是 false。这是因为:

  • a 的无标度值是 1,标度是 1
  • b 的无标度值是 10,标度是 2

虽然值相同,但它们的标度不同,所以 equals 返回 false。

compareTo

比较 BigDecimal 大小时应该使用 compareTo 方法,返回值为 1、-1、0,分别代表大于、小于、等于。

BigDecimal a = new BigDecimal("0.1"); BigDecimal b = new BigDecimal("0.10"); System.out.println(a.compareTo(b)); // 输出: 0,表示相等 BigDecimal c = new BigDecimal("0.2"); System.out.println(a.compareTo(c)); // 输出: -1,表示 a < c System.out.println(c.compareTo(a)); // 输出: 1,表示 c > a

创建 BigDecimal 的正确姿势

创建 BigDecimal 时,建议使用 String 类型的构造方法,也就是new BigDecimal("0.1")这样。

BigDecimal right = new BigDecimal("0.1"); System.out.println(right); // 输出: 0.1 BigDecimal wrong = new BigDecimal(0.1); System.out.println(wrong); // 输出: 0.1000000000000000055511151231257827021181583404541015625

如果你用了new BigDecimal(0.1)的方式,创建出来的值其实也不是 0.1,而是一个近似值。这是因为传入的 double 本身就已经是近似值了,BigDecimal 只是忠实地把这个近似值保存下来而已。

还有一个更方便的方法:
BigDecimal bd = BigDecimal.valueOf(0.1); System.out.println(bd); // 输出: 0.1

valueOf方法内部会先把 double 转成 String,再调用 String 构造方法,所以也是安全的。

常用的 BigDecimal 运算

BigDecimal a = new BigDecimal("10.5"); BigDecimal b = new BigDecimal("2.3"); // 加法 System.out.println(a.add(b)); // 12.8 // 减法 System.out.println(a.subtract(b)); // 8.2
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/1 1:44:42

面向切面编程和面向对象编程的区别,两者有冲突吗?

面向切面编程&#xff08;AOP&#xff09;和面向对象编程&#xff08;OOP&#xff09;是两种不同的编程范式&#xff0c;它们各自解决不同类型的问题&#xff0c;并且通常可以互补使用而不是冲突。OOP 关注的是对象和它们的交互&#xff0c;强调的是数据和行为的封装。 AOP关注…

作者头像 李华
网站建设 2026/7/1 1:44:18

乳牙蛀了不用管?避开护牙误区,科学守护孩子恒牙健康

乳牙蛀了不用管&#xff1f;避开护牙误区&#xff0c;科学守护孩子恒牙健康“孩子才几岁&#xff0c;乳牙迟早要换&#xff0c;蛀了也不用在意。” 这是儿童口腔科普中最常遇到的认知误区。很多家长觉得乳牙是 “临时牙齿”&#xff0c;不需要费心护理&#xff0c;却忽略了乳牙…

作者头像 李华
网站建设 2026/7/1 1:42:56

什么是线程池?

说说工作中线程池的应用 可以讲在拼团中怎么用的。 3、说一下线程池的工作流程 线程池刚创建时&#xff0c;里面没有一个线程。任务队列是作为参数传进来的&#xff08;因为可以选择&#xff09;。当调用excute()方法添加一个任务时&#xff0c;线程池会做如下判断&#xff1a…

作者头像 李华
网站建设 2026/7/1 1:41:58

2026年全屋智能方案选购参考:海尔智慧家庭实测梳理

最近不少准备装修或者做局部焕新的朋友都在问&#xff0c;全屋智能装起来坑多&#xff0c;要么设备之间不兼容&#xff0c;要么断网就全瘫&#xff0c;要么操作复杂老人小孩用不明白&#xff0c;有没有相对省心的选项&#xff1f;我们整理了目前市场上主流的方案&#xff0c;其…

作者头像 李华
网站建设 2026/7/1 1:41:50

基于51单片机的智能温控风扇 红外遥控 人体感应控制 2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)

基于51单片机的智能温控风扇 红外遥控 人体感应控制 2(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_降重降ai&#xff09; 特点&#xff1a;一个成品的好坏要看产品功能的完整性&#xff0c;本产品有单片机处理单元&#xff0c;温度检测部分&#xff0c;人机交互液…

作者头像 李华
网站建设 2026/7/4 8:28:05

实战掌握Adobe软件激活:全面解析GenP 3.0破解工具高效配置

实战掌握Adobe软件激活&#xff1a;全面解析GenP 3.0破解工具高效配置 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP 还在为Adobe Creative Cloud高昂的订阅费用而…

作者头像 李华