目录
1.概述
2.基本API
2.1.创建 BigDecimal 对象:
2.3.基本运算方法:
2.4.精度控制方法:
2.5.比较
2.6.转换
3.注意事项
4.底层实现原理
1.概述
精度丢失,由于现代计算机中采用了浮点数来表示小数,这种表示法会存在精度丢失的问题。想要了解精度丢失的原因,可以去看博主另一篇文章,里面详细解释了其中的原因:
详解浮点数__BugMan的博客-CSDN博客
下面举一个精度丢失的例子:
// 商品价格
double price1 = 19.99;
double price2 = 9.99;
double price3 = 4.99;
// 商品数量
int quantity1 = 2;
int quantity2 = 3;
int quantity3 = 1;
// 计算每种商品的小计
double subtotal1 = price1 * quantity1;
double subtotal2 = price2 * quantity2;
double subtotal3 = price3 * quantity3;
// 计算总价
double total = subtotal1 + subtotal2 + subtotal3;
// 输出结果
System.out.println("商品1小计:" + subtotal1);
System.out.println("商品2小计:" + subtotal2);
System.out.println("商品3小计:" + subtotal3);
System.out.println("购物车总价:" + total);
摸出手机算一下就知道,本来总价应该等于74.94,但是实际的运行结果等于:
这种精度丢失在诸如金融、航天等需要高精度运算的场景下是无法接受的,为了保证精度,JDK中推出了BigDecimal类型,用来进行浮点数的精确计算。将上面的例子改为BigDecimal后:
// 商品价格
BigDecimal price1 = new BigDecimal("19.99");
BigDecimal price2 = new BigDecimal("9.99");
BigDecimal price3 = new BigDecimal("4.99");
// 商品数量
int quantity1 = 2;
int quantity2 = 3;
int quantity3 = 1;
// 计算每种商品的小计
BigDecimal subtotal1 = price1.multiply(new BigDecimal(quantity1));
BigDecimal subtotal2 = price2.multiply(new BigDecimal(quantity2));
BigDecimal subtotal3 = price3.multiply(new BigDecimal(quantity3));
// 计算总价
BigDecimal total = subtotal1.add(subtotal2).add(subtotal3);
// 输出结果
System.out.println("商品1小计:" + subtotal1);
System.out.println("商品2小计:" + subtotal2);
System.out.println("商品3小计:" + subtotal3);
System.out.println("购物车总价:" + total);
结果:
2.基本API
2.1.创建 BigDecimal 对象:
-
BigDecimal(String val):使用字符串表示创建一个 BigDecimal对象。
-
BigDecimal(double val):使用双精度浮点数创建一个 BigDecimal对象。
-
BigDecimal(BigInteger val):使用 BigInteger对象创建一个BigDecimal 对象。
BigDecimal bigDecimal1=new BigDecimal("0.8");
BigDecimal bigDecimal2=new BigDecimal(0.8);
BigDecimal bigDecimal3=new BigDecimal(8);
2.3.基本运算方法:
-
add(BigDecimal augend):加法操作,将当前 BigDecimal 对象与参数相加。
-
subtract(BigDecimal subtrahend):减法操作,将当前 BigDecimal 对象减去参数。
-
multiply(BigDecimal multiplicand):乘法操作,将当前 BigDecimal 对象与参数相乘。
-
divide(BigDecimal divisor):除法操作,将当前 BigDecimal 对象除以参数。
-
pow(int exponent):指数操作,将当前 BigDecimal 对象的幂次方计算。
BigDecimal bigDecimal1=new BigDecimal("0.8");
BigDecimal bigDecimal2=new BigDecimal("0.2");
System.out.println(bigDecimal1.add(bigDecimal2));
System.out.println(bigDecimal1.multiply(bigDecimal2));
System.out.println(bigDecimal1.divide(bigDecimal2));
System.out.println(bigDecimal1.pow(2));
2.4.精度控制方法:
-
setScale(int newScale):设置 BigDecimal对象的精度(小数点后的位数)。
-
setScale(int newScale, RoundingMode roundingMode):设置 BigDecimal 对象的精度,并指定舍入模式。
-
round(MathContext mc):使用指定的舍入规则舍入当前 BigDecimal对象。
BigDecimal number = new BigDecimal("123.456789");
BigDecimal roundedNumber = number.setScale(2, BigDecimal.ROUND_HALF_UP);
2.5.比较
-
compareTo(BigDecimal val):比较当前 BigDecimal对象与参数的大小关系。
-
equals(Object obj):比较当前 BigDecimal 对象与参数是否相等。
BigDecimal bigDecimal1 = new BigDecimal("123.456789");
BigDecimal bigDecimal2 = new BigDecimal("123.456789");
System.out.println(bigDecimal1.compareTo(bigDecimal2));
System.out.println(bigDecimal1.equals(bigDecimal2));
2.6.转换
-
intValue()、longValue()、floatValue()、doubleValue():将 BigDecimal 对象转换为对应的基本数据类型。
-
toBigInteger()、toBigIntegerExact():将 BigDecimal对象转换为 BigInteger对象。
BigDecimal bigDecimal = new BigDecimal("123.456789");
int i = bigDecimal.intValue();
float v = bigDecimal.floatValue();
double v1 = bigDecimal.doubleValue();
long l = bigDecimal.longValue();
BigInteger bigInteger = bigDecimal.toBigInteger();
BigInteger bigInteger1 = bigDecimal.toBigIntegerExact();
3.注意事项
BigDecimal虽然能杜绝运算过程中的精度丢失,但无法杜绝初始化过程中的精度丢失,例如:
BigDecimal bigDecimal1=new BigDecimal(1.2);
BigDecimal bigDecimal2=new BigDecimal(1.234);
System.out.println(bigDecimal1.add(bigDecimal2));
上面代码就会存在精度丢失,因为我们输入1.2和1.234后,输入的数据会转浮点数存在计算机的内存中,这种转换过程中,精度就已经丢失了,BigDecimal去内存中读这两个数时,读出来的就是精度丢失的数。
为了确保精度,尽量用字符串来初始化:
BigDecimal bigDecimal1=new BigDecimal("1.2");
BigDecimal bigDecimal2=new BigDecimal("1.234");
System.out.println(bigDecimal1.add(bigDecimal2));
4.底层实现原理
BigDecimal底层的代码可读性不是很好,这里直接给出底层实现原理的总结:
在BigDecimal内部,它使用一个整数数组来存储数值的每一位。通常情况下,数组的每个元素表示一组十进制数的位数,例如,数组的第一个元素表示最低位,第二个元素表示十位,以此类推。每个数组元素都是一个32位整数,即可以存储0到2^32-1之间的数值。
为了表示一个数值,BigDecimal还需要维护一些其他的信息,包括符号(正数、负数或零)、小数点的位置以及数值的精度。这些信息通过额外的变量来保存。
在进行数值的运算时,BigDecimal会根据操作的类型和需要的精度,对两个数值进行相应的运算,例如加法、减法、乘法和除法。运算的过程中,它会对两个数值的符号进行处理,并按照数学规则进行运算。对于除法运算,BigDecimal会通过精确的算法进行计算,避免了浮点数除法可能产生的精度损失。