前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BigDecimal

BigDecimal

原创
作者头像
疯狂的KK
修改2023-07-05 15:15:15
3300
修改2023-07-05 15:15:15
举报
文章被收录于专栏:Java项目实战

推荐阅读

【玩转 GPU】AI绘画、AI文本、AI翻译、GPU点亮AI想象空间-腾讯云开发者社区-腾讯云 (tencent.com)

腾讯云玩转Stable Diffusion 模型-腾讯云开发者社区-腾讯云 (tencent.com)

整数及小数的计算在程序中是非常常见的,而Java提供了两种处理浮点数值的数据类型:doublefloat。然而,由于在计算时,%运算对于double和float类型是没用的,因此在处理高精度计算时,使用 BigDecimal 类型会更为可靠。在本文中,我们将介绍BigDecimal类型,讨论它的使用、需要注意的地方和常用方法,最后我们将得出除非您需要执行四舍五入,否则请不要使用 double 类型作为高精度计算的基本数据类型,而应该使用BigDecimal的结论。

什么是BigDecimal?

BigDecimal是Java开发包中的一个类,可以处理高精度数,它提供了大量的方法来处理浮点数据,可以对浮点数进行各种基本的数学运算(+-/*)以及其他计算(如对数、平方根和指数函数)。另一个重要的功能就是它支持精确定义小数点的位置和标度(即小数位数)。在BigDecimal中定义了两个整数:精度和标度。精度表示数字中的位数,标度表示小数点右边的位数。例如,在数字345.67中,精度是5,而标度是2。

当分子和分母都是整数时,正常情况下的除法不一定会得到一个整数,会得到一个类似于“圆整”的值。使用BigDecimal可以避免这种情况。

BigDecimal非常适用于需要高精度计算的场合,如货币计算、科学计算、精确计算等,它可以处理非常大的数据,不会出现精度丢失或舍入问题。由于它的高精度计算特性,它也非常适用于数据结构、数字对准、公差准确度、维度计算等领域。

为什么不使用double 类型进行高精度计算?

Java内置了doublefloat两种浮点数类型,它们在对于小数的计算上都有很好的支持。但是,在进行高精度计算时,我们很快就会发现double数据类型存在精度问题,这是由于二进制无法精确表示所有的十进制数,例如 0.1 这个小数在二进制表示中是一个无限循环的小数。如下是一个简单的例子:

代码语言:java
复制
double a = 0.1;
double b = 0.2;
double c = a + b;
System.out.println(c); 
// 预期输出值应该是0.3,但实际上会输出0.30000000000000004

结果非常接近0.3,但它并不完全等于 0.3,这是由于16进制的浮点数不能够精确表示0.1,因此计算时会出现计算误差。这个问题可能会导致在金额计算等场景中出现错误,严重的话可能会影响到业务逻辑的正确性。

另外,floatdouble数值类型中的某些特殊值(如无法计算结果、除以0等)可能会导致抛出运行时异常。

floatdouble不同,BigDecimal在内部使用整数实现非常高的精度,并提供了与Java中的其他基本类型相同的算术操作。因此,它可以处理更大的数字和更高的精度,实现更可靠的高精度计算。

BigDecimal使用时需要注意的地方

在使用BigDecimal时,有几个需要注意的地方。

构造方法

BigDecimal有很多不同的构造方法,它们可以用于不同类型的数字的初始化。以下是几个常用的构造方法:

  1. BigDecimal(double val) - 将指定的double值转换为BigDecimal,并将其初始化为其精确十进制表达式。
  2. BigDecimal(String val) - 将指定的字符串转换为BigDecimal。
  3. BigDecimal(BigInteger val) - 将指定的BigInteger转换为BigDecimal。

在这些构造函数中,值得注意的是用浮点数作为初始化值时,通过使用该浮点数的精确表示来初始化BigDecimal对象。因此,当使用一些特定的浮点数时,可能会引起不可预料的行为和性能问题。我们建议尽可能使用字符串来初始化BigDecimal对象,以避免这种情况发生。

舍入模式

在高精度计算中,舍入可能是必要的。BigDecimal 提供了 RoundingMode 枚举,可以通过该枚举设置舍入模式。在使用BigDecimal进行除法或设置精度时,指定正确的舍入模式非常重要。

以下是一些可用的舍入模式:

  1. RoundingMode.UP - 向远离零的方向舍入,即向正无穷大方向舍入
  2. RoundingMode.DOWN - 向靠近零的方向舍入,即向负无穷方向舍入
  3. RoundingMode.CEILING - 如果数字大于零,则向正无穷方向舍入;如果数字小于零,则向零方向舍入
  4. RoundingMode.FLOOR - 如果数字大于零,则向零方向舍入;如果数字小于零,则向负无穷方向舍入
  5. RoundingMode.HALF_UP - 向最接近的数字舍入,如果与两个相邻数字的距离相等,则向最近的偶数舍入
  6. RoundingMode.HALF_DOWN - 向最接近的数字舍入,如果与两个相邻数字的距离相等,则向远离零的方向舍入
  7. RoundingMode.HALF_EVEN - 向最接近的数字舍入,如果与两个相邻数字的距离相等,则向最近的偶数舍入,类似于四舍五入

例如,当我们使用BigDecimal进行除法计算时,应指定一个舍入模式,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal(10);
BigDecimal b = new BigDecimal(3);
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP);
System.out.println(result); // 输出3.33

在上面的代码中,我们使用RoundingMode.HALF_UP模式对结果进行了四舍五入处理,保留了两位小数。

空指针异常

当使用BigDecimal时,我们需要经常检查对象是否为null,这是因为当BigDecimal对象为null时,任何操作都将导致空指针异常。另外,应该使用BigDecimal.ZERO代替null值。当我们将null值赋给一个BigDecimal值时,它会抛出一个NullPointerException。因此,最好在算术运算之前检查引用。例如:

代码语言:java
复制
BigDecimal a = null;
if (a == null) {
    a = BigDecimal.ZERO; // 使用 BigDecimal.ZERO 代替 null
}
BigDecimal b = new BigDecimal(10);
BigDecimal result = a.add(b);

在上面的代码中,我们检查a是否为空,如果是,我们将其设置为BigDecimal.ZERO。这样可以避免出现空指针异常。

不可变性

BigDecimal是不可变类,这意味着一旦创建了一个BigDecimal对象,它就不能被更改,如不能进行setter操作。如果想修改其值,则必须使用新的BigDecimal对象。这种不可变性确保了在进行多线程编程时线程安全文法,同时也使得BigDecimal类型非常适用于缓存处理方案。

BigDecimal 常用方法

在上述内容的基础上,下面我们将介绍BigDecimal类的一些常用方法。

add()

add() 方法可以用于对两个BigDecimal值进行加法运算,返回一个新的BigDecimal值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("20");
BigDecimal result = a.add(b);

在上面的代码中,我们使用add()方法计算了ab的和,结果保存在result变量中。如果需要进行多个数值的加法运算,可以按照类似的方式进行计算。

subtract()

subtract() 方法可以用于对两个BigDecimal值进行减法运算,返回一个新的BigDecimal值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("5");
BigDecimal result = a.subtract(b);

在上面的代码中,我们使用subtract()方法计算了ab的差,结果保存在result变量中。

multiply()

multiply() 方法可以用于对两个BigDecimal值进行乘法运算,返回一个新的BigDecimal值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("5");
BigDecimal result = a.multiply(b);

在上面的代码中,我们使用multiply()方法计算了ab的积,结果保存在result变量中。

divide()

divide() 方法可以用于对两个BigDecimal值进行除法运算,返回一个新的BigDecimal值,并可以设置精度和舍入模式。例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP);

在上面的代码中,我们使用divide()方法将a除以b,同时将结果四舍五入并保留两位小数,结果保存在result变量中。

compareTo()

compareTo() 方法可以用于比较两个BigDecimal值的大小关系,如果第一个BigDecimal值大于第二个,则返回一个正数,如果第一个值小于第二个,则返回一个负数,如果两个值相等,则返回0。例如。

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("5");
int result = a.compareTo(b);

在上面的代码中,我们使用compareTo()方法比较了ab的大小关系,结果保存在result变量中。

equals()

equals()方法可以用于比较两个BigDecimal值的相等性。

注意:在进行比较时,需要使用compareTo()方法,不能使用等于运算符(==),因为等于运算符比较的是对象的引用,而不是它们的值。例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("10");

// 通过compareTo()进行比较
if(a.compareTo(b) == 0) {
    System.out.println("a equals b");
}

// 通过equals()方法进行比较
if (a.equals(b)) {
    System.out.println("a equals b");
}

在上面的代码中,我们使用compareTo()方法和equals()方法比较了ab的相等性。请注意,两个BigDecimal对象的相等性和它们的值以及小数点后面的精度有关。

abs()

abs()方法可以用于获取一个BigDecimal值的绝对值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("-10");
BigDecimal result = a.abs();

在上面的代码中,我们使用abs()方法获取a的绝对值,结果保存在result变量中。

intValue()

intValue()方法可以将一个BigDecimal值转换为一个int值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
int result = a.intValue();

在上面的代码中,我们使用intValue()方法将a转换为int类型,并将结果保存在result变量中。

longValue()

longValue()方法可以将一个BigDecimal值转换为一个long值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
long result = a.longValue();

在上面的代码中,我们使用longValue()方法将a转换为long类型,并将结果保存在result变量中。

floatValue()

floatValue()方法可以将一个BigDecimal值转换为一个float值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
float result = a.floatValue();

在上面的代码中,我们使用floatValue()方法将a转换为float类型,并将结果保存在result变量中。

doubleValue()

doubleValue()方法可以将一个BigDecimal值转换为一个double值,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10");
double result = a.doubleValue();

在上面的代码中,我们使用doubleValue()方法将a转换为double类型,并将结果保存在result变量中。

scale()

scale()方法可以获取BigDecimal值的标度(小数点后的位数),例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10.00");
int result = a.scale();

在上面的代码中,我们使用scale()方法获取a的标度,结果保存在result变量中。

setScale()

setScale()方法可以设置BigDecimal值的标度(小数点后的位数),并指定舍入模式,例如:

代码语言:java
复制
BigDecimal a = new BigDecimal("10.1234");
BigDecimal result = a.setScale(2, RoundingMode.HALF_UP);

在上面的代码中,我们使用setScale()方法将a的小数点后的位数设置为2,并指定了舍入模式,结果保存在result变量中。

总结

通过本文的介绍,我们了解了BigDecimal类型,掌握了它的基本用法、需要注意的地方和常用方法。与doublefloat浮点数类型相比,它在进行高精度计算时具有更高的精度和更可靠的精度控制。同时,由于它的不可变性和线程安全性,它也很适用于缓存处理方案。

在进行高精度计算时,我们强烈建议使用BigDecimal类型,并尽可能地避免使用doublefloat类型。由于BigDecimal类型的高精度特性,它可以避免在计算过程中出现精度丢失或舍入问题,从而确保业务逻辑的正确性。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是BigDecimal?
  • 为什么不使用double 类型进行高精度计算?
  • BigDecimal使用时需要注意的地方
    • 构造方法
      • 舍入模式
        • 空指针异常
          • 不可变性
          • BigDecimal 常用方法
            • add()
              • subtract()
                • multiply()
                  • divide()
                    • compareTo()
                      • equals()
                        • abs()
                          • intValue()
                            • longValue()
                              • floatValue()
                                • doubleValue()
                                  • scale()
                                    • setScale()
                                    • 总结
                                    相关产品与服务
                                    云服务器
                                    云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档