为什么用BigDecimal?
在编程的世界里,BigDecimal
是一位精确而谨慎的数学家,它的存在解决了一个棘手的问题:浮点数(如 float
和 double
)在某些情况下的不精确表达。
尤其是在财务计算中,精确度是至关重要的。作为一名程序员,如果负责计算巨额交易的利息。即使是极小的误差,也可能导致巨大的经济损失。在这种情况下,如果使用 float
或 double
来表示货币,那很有可能你会因为精度丢失的问题导致你的年终奖被扣走
这时就需要使用BigDecimal
这个能够精确地测量和表示的API去处理这些数字。可以精确地控制数值的小数点后位数,避免了浮点数的舍入误差。
注意:正因为它的精度高,所以计算的时候,会比正常的计算慢数十倍。所以根据自己业务需要去做选择
基本使用
那么对上面我们的业务做好权衡之后,我们就要去使用它了。
1. 构造
简单来说BigDecimal
就是java中的一个类,所以我们在使用的时候就将它按照一个对象去处理
- 通过字符串构造 :
new BigDecimal(String val)
。使用字符串构造BigDecimal
是推荐的方法,因为它可以避免由于浮点数不精确表示而导致的意外结果。 - 通过双精度类型构造 :
new BigDecimal(double val)
。这种方式虽然也可以使用,但不推荐,因为它可能会引起浮点数本身的精度问题。
我们在创建了一个对象之后它就是不可变的了,如果我们想要在一个循环里面去相加就需要去创建一个0
这里推荐一个创建0的方法,这种方式也是性能最高的
BigDecimal zero = BigDecimal.ZERO;
剩下的我们只能通过的基本运算去做一下相关的修改
2. 基本运算
- 加法 :
add(BigDecimal augend)
- 减法 :
subtract(BigDecimal subtrahend)
- 乘法 :
multiply(BigDecimal multiplicand)
- 除法 :
divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
需要特别注意 divide
方法,因为在做除法的时候可能会产生无限循环小数,需要指定小数点后的位数(scale
)和舍入模式。
3. 精确度和舍入模式
因为除法的特殊性,所以我在这里单独拉出来讲一讲
BigDecimal
有两种模式即标题的两种模式,对应divide
后面的两个参数
- 精确度(Scale) 精确度是指小数点后保留的位数。在进行除法时,如果结果是一个无限循环小数,没有适当的精确度设置,将会抛出
ArithmeticException
异常。 - 舍入模式(RoundingMode) 它其实就是 Java 中的
RoundingMode
是一个枚举类型,它定义了几种不同的舍入行为。以下是一些常用的舍入模式: RoundingMode.UP
:远离零方向舍入。RoundingMode.DOWN
:靠近零方向舍入。RoundingMode.CEILING
:向正无穷方向舍入。RoundingMode.FLOOR
:向负无穷方向舍入。RoundingMode.HALF_UP
:四舍五入(若舍弃部分≥0.5,则舍入行为与RoundingMode.UP
相同)
一般常用的就是上面的这几种
示例代码:
java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalDemo {
public static void main(String[] args) {
BigDecimal dividend = new BigDecimal("10");
BigDecimal divisor = new BigDecimal("3");
// 使用不同的舍入模式
BigDecimal resultHalfUp = dividend.divide(divisor, 2, RoundingMode.HALF_UP);
BigDecimal resultDown = dividend.divide(divisor, 2, RoundingMode.DOWN);
System.out.println("Result with HALF_UP: " + resultHalfUp); // 输出 "Result with HALF_UP: 3.33"
System.out.println("Result with DOWN: " + resultDown); // 输出 "Result with DOWN: 3.33"
}
}
4. 如何比较
我们在对BigDecimal
类的比较操作需要特别的注意,因为它与基本数据类型或其他对象类型的比较有所不同。主要原因是 BigDecimal
包含了数值和精确度(Scale)两个部分,这两部分都会影响比较的结果。
为什么会出现这样的情况?
主要是因为它们的精确度不同,直接使用 equals
方法比较时会返回 false
。这是因为 equals
方法考虑了对象的数值和精确度。
所以我们就需要使用 compareTo
进行数值比较 示例:
java
BigDecimal a = new BigDecimal("2.00");
BigDecimal b = new BigDecimal("2.0");
boolean isEqual = a.equals(b); // 返回 false,因为精确度不同
int comparison = a.compareTo(b); // 返回 0,数值相等
实战使用
- 那么我在开发的过程中就遇到了在对接银联支付的时候,银联需要将金钱转化为Integer类型给传到json里面在发送过去,那么下面是我的代码处理。
java
int amount = order.getPayPrice().multiply(BigDecimal.TEN).multiply(BigDecimal.TEN).intValue();
- 在计算税率的时候的一个实战代码
java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class FinancialCalculationExample {
public static void main(String[] args) {
BigDecimal principal = new BigDecimal("1000.00"); // 本金
BigDecimal interestRate = new BigDecimal("0.05"); // 税率
// 计算税额: 税额 = 本金 * 税率
BigDecimal interest = principal.multiply(interestRate);
// 计算价税合计: 总额 = 本金 + 税额
BigDecimal totalAmount = principal.add(interest).setScale(2, RoundingMode.HALF_UP);
System.out.println("Total amount after one year: " + totalAmount); // 输出价税合计
}
}
总结
最后非常感谢您阅读 BigDecimal
的基操。我希望上述的信息和代码示例能够帮助您更好地理解和使用 BigDecimal
,特别是在需要高精度计算的场景中。无论您是在处理复杂的金融计算、需要处理精确的用户输入,还是简单地希望避免浮点数带来的精度问题,BigDecimal
都是一个非常有力的工具。
如果您在日常开发中遇到任何关于 Java 或 BigDecimal
的问题,欢迎随时向我咨询。再次感谢您的阅读,祝您编程愉快,项目顺利!