BigDecimal详解

 


1、BigDecimal基本介绍

  • 高精度数值计算:BigDecimal是 Java 中用于高精度数值计算的类。它主要用于处理需要精确表示的十进制数,特别是在金融、货币计算等对精度要求极高的领域。双精度浮点型变量double 虽然可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理,BigDecimal 就可以用来对超过16位有效位的数进行精确的运算。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。
  • 不可变对象:BigDecimal对象是不可变的,这意味着一旦创建,其数值就不能被修改。对BigDecimal对象进行的任何操作(如加法、减法、乘法、除法等)都会返回一个新的BigDecimal对象,而原对象保持不变。这种不可变性有助于确保数据的一致性和线程安全性。

当程序中涉及到金额时,必须使用 BigDecimal 而不是 double 或 float 。

 

1.1、浮点类型坑示例

  1. public static void main(String[] args) {
  2. float a = 1;
  3. float b = 0.9f;
  4. System.out.println(a - b);
  5. }

 

2、BigDecimal 的常用方法

2.1、常用构造方法

  • new BigDecimal(int val):创建一个具有参数所指定整数值的对象。
  • new BigDecimal(double val):(不推荐使用,因为存在精度丢失问题)创建一个具有参数所指定双精度值的对象。
  • new BigDecimal(long val):创建一个具有参数所指定长整数值的对象
  • new BigDecimal(String val):(推荐使用)创建一个具有参数所指定以字符串表示的数值的对象。
复制代码
  1. public static void main(String[] args) {
  2. BigDecimal b_int = new BigDecimal(10);
  3. BigDecimal b_double = new BigDecimal(0.1);
  4. BigDecimal b_float = new BigDecimal(0.1f);
  5. BigDecimal b_string = new BigDecimal("0.1");
  6. BigDecimal b_valueof = BigDecimal.valueOf(0.1);
  7. System.out.println("b_int:" + b_int);
  8. System.out.println("b_double:" + b_double);
  9. System.out.println("b_float:" + b_float);
  10. System.out.println("b_string:" + b_string);
  11. System.out.println("b_valueof:" + b_valueof);
  12. }
复制代码

输出如下:

使用构造函数时,推荐用 int 或 String 做参数,如果不满足,则建议使用 BigDecimal.valueOf 方法,该方法可接受 int、long、double、float 做参数,但注意不接受 String。

 

2.2、常用方法

注意:BigDecimal进行运算时必须要保证对象本身不能是null,否则就会抛空指针异常。

 

1)加减乘除

  • add(BigDecimal):加,BigDecimal对象中的值相加,返回BigDecimal对象
  • subtract(BigDecimal):减,BigDecimal对象中的值相减,返回BigDecimal对象
  • multiply(BigDecimal):乘,BigDecimal对象中的值相乘,返回BigDecimal对象
  • divide(BigDecimal):除,BigDecimal对象中的值相除,返回BigDecimal对象。该方法可能会遇到无限精度问题,会抛出异常,使用时需注意。所以我们建议在使用BigDecimal进行除运算时,一定要指定精度和舍入模式。
复制代码
  1. public static void main(String[] args) {
  2. BigDecimal a = new BigDecimal("10");
  3. BigDecimal b = new BigDecimal("20");
  4. BigDecimal c = new BigDecimal("30");
  5. BigDecimal add = a.add(b);
  6. BigDecimal subtract = a.subtract(b);
  7. BigDecimal multiply = a.multiply(b);
  8. BigDecimal divide = a.divide(b);
  9. System.out.println("加结果为:" + add);
  10. System.out.println("减结果为:" + subtract);
  11. System.out.println("乘结果为:" + multiply);
  12. System.out.println("除结果为:" + divide);
  13. // 除不尽将会抛出异常
  14. BigDecimal divide2 = a.divide(c);
  15. }
复制代码

输入结果如下:

 

其他常见方法:

方法 含义
abs() 将BigDecimal对象中的值转换成绝对值
doubleValue() 将BigDecimal对象中的值转换成双精度数
floatValue() 将BigDecimal对象中的值转换成单精度数
longValue() 将BigDecimal对象中的值转换成整数
compareTo(BigDecimal val) 比较大小,返回int类型。0(相等) 1(大于) -1(小于)
toPlainString() 推荐使用,直接转换为字符串且不使用任何计数法
toString() 转换为字符串,但在必要时使用科学计数法。
toEngineeringString() 转换为字符串,但在必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法,与科学技术法类似,但要求10的幂必须是3的倍数
max(BigDecimal val) 两值比较,返回最大值
negate() 求相反数,正变负,负变正
pow(int n) 求乘方,如BigDecimal.valueOf(2).pow(3)的值为8

 

3、设置精度和舍入模式

BigDecimal 并不代表无限精度,当在两个数除不尽的时候,就会报错。所以我们建议在使用BigDecimal进行除运算时,一定要指定精度和舍入模式。

