为什么 BigDecimal 不丢失精度?

AI 概述
本文讲解Java中BigDecimal保证精度的底层原理:float/double因二进制存储无法精确表示十进制小数,易丢失精度;BigDecimal采用**大整数+标度**模拟十进制,基于整数运算且严格遵循十进制规则,精度可控。同时提醒避免用double构造、除法需指定舍入模式,确保金额等计算精准。
目录
文章目录隐藏
  1. 一、先搞懂:float/double 为什么会丢精度?
  2. 二、BigDecimal 为什么不丢精度?核心原理一句话
  3. 三、它为什么能精确?关键点有三个
  4. 四、一个例子看懂内部计算
  5. 五、重要提醒:BigDecimal 不代表绝对安全
  6. 六、总结

为什么 BigDecimal 不丢失精度?

在 Java 里做金额、税费、利息计算,几乎所有人都知道一条铁律:

禁止使用 float、double,必须用 BigDecimal。

因为 float/double 动不动就出现这种诡异结果:

System.out.println(0.1 + 0.2);
// 输出:0.30000000000000004

而 BigDecimal 就能老老实实算出 0.3。

很多人只知道“要用 BigDecimal”,却不知道:它到底凭什么能保证不丢失精度?

今天从底层原理讲清楚,看完你就彻底通透。

一、先搞懂:float/double 为什么会丢精度?

计算机底层是二进制。

像 0.1 这种十进制小数,转换成二进制是一个无限循环小数:

0.1 → 二进制:0.00011 0011 0011 0011…(无限循环)

而 float、double 的存储空间是固定长度的:

  • float:32 位;
  • double:64 位。

无限循环的二进制,只能被截断存储。

一截断,就丢精度;一计算,误差就放大。

这就是为什么 0.1 + 0.2 ≠ 0.3。

不是计算错了,是存储方式天生不精确。

二、BigDecimal 为什么不丢精度?核心原理一句话

BigDecimal 不用二进制浮点存储,而是用“整数 + 标度”来模拟十进制小数。

它把一个小数拆成两部分存在内部:

  1. unscaled value:一个任意长度的大整数(BigInteger);
  2. scale:小数点向右移动几位(标度/指数)。

比如:

123.45
= 12345 × 10^-2

在 BigDecimal 内部就是:

  • unscaled value = 12345;
  • scale = 2。

再比如:

0.1
= 1 × 10^-1
  • unscaled value = 1;
  • scale = 1。

三、它为什么能精确?关键点有三个

1. 完全基于整数运算

BigDecimal 做加减乘除,本质上都是整数运算。

整数在二进制里可以精确表示,不会循环、不会截断。

只要内存够,整数可以无限大,不存在精度上限。

2. 严格模拟十进制,而非二进制浮点

double 是二进制小数,天生无法精确表示很多十进制小数。

BigDecimal 是十进制表示:

  • 你写 0.1,它内部就是 1/10;
  • 你写 0.25,它内部就是 25/100。

完全按照人类十进制数学规则计算,自然不会乱飘。

3. 标度(scale)可控,舍入策略明确

你可以精确控制:

  • 保留几位小数;
  • 超出部分如何舍入(四舍五入、向上取整、截断等)。

只要你不主动舍入,它就绝对不会偷偷丢精度。

四、一个例子看懂内部计算

以 0.1 + 0.2 为例:

  •  0.1 → 1 × 10^-1
  •  0.2 → 2 × 10^-1

相加:

(1 + 2) × 10^-1 = 3 × 10^-1 = 0.3

全程整数运算,完美精确。

换成 double:

  • 0.1 是近似值;
  • 0.2 也是近似值;
  • 加完还是近似值。

结果自然错乱。

五、重要提醒:BigDecimal 不代表绝对安全

虽然 BigDecimal 本身精度可靠,但用法不对照样丢精度。

坑 1:用 double 构造 BigDecimal(经典错误)

// 错误!
BigDecimal a = new BigDecimal(0.1);

0.1 传入时已经是不精确的 double,

BigDecimal 再精确也救不回来。

正确写法

BigDecimal a = new BigDecimal("0.1");

坑 2:除法不指定舍入模式

BigDecimal.ONE.divide(new BigDecimal("3"));

无限小数会直接抛异常:ArithmeticException: Non-terminating decimal expansion

正确写法

BigDecimal.ONE.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);

六、总结

为什么 BigDecimal 不丢失精度?

  1. 放弃二进制浮点,改用十进制思想;
  2. 内部用大整数 + 标度表示小数;
  3. 所有运算都是精确整数运算;
  4. 舍入行为可控,不会偷偷截断。

以上关于为什么 BigDecimal 不丢失精度?的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

1

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 为什么 BigDecimal 不丢失精度?

发表回复