当前位置: 首页 > news >正文

为什么不该用 Double 表示金额及解决方案

众所周知,double 和 float 这些浮点数其实是不精确的。

比如 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.1BigDecimal 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// 乘法
System.out.println(a.multiply(b));  // 24.15// 除法(需要指定精度和舍入模式)
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP));  // 4.57

注意除法操作时,如果不指定精度,遇到除不尽的情况会抛出 ArithmeticException。所以建议都加上精度和舍入模式。

总之,涉及金额计算时,千万别图省事用 double,老老实实用 BigDecimal 才是王道。

http://www.hskmm.com/?act=detail&tid=28173

相关文章:

  • Windows开发环境安装备忘录
  • Vue.use(Vuex)
  • [Gym-100343E]Convex Permutominoes 题解
  • MyBatis 中的动态 SQL 的相关使用方法(Javaee/MyBatis) - 教程
  • 网络优化问题
  • Java环境安装备忘录
  • 深入解析:【Spring MVC终极指南】一文掌握请求处理与响应!从Servlet原生方式到SpringMVC高效优雅写法
  • foobar2000 v2.25.2 汉化版
  • 比特币地址投毒攻击深度剖析
  • 为什么大家都爱用微擎?这几点真的太香了
  • 【JS逆向百例】某坤行 1101,雪球 1038,新 acw_sc__v2 逆向分析
  • JAVA 的模板方法模式解析
  • 《构建之法-现代软件工程》 -阅读和提问作业1
  • 计算机视觉与AI在人体成分分析中的技术突破
  • 2024-网鼎杯web-PyBlockly
  • 关于微信小程序申请地理位置接口申请
  • c++学习总结
  • 2025 年大闸蟹蟹卡 / 大闸蟹礼盒 / 大闸蟹礼券 / 好蟹汇大闸蟹选择指南:生态养殖与全国服务双保障解析
  • 分享一个超级耐玩的游戏 转载 植物大战僵尸融合版最新版(v3.0.1)支持安卓版+PC电脑版
  • 【Go 语言神器】iota 到底是什么?为什么高手都爱用它?
  • 2025 年模具生产厂家最新推荐榜单:聚焦优质源头企业,助力工程采购精准选型框格梁模具/框格梁模板/混泥土模具厂家推荐
  • 2025 年最新推荐仿石漆厂家实力厂家口碑排行榜:精选优质环保外墙内墙涂料企业权威揭晓
  • oracle查询存储过程和函数中是否包含某个字符串
  • Qoder 负责人揭秘:Qoder 产品背后的思考与未来发展
  • 2025 年半导体晶片生产厂家最新推荐榜单:专利技术与规模化供货能力双维度深度解析
  • 2025 年水产养殖降氨氮亚盐厂家最新推荐排行榜 ,助力北方对虾鱼塘螃蟹池塘养殖户轻松选购优质产品
  • CS:APP学习笔记之程序的机器级表示(三) - Invinc
  • EHOME视频平台EasyCVR构建全协议、全场景融合的视频监控中枢
  • GA/T 1400视图库平台EasyCVR平台GB28181与1400级联方式全解析
  • 2025 年玻璃钢水箱生产厂家最新推荐榜单:含 30 吨 / 订做 / 消防 / 方形 / 拼装式 / 屋顶 / 大型产品,从产能与服务双维度精选优质企业