泪伤荡的编程指南 泪伤荡的编程指南
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM篇
  • 新特性
  • 进阶篇
  • 网络
  • 操作系统
  • 数据结构与算法
  • 硬件
  • 基础篇
  • MySql
  • Oracle
  • PostgreSQL
  • 达梦
  • Redis
  • Mongodb
  • Hive
  • 数据库比较
  • Spring
  • SpringMvc
  • SpringBoot
  • Hibernate
  • iBatis
  • Mybatis
  • Mybatis-plus
  • Mybatis-plus-join
  • 各个框架对比
  • UML画图
  • 设计须知
  • 开发流程
  • 开发理论
  • 架构体系
  • 设计模式
  • 开源知识
  • 分布式解决方案
  • SpringCloud
  • API网关
  • 注册中心
  • 配置中心
  • 服务调用
  • 分布式事务
  • 消息队列
  • 调度作业
  • 链路追踪
  • 服务保障
  • 搜索引擎Elk
  • 安全框架
  • 监控体系
  • 部署容器
  • Netty
  • Tomcat
  • Nginx
  • 图片云存储
  • 云存储
  • 虚拟机Linux
  • 项目部署
  • 容器部署
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • Bug记录仓库
  • 随笔
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 视频网站
  • 音乐网站
  • 商城网站
  • 论坛网站
  • scrm项目
  • Yudao-cloud
  • RuoYi-Vu-cloud
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
  • Java术语
  • 命名英语
  • 业务英语
  • 表字段英语
  • 包名英语
Github (opens new window)
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM篇
  • 新特性
  • 进阶篇
  • 网络
  • 操作系统
  • 数据结构与算法
  • 硬件
  • 基础篇
  • MySql
  • Oracle
  • PostgreSQL
  • 达梦
  • Redis
  • Mongodb
  • Hive
  • 数据库比较
  • Spring
  • SpringMvc
  • SpringBoot
  • Hibernate
  • iBatis
  • Mybatis
  • Mybatis-plus
  • Mybatis-plus-join
  • 各个框架对比
  • UML画图
  • 设计须知
  • 开发流程
  • 开发理论
  • 架构体系
  • 设计模式
  • 开源知识
  • 分布式解决方案
  • SpringCloud
  • API网关
  • 注册中心
  • 配置中心
  • 服务调用
  • 分布式事务
  • 消息队列
  • 调度作业
  • 链路追踪
  • 服务保障
  • 搜索引擎Elk
  • 安全框架
  • 监控体系
  • 部署容器
  • Netty
  • Tomcat
  • Nginx
  • 图片云存储
  • 云存储
  • 虚拟机Linux
  • 项目部署
  • 容器部署
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • Bug记录仓库
  • 随笔
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 视频网站
  • 音乐网站
  • 商城网站
  • 论坛网站
  • scrm项目
  • Yudao-cloud
  • RuoYi-Vu-cloud
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
  • Java术语
  • 命名英语
  • 业务英语
  • 表字段英语
  • 包名英语
