从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

    • 我的 Java 学习路线
    • 安装 Java
    • Java 数据类型

    • Java 多版本配置
    • 面向对象

    • Java 核心类

      • 基本包装类型
      • BigInteger 类
      • BigDecimal 类
        • 常用方法
        • 演示 ArithmeticException 异常
        • 演示重载的 divide
        • BigDecimal 的缺点
        • 小结
      • 深入 BigDecimal
    • IO

    • Java 与时间

    • 异常处理

    • 哈希和加密算法

    • Java8 新特性

    • 网络编程

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • JavaSE
  • Java 核心类
2022-12-16
目录

BigDecimal 类

# BigDecimal 类

如果要进行高精度浮点值的计算,可以用 java.math 包中的 BigDecimal 类。

什么时候我们需要用到高精度的浮点数运算呢?当处理金额的时候,绝对不能有精度损失;而之前我们说过,double 和 float 是不精确的(参考我写的 Java 中的浮点数 (opens new window))

为了解决浮点数的精度问题,一些编程语言引入了十进制的小数 Decimal。Decimal 在不同社区中都十分常见,如果编程语言没有原生支持 Decimal,我们在开源社区也一定能够找到使用特定语言实现的 Decimal 库。例如 Java 通过 BigDecimal 提供了无限精度的小数。

BigDecimal 类可以用于表示任意精度的数。特点如下:

  • 可以用 new BigDecimal(String)来创建对象,或者 new BigDecimal(double),构造方法有很多。
  • 可以用 add、substr、multiple、divide和 remainder(取余)方法来完成算数运算
  • 是不可变的。任何针对 BigDecimal 对象的修改都会产生一个新对象(类似 String)
  • 如果除法运算过程不能终止(例如除不尽),会抛出 ArithmeticException 异常;但可以使用重载的方法来指定尺度(保留几位小数)和舍入方式(向上取整、向下取整还是四舍五入)来避免异常:divide(BigDecimal d, int scale, int roundingMode)

# 常用方法

BigDecimal 有很多的构造方法,例如:

  • BigDecimal(int) 创建一个具有参数所指定整数值的对象
  • BigDecimal(double) 创建一个具有参数所指定双精度值的对象
  • BigDecimal(long) 创建一个具有参数所指定长整数值的对象
  • BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象

常用的方法如下:

  • add(BigDecimal) BigDecimal 对象中的值相加,然后返回这个对象。
  • subtract(BigDecimal) BigDecimal 对象中的值相减,然后返回这个对象。
  • multiply(BigDecimal) BigDecimal 对象中的值相乘,然后返回这个对象。
  • divide(BigDecimal) BigDecimal 对象中的值相除,然后返回这个对象。
  • toString() 将 BigDecimal 对象的数值转换成字符串。
  • doubleValue() 将 BigDecimal 对象中的值以双精度数返回。
  • floatValue() 将 BigDecimal 对象中的值以单精度数返回。
  • longValue() 将 BigDecimal 对象中的值以长整数返回。
  • intValue() 将 BigDecimal 对象中的值以整数返回。

# 演示 ArithmeticException 异常

import java.math.BigDecimal;

public class BigDecimalDemo1 {
  public static void main(String[] args) {
    BigDecimal a = new BigDecimal(1.0);
    BigDecimal b = new BigDecimal(3);
    a.divide(b);
  }
}
1
2
3
4
5
6
7
8
9
> javac BigDecimalDemo1.java
> java BigDecimalDemo1  
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.  
	at java.math.BigDecimal.divide(BigDecimal.java:1693)
        at BigDecimalDemo1.main(BigDecimalDemo1.java:7)
1
2
3
4
5

# 演示重载的 divide

import java.math.BigDecimal;

