首页 > 编程语言 >Java项目日常开发中使用BigDecimal常见问题总结

Java项目日常开发中使用BigDecimal常见问题总结

时间:2023-09-03 22:33:08浏览次数:35  
标签:常见问题 Java BigDecimal successCount System println new out

Java项目中有计算精度要求高的场景(如金额计算)会使用BigDecimal类型来代替DoubleFloat
本文整理了一些日常开发中使用BigDecimal值得注意的问题和代码实例。

  1. BigDecimal初始化时入参应使用String类型

例1:

BigDecimal x = new BigDecimal(3.33);
BigDecimal y = new BigDecimal("3.33");
System.out.println("x=" + x);
System.out.println("y=" + y);

输出:

x=3.3300000000000000710542735760100185871124267578125
y=3.33

使用double类型初始化的BigDecimal对象,出现了精度丢失的情况,应使用String类型值来初始化。

  1. BigDecimal初始化使用String类型时,需注意字符串前后不要有空白符

例2:

BigDecimal a = new BigDecimal("102.7");
System.out.println(a);

BigDecimal b = null;
try {
    b = new BigDecimal("102.7 ");
    System.out.println(b);
} catch (Exception e) {
    e.printStackTrace();
}

输出:

java.lang.NumberFormatException
	at java.math.BigDecimal.<init>(BigDecimal.java:497)
	at java.math.BigDecimal.<init>(BigDecimal.java:383)
	at java.math.BigDecimal.<init>(BigDecimal.java:809)
    ...

构建BigDecimal对象失败,抛NumberFormatException
如果字符串数值是第三方传入、mq消息、不小心人工输入空格等,需要注意这点。

  1. BigDecimal乘法运算结果格式化

例3:

BigDecimal c = new BigDecimal("13.14");
System.out.println(c.multiply(new BigDecimal("100")).toString());

输出:

1314.00

直接toString()方法,输出结果在小数点后多了2位0,如果想保留整数,可使用toBigInteger()方法。

// 1314
System.out.println(c.multiply(new BigDecimal("100")).toBigInteger());
  1. BigDecimal除法运算可能有除不尽的情况,应使用带scale参数的divide方法

例4:

try {
    BigDecimal nonTerminatingDecimal = new BigDecimal("1").divide(new BigDecimal("3"));
    System.out.println("nonTerminatingDecimal=" + nonTerminatingDecimal);
} catch (Exception e) {
    // e.printStackTrace();
}

输出:

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.math.BigDecimal.divide(BigDecimal.java:1693)
    ...

因为1除3除不尽,结果是无限循环小数,因此抛了ArithmeticException,应使用带scale参数的divide方法,如指定保留小数位数、舍入模式。

BigDecimal nonTerminatingDecimal2 = new BigDecimal("1").divide(new BigDecimal("3"), 2, RoundingMode.HALF_DOWN);
// 0.33
System.out.println(nonTerminatingDecimal2);
  1. BigDecimal做运算时注意次序问题

例5:

public static void main(String[] args) {
    Integer totalCount;
    Integer successCount;
    Integer errorCount;

    totalCount = 618;
    successCount = 60;
    errorCount = 18;
    // 12.48 ERROR
    calProgress1(totalCount, successCount, errorCount);
    // 12.62 OK
    calProgress2(totalCount, successCount, errorCount);

    System.out.println(StringUtils.center("分隔线", 50, "-"));

    totalCount = 61800;
    successCount = 60;
    errorCount = 18;
    // 0.00 ERROR
    calProgress1(totalCount, successCount, errorCount);
    // 0.13 ERROR
    calProgress2(totalCount, successCount, errorCount);
}

private static void calProgress1(Integer totalCount, Integer successCount, Integer errorCount) {
    BigDecimal calProgress = new BigDecimal(String.valueOf(successCount + errorCount))
            .multiply(new BigDecimal("100")
            .divide(new BigDecimal(String.valueOf(totalCount)), 2, RoundingMode.HALF_UP));
    System.out.println(calProgress.toString());
}

private static void calProgress2(Integer totalCount, Integer successCount, Integer errorCount) {
    BigDecimal calProgress2 = new BigDecimal(String.valueOf(successCount + errorCount))
            .multiply(new BigDecimal("100"))
            .divide(new BigDecimal(String.valueOf(totalCount)), 2, RoundingMode.HALF_UP);
    System.out.println(calProgress2.toString());
}

输出:

12.48
12.62
-----------------------分隔线------------------------
0.00
0.13

程序逻辑是计算百分比,公式:(成功数+失败数)*100/总数;

注意calProgress1calProgress2里代码的差别:
calProgress1方法里multiply(new BigDecimal("100")少写了个),导致先做了100/总数除法运算。