Github (opens new window)
  • 开发工具篇

    • idea设置

      • 配置篇
      • 快捷键篇
      • debug篇
      • 插件篇
    • 玩转Git

      • 基础知识梳理
      • Git配置相关操作
      • git修改已提交人的用户名和邮箱
      • git提交规范
      • 常见问题
    • Maven相关

      • Maven简介
      • Maven常用命令
      • 依赖管理
      • Maven生命周期与插件
      • Maven项目管理工具
    • Apifox使用小结
    • nvm使用小结
    • JMeter使用小记
  • 工具库篇

    • lombok工具库

      • lombok注解使用小结
      • Builder用法解析
      • 异常相关注解
    • EasyExcel小记

      • 工具类
      • 文件导入
      • 文件导出
    • 定时任务相关

      • 基础入门
      • SpringTask学习
      • Quartz学习
    • Hutool工具库

      • 图片压缩
    • 极光推送学习
    • OkHttp学习
    • BigDecimal类详解
      • 为什么使用 BigDecimal?
      • 创建 BigDecimal 对象
      • 获取小数位数
      • 基本运算
      • 舍入模式
      • 比较操作
      • 格式化输出
      • 处理精度问题
      • 结论
      • 学习参考
    • PdfBox学习
  • 开发技巧篇

    • 常见数据校验注解
    • 字符串拼接的5种方式
    • 遍历集合的N种方式
    • 集合使用注意事项总结
    • MP使用小记
    • Stream流技巧总结
    • 字符串处理最佳实践
    • SQL语句优化
    • 时间字段处理小记
    • Curl用法解析
    • 列表分页的两种实现方案
    • HashMap根据value获取key
    • Map的7种遍历方式
    • 唯一索引和逻辑删除冲突解决方法
    • 正则表达式
    • 二维码扫码登录学习
    • 脱敏最佳实践
    • 日志记录相关
  • 工具类系列

    • 手写一个文件阅读器
    • 手写一个运行耗时计算器
    • 自定义实现Java Bean属性中列表元素格式校验注解及其实现
    • 父子工程项目搭建
    • 自制代码生成器
  • 随笔

    • HttpServletRequest知识小结
    • Spring MVC 项目构建流程
    • 虚拟机固定ip地址
    • 项目部署
    • 深入理解数组
    • IIS使用小记
    • From的两种类型解析
    • 开发疑惑
    • 开发小记
    • bug解决
  • 开发日常
  • 工具库篇
泪伤荡
2024-04-16
目录

BigDecimal类详解

# Java 中的 BigDecimal 类详解

在 Java 编程中,处理浮点数时经常会遇到精度问题。为了解决这个问题,Java 提供了一个 BigDecimal 类,它提供了精确的浮点数运算。本文将详细介绍 BigDecimal 类的使用方法和一些常见场景。

# 为什么使用 BigDecimal?

在 Java 中,float 和 double 类型的变量是基于 IEEE 754 标准的浮点数表示,这可能会导致精度损失。例如,0.1 无法在二进制浮点数中精确表示,这可能会导致计算结果与预期不符。而 BigDecimal 类则提供了任意精度的浮点数运算,非常适合处理金融数据。

# 创建 BigDecimal 对象

BigDecimal 对象可以通过多种方式创建:

  • BigDecimal(int) 创建一个具有参数所指定整数值的对象。
  • BigDecimal(double) 创建一个具有参数所指定双精度值的对象。 【不推荐使用】
  • BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
  • BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。【推荐使用】
    @Test
    void testConstructor() {
        System.out.println("==================构造器==================");

        // 通过 int 值构造
        BigDecimal intBig = new BigDecimal(10);
        System.out.println("intBig = " + intBig);

        // 通过 double 值构造,会有精度问题(不推荐)
        BigDecimal doubleBig = new BigDecimal(20.45);
        System.out.println("doubleBig = " + doubleBig);

        // 通过 long 值构造
        BigDecimal longBig = new BigDecimal(30L);
        System.out.println("longBig = " + longBig);

        // 通过 String 值构造(推荐)
        BigDecimal stringBig = new BigDecimal("123.45");
        System.out.println("stringBig = " + stringBig);

        // 将 double 值转换成 String 类型再构造
        Double doubleValue = new Double(20.45);
        BigDecimal doubleBig2 = new BigDecimal(doubleValue.toString());
        System.out.println("doubleBig2 = " + doubleBig2);

        // intBig = 10
        // doubleBig = 20.449999999999999289457264239899814128875732421875
        // longBig = 30
        // stringBig = 123.45
        // doubleBig2 = 20.45
    }
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
28
29
30
31

也可以用 BigDecimal.valueOf() 方法:

BigDecimal multiplier = BigDecimal.valueOf(100);
1

# 获取小数位数

// 获取xia
int scale = amountDecimal.scale();
1
2

# 基本运算

