java特殊类【BigDecimal】

本文详细介绍了Java中的BigDecimal类,包括其用途、为何使用、精度控制、示例代码以及创建、运算、比较、格式化和转换等相关知识,重点强调了高精度计算和避免浮点数精度问题的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、是什么

BigDecimal是java提供的一个不变的、任意精度的有符号十进制数对象。它可以用来处理更大或更小的数,进行精确的数值运算。

1.1. 作用

  • 高精度的数学运算:由于BigDecimal支持任意精度的加、减、乘、除等数学运算,因此可以避免使用doouble或float类型是出现的精度丢失问题。
  • 金融计算:金融计算对精度要求很高,使用BigDecimal可以保证计算结果的准确性。
  • 数字格式化:BigDecimal类可以将数字格式化成各种格式,如货币格式、百分比格式等。
  • 数据库操作:在处理数据库的数值类型时,使用BigDecimal可以避免精度丢失。

1.2. 为什么要用

  • 精度要求:BigDecimal提供了更高的精度计算,可以避免使用float或double类型时可能出现的精度丢失或计算错误的问题。这对于金融计算、货币计算等需要精确结果的场景尤其重要。
  • 性能考虑:虽然BigDecimal在某些情况下会比使用float或double类型消耗更多的内存和CPU资源,但是在需要高精度计算的场景中,其性能优势是无可比拟的。由于其内部采用字符串存储和运算,因此可以避免浮点数运算中的舍入误差和精度限制。
  • 稳定性:BigDecimal的内部实现是经过精心设计和优化的,可以确保在各种场景下的稳定性和可靠性。此外,由于其采用字符串存储方式,可以避免由于数据溢出、舍入误差等问题导致的程序崩溃或数据丢失等情况。
  • 兼容性:BigDecimal与其他Java库的兼容性较好,可以方便地进行数值计算和数据处理。同时,由于其内部采用BigInteger进行运算,可以方便地进行大数计算和处理。

二、精度丢失示例代码

示例代码:

public class test {
    public static void main(String[] args) {
        System.out.println(0.2 + 0.1);

        System.out.println(0.3 - 0.1);

        System.out.println(0.2 * 0.1);

        System.out.println(0.3 / 0.1);
    }
}

输出结果:
图片.png
结论:以上得到的结果与预期的不一致。在计算机科学中,浮点数(double/float)不能精确的表示所有小数,是因为浮点数是以二进制的形式表示的,而一些十进制小数不能用有限的二进制小数精确表示。
例如,0.1(十进制)不能被精确地表示为二进制小数。当你进行浮点数运算时,如加、减、乘、除等,由于这种精度问题,可能会导致结果与预期不符。

三、BigDecimal构造方法

  • BIgDecimal(double val):将double表示形式转换为BigDecimal,*不建议使用
  • BIgDecimal(int val):将int表示形式转换为BigDecimal
  • BIgDecimal(String val):将String表示形式转换为BigDecimal

示例代码:

public class test {
    public static void main(String[] args) {
        BigDecimal bInt = new BigDecimal(2); //参数int

        BigDecimal bDouble = new BigDecimal(2.3); //参数double

        BigDecimal bString = new BigDecimal("2.3"); //参数String

        System.out.println("bInt=" + bInt);

        System.out.println("bDouble=" + bDouble);

        System.out.println("bString=" + bString);
    }
}

输出结果:
图片.png
以上示例代码,使用double作为参数创建BigDecimal对象时,遇到了精度丢失的问题。当使用double值作为构造参数时,由于二进制无法精确表示某些十进制小数(例如,二进制中的0.1等于十进制中的0.4,而0.2等于十进制的0.5,因此二进制无法精确表示0.1+0.2=0.3),这可能导致一些精度丢失。
可以尝试使用BigDecimal.valueOf(2.3)方法创建BigDecimal对象,该方法可以避免精度丢失:

public class test {
    public static void main(String[] args) {
        BigDecimal bDouble = BigDecimal.valueOf(2.3);

        System.out.println(bDouble);
    }
}

输出结果:
图片.png

四、加减乘除运算

4.1. 示例代码(加减乘除)

public class test {
    public static void main(String[] args) {
        // 创建两个BigDecimal对象
        BigDecimal num1 = new BigDecimal("10.5");
        BigDecimal num2 = new BigDecimal("1.3");
        // 加法
        BigDecimal sum = num1.add(num2);
        System.out.println("加法结果: " + sum);
        // 减法
        BigDecimal difference = num1.subtract(num2);
        System.out.println("减法结果: " + difference);
        // 乘法
        BigDecimal product = num1.multiply(num2);
        System.out.println("乘法结果: " + product);
        // 除法
        BigDecimal quotient = num1.divide(num2, 2, RoundingMode.HALF_UP); // 设置精度为2位小数,使用四舍五入
        System.out.println("除法结果: " + quotient);
    }
}