复制代码
  1. public static void main(String[] args) {
  2. BigDecimal b1 = new BigDecimal("1.0");
  3. BigDecimal b2 = new BigDecimal("3.0");
  4. // 保留3位小数,且四舍五入
  5. BigDecimal divide = b1.divide(b2, 3, RoundingMode.HALF_UP);
  6. System.out.println(divide);//0.33
  7. }
复制代码

 

3.1、BigDecimal 中的舍入模式

  1. ROUND_UP(向上舍入)
    • 定义与行为:
      • 这种舍入模式总是在非零舍弃部分的左边一位加 1。无论是正数还是负数,只要有需要舍弃的非零部分,就会向上进位。
    • 示例:
      • 对于正数,如将1.1舍入到整数,按照ROUND_UP模式,结果是2。因为小数部分0.1是非零的,所以将整数部分11得到2
      • 对于负数,如将-1.1舍入到整数,结果是-2。因为小数部分0.1是非零的,所以将整数部分-11得到-2
  2. ROUND_DOWN(向下舍入)
    • 定义与行为:
      • 总是舍弃非零舍弃部分,无论正数还是负数,都不进行进位操作。
    • 示例:
      • 对于正数,如将1.9舍入到整数,按照ROUND_DOWN模式,结果是1,因为直接舍弃小数部分0.9
      • 对于负数,如将-1.9舍入到整数,结果是-1。同样是直接舍弃小数部分0.9
  3. ROUND_CEILING(天花板舍入)
    • 定义与行为:
      • 如果是正数,行为类似于ROUND_UP;如果是负数,行为类似于ROUND_DOWN。其目的是将数字舍入到正无穷方向。
    • 示例:
      • 对于正数,如将1.1舍入到整数,结果是2,和ROUND_UP模式相同。
      • 对于负数,如将-1.1舍入到整数,结果是-1,和ROUND_DOWN模式相同。
  4. ROUND_FLOOR(地板舍入)
    • 定义与行为:
      • 如果是正数,行为类似于ROUND_DOWN;如果是负数,行为类似于ROUND_UP。其目的是将数字舍入到负无穷方向。
    • 示例:
      • 对于正数,如将1.9舍入到整数,结果是1,和ROUND_DOWN模式相同。
      • 对于负数,如将-1.9舍入到整数,结果是-2,和ROUND_UP模式相同。
  5. ROUND_HALF_UP(四舍五入)
    • 定义与行为:
      • 如果舍弃部分大于或等于0.5,则在非零舍弃部分的左边一位加1;否则,直接舍弃。这是最常见的舍入模式,符合我们日常的 “四舍五入” 概念。
    • 示例:
      • 1.5舍入到整数,结果是2,因为小数部分0.5满足大于或等于0.5的条件,所以将整数部分11
      • 1.4舍入到整数,结果是1,因为小数部分0.4小于0.5,所以直接舍弃。
  6. ROUND_HALF_DOWN(五舍六入)
    • 定义与行为:
      • 如果舍弃部分大于0.5,则在非零舍弃部分的左边一位加1;否则,直接舍弃。与ROUND_HALF_UP的区别在于,当舍弃部分等于0.5时,不进位。
    • 示例:
      • 1.5舍入到整数,结果是1,因为小数部分等于0.5,不进位。
      • 1.6舍入到整数,结果是2,因为小数部分0.6大于0.5,所以将整数部分11
  7. ROUND_HALF_EVEN(银行家舍入)
    • 定义与行为:
      • 也称为 “银行家舍入”。如果舍弃部分左边的数字是偶数,且舍弃部分等于0.5,则直接舍弃;如果舍弃部分左边的数字是奇数,且舍弃部分等于0.5,则在非零舍弃部分的左边一位加1。对于其他情况,和ROUND_HALF_UP类似。
    • 示例:
      • 2.5舍入到整数,结果是2,因为整数部分2是偶数,且小数部分是0.5,所以直接舍弃。
      • 3.5舍入到整数,结果是4,因为整数部分3是奇数,且小数部分是0.5,所以将整数部分31

 

 

9、最佳实践

复制代码
  1. public static void main(String[] args) {
  2. // 使用字符串参数构造函数
  3. BigDecimal a = new BigDecimal("10");
  4. // 或使用BigDecimal.valueOf方法
  5. BigDecimal b = BigDecimal.valueOf(30);
  6. BigDecimal add = a.add(b);
  7. BigDecimal subtract = a.subtract(b);
  8. BigDecimal multiply = a.multiply(b);
  9. //进行除运算时,指定精度和舍入模式,避免除不尽导致报错
  10. BigDecimal divide = a.divide(b, 2, RoundingMode.HALF_UP);
  11. System.out.println("加结果为:" + add);
  12. System.out.println("减结果为:" + subtract);
  13. System.out.println("乘结果为:" + multiply);
  14. System.out.println("除结果为:" + divide);
  15. }
复制代码

输出如下:

 

posted @   wenxuehai  阅读(123)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
历史上的今天:
2023-01-09 ElasticSearch的基本使用
2020-01-09 Java的基本使用之核心类
//右下角添加目录
点击右上角即可分享
微信分享提示