BigDecimal 提供了一系列的方法来进行基本的数学运算,包括加法、减法、乘法和除法:

    @Test
    void testAsmd() {
        System.out.println("==================加减乘除==================");

        BigDecimal value1 = new BigDecimal("100.00");
        BigDecimal value2 = new BigDecimal("12.34");

        // 加法
        BigDecimal sum = value1.add(value2); // 112.34
        System.out.println("sum = " + sum);

        // 减法
        BigDecimal difference = value1.subtract(value2); // 87.66
        System.out.println("difference = " + difference);

        // 乘法
        BigDecimal product = value1.multiply(value2); // 1234.0000
        System.out.println("product = " + product);

        // 除法(四舍五入,保留两位小数)
        BigDecimal quotient = value1.divide(value2, 2, RoundingMode.HALF_UP); // 8.10
        System.out.println("quotient = " + quotient);

        // sum = 112.34
        // difference = 87.66
        // product = 1234.0000
        // quotient = 8.10
    }
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
28

在上面的例子中,divide 方法的第二个参数是小数位数,第三个参数是舍入模式。RoundingMode 是 BigDecimal 中的一个枚举,用于定义舍入行为。

# 舍入模式

  • ROUND_UP :向远离 0 的方向舍入
  • ROUND_DOWN :向零方向舍入
  • ROUND_CEILING :向正无穷方向舍入
  • ROUND_FLOOR :向负无穷方向舍入
  • ROUND_HALF_UP :向(距离)最近的一边舍入,如果两边(的距离)是相等时,向上舍入, 1.55 保留一位小数结果为 1.6,也就是我们常说的“四舍五入”
  • ROUND_HALF_DOWN :向(距离)最近的一边舍入,如果两边(的距离)是相等时,向下舍入, 例如1.55 保留一位小数结果为1.5
  • ROUND_HALF_EVEN :向(距离)最近的一边舍入,如果两边(的距离)是相等时,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
  • ROUND_UNNECESSARY :计算结果是精确的,不需要舍入模式
@Test
    void testRoundingMode() {
        System.out.println("==================舍入模式==================");
        // 注意:断言请求的操作具有精确的结果,因此【不需要舍入】。
        // 如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。 -- 如果这里小数选择保留 1 位就会抛异常
        BigDecimal bigDecimal = new BigDecimal("1.55").setScale(2, RoundingMode.UNNECESSARY);
        System.out.println("Rounding mode: " + RoundingMode.UNNECESSARY + ", Value: " + bigDecimal);

        testRoundingMode(new BigDecimal("1.55"), RoundingMode.values());

        /* Rounding mode: UNNECESSARY, Value: 1.55
        Rounding mode: UP, Value: 1.6
        Rounding mode: DOWN, Value: 1.5
        Rounding mode: CEILING, Value: 1.6
        Rounding mode: FLOOR, Value: 1.5
        Rounding mode: HALF_UP, Value: 1.6
        Rounding mode: HALF_DOWN, Value: 1.5
        Rounding mode: HALF_EVEN, Value: 1.6 */
    }

    private static void testRoundingMode(BigDecimal value, RoundingMode[] modes) {
        for (RoundingMode mode : modes) {
            BigDecimal roundedValue = value.setScale(1, mode);
            System.out.printf("Rounding mode: %s, Value: %s%n", mode, roundedValue);
        }
    }
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

# 比较操作