输出结果:
图片.png
注意:BigDecimal除法(divide)可能出现不能整除的情况,比如10.5/1.3,这时会报以下错误:
图片.png但是divide方法有三个参数可以解决:

  • BIgDecimal divisor:第一个参数表示除数;
  • int scale:第二个参数表示小数点后保留位数;
  • int roundingMode:第三个参数表示舍入模式。

只有在作除法运算或四舍五入时才用到舍入模式,有以下几种:

  1. RoundingMode.DOWN:向下舍入模式。无论小数部分是否为零,都直接舍去。例如,10.5 / 3.2的结果为3.309375,但使用向下舍入模式后结果为3。
  2. RoundingMode.UP:向上舍入模式。无论小数部分是否为零,都直接进位。例如,10.5 / 3.2的结果为3.309375,但使用向上舍入模式后结果为4。
  3. RoundingMode.HALF_UP:四舍五入模式。当小数部分大于等于0.5时进位,小于0.5时舍去。例如,10.5 / 3.2的结果为3.309375,但使用四舍五入模式后结果为3.31。
  4. RoundingMode.HALF_DOWN:四舍五入模式(向下)。当小数部分大于等于0.5时舍去,小于0.5时进位。例如,10.5 / 3.2的结果为3.309375,但使用四舍五入模式(向下)后结果为3.30。
  5. RoundingMode.HALF_EVEN:四舍六入五成双模式。当小数部分大于等于0.5且小于1时进位,其他情况下舍去。例如,10.5 / 3.2的结果为3.309375,但使用四舍六入五成双模式后结果为3.31。也称为“银行家舍入法”,优点是在重复进行一系列计算时,可以将累加错误减到最小。

。。。

4.2. 四舍五入/截断

示例代码:

public class test {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("4.5635");
        a = a.setScale(3, RoundingMode.HALF_UP); //保留3位小数,且四舍五入
        System.out.println(a);
    }
}

输出结果:
图片.png
处理BigDecimal使用setScale方法操作。

五、比较大小

5.1. compareTo

public class test {
    public static void main(String[] args) {
        BigDecimal b1 = new BigDecimal(61.40);
        System.out.println("b1小数位数" + b1.scale());
        BigDecimal b2 = new BigDecimal(61.4);
        System.out.println("b2小数位数" + b2.scale());

        BigDecimal b3 = new BigDecimal("61.40");
        System.out.println("b3小数位数" + b3.scale());
        BigDecimal b4 = new BigDecimal("61.4");
        System.out.println("b4小数位数" + b4.scale());

        System.out.println("===============compareTo==================");
        System.out.println("61.40 数值和61.4数值比较 " + b1.compareTo(b2));
        System.out.println("61.40 数值和61.40字符比较 " + b1.compareTo(b3));
        System.out.println("61.40 字符和61.4字符比较 " + b3.compareTo(b4));
    }
}

�输出结果:
图片.png

5.2. equals

示例代码:

public class test {
    public static void main(String[] args) {
        BigDecimal b1 = new BigDecimal(61.40);
        System.out.println("b1小数位数" + b1.scale());
        BigDecimal b2 = new BigDecimal(61.4);
        System.out.println("b2小数位数" + b2.scale());

        BigDecimal b3 = new BigDecimal("61.40");
        System.out.println("b3小数位数" + b3.scale());
        BigDecimal b4 = new BigDecimal("61.4");
        System.out.println("b4小数位数" + b4.scale());

        System.out.println("===============equals==================");
        System.out.println("61.40 数值和61.4数值比较 " + b1.equals(b2));
        System.out.println("61.40 数值和61.40字符比较 " + b1.equals(b3));
        System.out.println("61.40 字符和61.4字符比较 " + b3.equals(b4));
    }
}

输出结果:
图片.png

5.3. compareTo和equals区别

equals方法会比较两部分内容,分别是值(value)和精度(scale,即小数位数);
compareTo方法比较会忽略精度;
equals代码中比较逻辑(可以看到不仅比较了值还比较了精度(及小数位数))
图片.png
compareTo代码中比较逻辑(精度相同和不同都做了比较):
图片.png

六、BigDecimal转String

示例代码:

public class test {
    public static void main(String[] args) {
        // 浮点数的打印
        System.out.println(new BigDecimal("10000000000").toString());
        // 普通的数字字符串
        System.out.println(new BigDecimal("100.000").toString());
        // 去除末尾多余的0
        System.out.println(new BigDecimal("100.000").stripTrailingZeros().toString());
        // 避免输出科学计数法
        System.out.println(new BigDecimal("100.000").stripTrailingZeros().toPlainString());
    }
}

输出结果:
图片.png
以上结果原因:
stripTrailingZeros()方法是去除末尾的零;toPlainString()方法将结果转换为普通的字符串表示,而不是科学计数法。,因此使用toString()会得到科学记数(1E+2)。