public class BigDecimalDemo2 {
  public static void main(String[] args) {
    BigDecimal a = new BigDecimal(1.0);
    BigDecimal b = new BigDecimal(3);
    System.out.println( a.divide(b, 20, BigDecimal.ROUND_UP));


    BigDecimal d = new BigDecimal("-1");
    BigDecimal e = new BigDecimal("3");
    System.out.println(d.divide(e, 20, BigDecimal.ROUND_UP));
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

编译和运行:

> javac BigDecimalDemo2.java  
> java BigDecimalDemo2  
0.33333333333333333334
-0.33333333333333333334
1
2
3
4

这里舍入方式用了 BigDecimal 的 ROUND_UP,表明向上取整。BigDecimal 还有以下取整方式(都是 BigDecimal 的成员 static 变量,本质都是 int 值,只不过 BigDecimal 定义了个别名,方便)

  • ROUND_UP 远离零的舍入模式。 始终对非零舍弃部分前面的数字加 1。例如:2.36 -> 2.4 (2.4 比 2.36 更加远离 0)。可以理解为删除多余的小数位,并在最后一个数字上 +1.

  • ROUND_DOWN 接近零的舍入模式。例如:2.36 -> 2.3 (2.3 比 2.36 更靠近 0)。

  • ROUND_CEILING 接近正无穷大的舍入模式。

    如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;

    如果为负,则舍入行为与 ROUND_DOWN 相同。

    相当于是 ROUND_UP 和 ROUND_DOWN 的合集

  • ROUND_FLOOR 接近负无穷大的舍入模式。与 ROUND_CEILING 正好相反

  • ROUND_HALF_UP 就是四舍五入,例如:2.35 -> 2.4

  • ROUND_HALF_DOWN 就是五舍六入,例如:2.35 -> 2.3

  • ROUND_HALF_EVEN 如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同(四舍五入);如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同(五舍六入)。例如:1.15 -> 1.1,1.25 -> 1.2

    在重复进行一系列计算时,此舍入模式可以在统计上将累加错误减到最小。此舍入模式也称为“银行家舍 入法”,主要在美国使用。

  • ROUND_UNNECESSARY 这个模式名副其实,确实是"unnecessary",其实就和没设置精度一样, 如果相除的结果不是精确值 则抛出一个 ArithmeticException 异常.

更多详见官方文档 BigDecimal (Java Platform SE 8 ) (opens new window)

小结:常用的就是 ROUND_HALF_UP、ROUND_UP 和 ROUND_DOWN,其它的看看就行

import java.math.BigDecimal;

public class BigDecimalDemo2 {
  public static void main(String[] args) {
    BigDecimal a = new BigDecimal(1.0);
    BigDecimal b = new BigDecimal(3);
    System.out.println("ROUND_UP:      " +  a.divide(b, 20, BigDecimal.ROUND_UP));  //0.33333333333333333334
    System.out.println("ROUND_DOWN:    " +  a.divide(b, 20, BigDecimal.ROUND_DOWN));  //0.33333333333333333333
    System.out.println("ROUND_CEILING: " +  a.divide(b, 20, BigDecimal.ROUND_CEILING));  //0.33333333333333333334
    System.out.println("ROUND_FLOOR:   " +  a.divide(b, 20, BigDecimal.ROUND_FLOOR));  //0.33333333333333333333
    System.out.println("ROUND_HALF_UP: " +  a.divide(b, 20, BigDecimal.ROUND_HALF_UP));  //0.33333333333333333333


    BigDecimal d = new BigDecimal("-1");
    BigDecimal e = new BigDecimal("3");
    System.out.println("ROUND_UP:      " +  d.divide(e, 20, BigDecimal.ROUND_UP)); //-0.33333333333333333334
    System.out.println("ROUND_DOWN:    " +  d.divide(e, 20, BigDecimal.ROUND_DOWN)); //-0.33333333333333333333
    System.out.println("ROUND_CEILING: " +  d.divide(e, 20, BigDecimal.ROUND_CEILING)); //-0.33333333333333333333
    System.out.println("ROUND_FLOOR:   " +  d.divide(e, 20, BigDecimal.ROUND_FLOOR)); //-0.33333333333333333334
    System.out.println("ROUND_HALF_UP: " +  d.divide(e, 20, BigDecimal.ROUND_HALF_UP)); //-0.33333333333333333334

    BigDecimal f = new BigDecimal("0.7");
    BigDecimal g = new BigDecimal("2");
    System.out.println("ROUND_HALF_UP:   " +  f.divide(g, 1, BigDecimal.ROUND_HALF_UP)); //0.4
    System.out.println("ROUND_HALF_DOWN: " +  f.divide(g, 1, BigDecimal.ROUND_HALF_DOWN)); //0.3
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# BigDecimal 的缺点

像 BigInteger 和 BigDecimal 这种大数类的运算效率肯定是不如原生类型效率高,代价还是比较昂贵的,是否选用需要根据实际场景来评估。我们来尝试比较一下:

import java.math.BigDecimal;

public class BigDecimalDemo4 {

  public static void main(String[] args) {
    long startTime = System.currentTimeMillis();
    double d = 1.0;
    for(int i = 0; i < 100000; i++){
      d++;
    }
    long endTime = System.currentTimeMillis();
    System.out.println("double程序运行时间:" + (endTime - startTime) + "ms");

    startTime = System.currentTimeMillis();
    BigDecimal b = new BigDecimal("1.0");
    for(int i = 0; i < 100000; i++){
      b.add(b);
    }
    endTime = System.currentTimeMillis();
    System.out.println("BigDecimal程序运行时间:" + (endTime - startTime) + "ms");

  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

编译和运行:

javac BigDecimalDemo4.java -encoding utf8   
java BigDecimalDemo4                      
double程序运行时间:1ms
BigDecimal程序运行时间:6ms
1
2
3
4

可以看到,10 万次的情况下,BigDecimal 的时间是 double 类型的 6 倍。

注意:以上结果是在我本地电脑下运行的(Win10,i9),不同电脑的运行结果不一样。

什么时候不精确的值也可以呢?例如用户平均价格,店铺评分,用户经纬度等本身就没有精准值一说的推荐使用 double 或 float,写代码更方便,计算效率也高得多;

# 小结

BigDecimal 的原理和坑还是挺难理解的,希望大家理解原理后,反复阅读知识点并动手实践。

上次更新: 2025/6/3 09:31:54
BigInteger 类
深入 BigDecimal

← BigInteger 类 深入 BigDecimal→

最近更新
01
新闻合订本 2025-10
10-31
02
2025 年 10 月记
10-30
03
用 AI 批量优化思源笔记排版
10-15
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式