BigDecimal 类提供了比较两个对象的方法,这些方法返回一个整数,它表示调用对象与参数对象的相对大小:

    @Test
    void testCompare() {
        System.out.println("==================比较操作==================");
        BigDecimal value = new BigDecimal("100.00");

        BigDecimal value1 = new BigDecimal("99.00");
        BigDecimal value2 = new BigDecimal("100.00");
        BigDecimal value3 = new BigDecimal("101.00");

        // 比较值
        int cmp1 = value.compareTo(value1);
        int cmp2 = value.compareTo(value2);
        int cmp3 = value.compareTo(value3);
        System.out.println("cmp1 = " + cmp1); // 1
        System.out.println("cmp2 = " + cmp2); // 0
        System.out.println("cmp3 = " + cmp3); // -1

        // 检查是否相等
        boolean isEqual1 = value.equals(value1);
        boolean isEqual2 = value.equals(value2);
        boolean isEqual3 = value.equals(value3);
        System.out.println("isEqual1 = " + isEqual1); // false
        System.out.println("isEqual2 = " + isEqual2); // true
        System.out.println("isEqual3 = " + isEqual3); // false

        // 检查是否大于
        boolean isGreaterThan1 = value.compareTo(value1) > 0;
        boolean isGreaterThan2 = value.compareTo(value2) > 0;
        boolean isGreaterThan3 = value.compareTo(value3) > 0;
        System.out.println("isGreaterThan1 = " + isGreaterThan1); // true
        System.out.println("isGreaterThan2 = " + isGreaterThan2); // false
        System.out.println("isGreaterThan3 = " + isGreaterThan3); // false
    }
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
28
29
30
31
32
33

# 格式化输出

DecimalFormat 类允许您自定义数字格式,包括小数点后的位数、分组分隔符、甚至前缀和后缀。

  • 它是 java.text 包中的一个类,用于格式化十进制数字。
    @Test
    void testFormat() {
        System.out.println("==================格式化输出==================");

        DecimalFormat df = new DecimalFormat("#,##0.00");
        double number1 = 123456.789;
        double number2 = 1233123456.783;
        String formattedNumber1 = df.format(number1); // 123,456.79
        String formattedNumber2 = df.format(number2); // 1,233,123,456.78
        System.out.println("Formatted Number1: " + formattedNumber1);
        System.out.println("Formatted Number2: " + formattedNumber2);
    }
// 这个例子展示了如何使用 DecimalFormat 对象来格式化一个 double 类型的数字,并添加了【千位分隔符】和【小数点后两位】的格式。
1
2
3
4
5
6
7
8
9
10
11
12
13

解析 "#,##0.00" 这个模式字符串:

  • #:表示如果存在数字则显示数字。如果位数不足,它不会填充领先符号。在分组分隔符前的 # 表示尽可能多地使用分组分隔符。
  • ,:表示分组分隔符,通常用于分隔千位、百万位等。在这个模式中,它会在每三位数字后添加一个逗号。
  • ##0:表示至少有一个有效数字(非零数字),如果数字不足,它将用零填充。## 表示如果存在数字则显示数字,如果不存在则不显示。这里的 0 表示如果数字不足,将用零填充。
  • .00:表示小数点后有两位固定位数,即使它们是零。

综上所述,"#,##0.00" 这个模式会将数字格式化为带有千位分隔符的格式,并且小数点后总是有两位小数,如果小数部分不足两位,则用零填充。

# 处理精度问题

在使用 BigDecimal 时,需要注意精度问题。例如,直接使用 double 构造器可能会引入初始的精度问题:

// 错误的示例,可能会有精度问题
BigDecimal bd = new BigDecimal(0.1);
1
2

正确的做法是使用字符串构造器:

// 正确的示例
BigDecimal bd = new BigDecimal("0.1");
1
2

# 结论

BigDecimal 类是 Java 中处理精确数值计算的重要工具。

  • 它提供了丰富的 API 来执行各种数学运算,并能够避免 float 和 double 类型的精度问题。
  • 在处理金融数据或需要精确计算的场景中,BigDecimal 是首选的数据类型。
  • 推荐使用 字符串 构造器来创建 BigDecimal 对象,以确保精度不受损失。

# 学习参考

Java中BigDecimal详解及应用 - 知乎 (zhihu.com) (opens new window)

上次更新: 2024/10/26 02:01:17
OkHttp学习
PdfBox学习

← OkHttp学习 PdfBox学习→

Theme by Vdoing | Copyright © 2024-2025 泪伤荡 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式