七、总结

Java中的BigDecimal类是用于高精度计算的类,它可以表示非常大或非常小的浮点数,并且可以避免由于浮点数精度限制而导致的问题。

7.1. 创建BigDecimal对象

  • 使用字符串创建:BigDecimal bd = new BigDecimal(“123.456”);
  • 使用双精度浮点数创建:BigDecimal bd = new BigDecimal(123.456); *不推荐使用
  • 使用整数创建:BigDecimal bd = new BigDecimal(123);

7.2. 四舍五入

  • 使用setScale()方法进行四舍五入:bd = bd.setScale(2, RoundingMode.HALF_UP);

7.3. 舍去末尾零

  • 使用stripTrailingZeros()方法:bd = bd.stripTrailingZeros();

7.4. 比较

  • 使用compareTo()方法进行比较:int result = bd1.compareTo(bd2);
  • 使用equals()方法进行相等性比较:boolean isEqual = bd1.equals(bd2);

7.5. 算数运算

  • 加法:bd = bd1.add(bd2);
  • 减法:bd = bd1.subtract(bd2);
  • 乘法:bd = bd1.multiply(bd2);
  • 除法:bd = bd1.divide(bd2, BigDecimal.ROUND_HALF_UP);

7.6. 数学函数

  • 平方根:bd = bd.sqrt();
  • 指数:bd = bd.pow(2);
  • 对数:bd = bd.log();

7.7. 格式化输出

  • 使用toPlainString()方法返回普通字符串表示形式。
  • 使用toString()方法返回字符串表示形式。
  • 使用toEngineeringString()方法返回工程字符串表示形式。

7.8. 转换为其它数值类型

  • 转换为长整型:long l = bd.longValue();
  • 转换为双精度浮点数:double d = bd.doubleValue();

7.9. 注意事项

  • 在进行除法运算时,如果除数为0,会抛出ArithmeticException异常。
  • 在进行除法运算时,如果结果为无限大或无限小,也会抛出异常。
  • 使用BigDecimal进行高精度计算时,应该避免直接使用浮点数或双精度浮点数进行计算,因为它们可能存在精度问题。
### JavaBigDecimal 的取整方法 在 Java 中,`BigDecimal` 提供了多种方式来执行取整操作。这些取整模式通过 `setScale()` 方法指定舍入行为,并结合不同的舍入常量完成具体的取整逻辑[^1]。 以下是常见的几种取整模式及其对应的代码示例: #### 向上取整 (Ceiling) 向上取整表示向正无穷方向取最接近的整数值。可以通过设置 `RoundingMode.CEILING` 来实现此功能。 ```java import java.math.BigDecimal; import java.math.RoundingMode; public class Main { public static void main(String[] args) { BigDecimal number = new BigDecimal("10.5"); BigDecimal ceilingResult = number.setScale(0, RoundingMode.CEILING); System.out.println("向上取整:" + ceilingResult); // 输出:11 } } ``` #### 向下取整 (Floor) 向下取整表示向负无穷方向取最接近的整数值。可以使用 `RoundingMode.FLOOR` 实现该功能。 ```java import java.math.BigDecimal; import java.math.RoundingMode; public class Main { public static void main(String[] args) { BigDecimal number = new BigDecimal("-10.5"); BigDecimal floorResult = number.setScale(0, RoundingMode.FLOOR); System.out.println("向下取整:" + floorResult); // 输出:-11 } } ``` #### 四舍五入 (Half Up) 四舍五入是最常用的取整方式之一,它会根据小数部分决定是否进位。当小数部分大于等于 0.5 时,向上取整;否则向下取整。这可以通过 `RoundingMode.HALF_UP` 完成。 ```java import java.math.BigDecimal; import java.math.RoundingMode; public class Main { public static void main(String[] args) { BigDecimal number = new BigDecimal("10.5"); BigDecimal halfUpResult = number.setScale(0, RoundingMode.HALF_UP); System.out.println("四舍五入:" + halfUpResult); // 输出:11 } } ``` #### 截断 (Down/Truncate) 截断意味着直接去掉小数部分而不考虑其大小。这种情况下可使用 `RoundingMode.DOWN` 或者不改变精度的方式处理。 ```java import java.math.BigDecimal; import java.math.RoundingMode; public class Main { public static void main(String[] args) { BigDecimal number = new BigDecimal("10.999"); BigDecimal truncateResult = number.setScale(0, RoundingMode.DOWN); System.out.println("截断:" + truncateResult); // 输出:10 } } ``` 需要注意的是,在某些特殊场景下可能会遇到异常情况,比如无限循环的小数扩展无法精确表达为十进制结果时抛出 ArithmeticException 异常[^2]。因此建议开发者在实际应用过程中充分测试各种边界条件以确保程序健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值