2个方法都能编译通过运行;
第1组输出由于数据量较小,结果差异不大,看上去都能计算出百分比,
第2组输出加大了总数量,calProgress1输出结果为0.00,丢失了部分精度。

例5是来自项目里的实例,某界面上展示数据处理的百分比进度,测试环境数据量较小,界面上看上去没问题,能从0-100%正常展示;
线上环境数据量较大,程序处理数据,界面上长时间展示0.00%。
修正为calProgress2里的代码,保证严格按照公式计算,先做乘法最后除法运算,除法运算指定保留小数位数和舍入方式。

标签:常见问题,Java,BigDecimal,successCount,System,println,new,out
From: https://www.cnblogs.com/cdfive2018/p/17668997.html

相关文章

  • Java线程池面试题集
    以面试官视角万字解读线程池10大经典面试题!(这个链接里难的要命)线程池面试连环炮,你能抗住几题?(这个链接里比较正常)什么是线程池?为什么要使用线程池?线程池就是采用池化思想管理线程的工具。JUC为我们提供了ThreadPoolExecutor体系类来帮助我们管理线程、并行执行任务。顶级接......
  • Java智慧工地源码:进度、质量、 成本、安全尽在掌握
    智慧工地是一种崭新的工程全生命周期管理理念。智慧工地是智慧城市在建筑施工领域的延伸,是智慧城市的“基石”。随着工程信息化管理技术的发展,移动互联网、物联网、AI、大数据等新技术与施工现场业务场景深度融合,智慧工地已成为建筑工地信息化建设和创新的热点。智慧工地将更多人......
  • java语言B/S医院HIS系统【springboot】
     医院云HIS全称为基于云计算的医疗卫生信息系统(Cloud-BasedHealthcareInformationSystem),是运用云计算、大数据、物联网等新兴信息技术,按照现代医疗卫生管理要求,在一定区域范围内以数字化形式提供医疗卫生行业数据收集、存储、传递、处理的业务和技术平台,实现区域内医疗卫生......
  • 基于JavaWeb实现智慧菜市场系统的设计与实现程序
    作者主页:编程指南针作者简介:Java领域优质创作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、腾讯课堂常驻讲师主要内容:Java项目、Python项目、前端项目、人工智能与大数据、简历模板、学习资料、面试题库、技术互助收藏点赞不迷路 关注作者有好处目录一,环境介绍二,项目......
  • Java垃圾回收全过程
    JVM中一次完整的垃圾回收(GC)过程。这里以HotSpot虚拟机为例,并将参考其中的一种常用的垃圾回收器:G1(Garbage-First)。堆的划分在HotSpotJVM中,堆(Heap)通常被划分为以下几个部分:年轻代(YoungGeneration):这部分进一步分为Eden区和两个Survivor区(S0和S1)。老年代(OldGeneration):存放长时间......
  • 《Java编程思想第四版》学习笔记22
    注意下面这两句话:1、针对g()和main(),Throwable类必须在违例规格中出现,因为fillInStackTrace()会生成一个Throwable对象的句柄。由于Throwable是Exception的一个基础类,所以有可能获得一个能够“掷”出的对象(具有Throwable属性),但却并非一个Exception(违例)。因此,在main()......
  • Java面试之用一个栈实现另一个栈的排序
    一题目一个栈中元素的类型为整数,现在想将该栈从顶到底按从大到小的顺序排序,只许申请一个栈。除此之外可以申请新的变量,但不能申请额外的数据结构。如何完成排序?二要求将要排序的栈记为stack,申请的辅助栈记为help。在stack上执行pop操作,弹出的元素记为cur。1.如果cur小于或等于hel......
  • 无涯教程-JavaScript - TTEST函数
    TTEST函数取代了Excel2010中的T.TEST函数。描述该函数返回与学生t检验相关的概率。使用TTEST来确定两个样本是否可能来自均值相同的相同两个基础总体。语法TTEST(array1,array2,tails,type)争论Argument描述Required/OptionalArray1Thefirstdataset.Required......
  • Java中使用ProcessBuilder执行命令
    在Java中我们可以使用ProcessBuilder来创建一个进程并执行命令。在使用前我们首先了了解下两个程序。一、认识命令行解释程序不论在windows中还是linux中都可以通过命令行方式来执行某些程序或脚本,而解析命令行的程序被称为shell,其本身是一种命令行解释器,用于与操作系统进行交互和......
  • Java对象从创建到回收全过程
    先了解下Java虚拟机内存组成,当Java虚拟机启动后,会将系统分配给JVM的空间逻辑上划分为堆、虚拟机栈、本地方法栈、方法区、程序计数器五个部分,如下图所示:堆:放置new出来的对象、数组虚拟机栈:线程运行前,会给其分配一个线程栈空间,线程中每个方法执行都会生成一个栈帧放入线程栈中,栈帧里......