BigDecimal详解

世界杯足彩 836

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、浮点类型坑示例

public static void main(String[] args) {

float a = 1;

float b = 0.9f;

System.out.println(a - b);

}

2、BigDecimal 的常用方法

2.1、常用构造方法

new BigDecimal(int val):创建一个具有参数所指定整数值的对象。

new BigDecimal(double val):(不推荐使用,因为存在精度丢失问题)创建一个具有参数所指定双精度值的对象。

new BigDecimal(long val):创建一个具有参数所指定长整数值的对象

new BigDecimal(String val):(推荐使用)创建一个具有参数所指定以字符串表示的数值的对象。

public static void main(String[] args) {

BigDecimal b_int = new BigDecimal(10);

BigDecimal b_double = new BigDecimal(0.1);

BigDecimal b_float = new BigDecimal(0.1f);

BigDecimal b_string = new BigDecimal("0.1");

BigDecimal b_valueof = BigDecimal.valueOf(0.1);

System.out.println("b_int:" + b_int);

System.out.println("b_double:" + b_double);

System.out.println("b_float:" + b_float);

System.out.println("b_string:" + b_string);

System.out.println("b_valueof:" + b_valueof);

}

输出如下:

使用构造函数时,推荐用 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进行除运算时,一定要指定精度和舍入模式。

public static void main(String[] args) {

BigDecimal a = new BigDecimal("10");

BigDecimal b = new BigDecimal("20");

BigDecimal c = new BigDecimal("30");

BigDecimal add = a.add(b);

BigDecimal subtract = a.subtract(b);

BigDecimal multiply = a.multiply(b);

BigDecimal divide = a.divide(b);

System.out.println("加结果为:" + add);

System.out.println("减结果为:" + subtract);

System.out.println("乘结果为:" + multiply);

System.out.println("除结果为:" + divide);

// 除不尽将会抛出异常

BigDecimal divide2 = a.divide(c);

}

输入结果如下:

其他常见方法:

方法

含义

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进行除运算时,一定要指定精度和舍入模式。

public static void main(String[] args) {

BigDecimal b1 = new BigDecimal("1.0");

BigDecimal b2 = new BigDecimal("3.0");

// 保留3位小数,且四舍五入

BigDecimal divide = b1.divide(b2, 3, RoundingMode.HALF_UP);

System.out.println(divide);//0.33

}

3.1、BigDecimal 中的舍入模式

ROUND_UP(向上舍入)

定义与行为:

这种舍入模式总是在非零舍弃部分的左边一位加 1。无论是正数还是负数,只要有需要舍弃的非零部分,就会向上进位。

示例:

对于正数,如将1.1舍入到整数,按照ROUND_UP模式,结果是2。因为小数部分0.1是非零的,所以将整数部分1加1得到2。

对于负数,如将-1.1舍入到整数,结果是-2。因为小数部分0.1是非零的,所以将整数部分-1减1得到-2。

ROUND_DOWN(向下舍入)

定义与行为:

总是舍弃非零舍弃部分,无论正数还是负数,都不进行进位操作。

示例:

对于正数,如将1.9舍入到整数,按照ROUND_DOWN模式,结果是1,因为直接舍弃小数部分0.9。

对于负数,如将-1.9舍入到整数,结果是-1。同样是直接舍弃小数部分0.9。

ROUND_CEILING(天花板舍入)

定义与行为:

如果是正数,行为类似于ROUND_UP;如果是负数,行为类似于ROUND_DOWN。其目的是将数字舍入到正无穷方向。

示例:

对于正数,如将1.1舍入到整数,结果是2,和ROUND_UP模式相同。

对于负数,如将-1.1舍入到整数,结果是-1,和ROUND_DOWN模式相同。

ROUND_FLOOR(地板舍入)

定义与行为:

如果是正数,行为类似于ROUND_DOWN;如果是负数,行为类似于ROUND_UP。其目的是将数字舍入到负无穷方向。

示例:

对于正数,如将1.9舍入到整数,结果是1,和ROUND_DOWN模式相同。

对于负数,如将-1.9舍入到整数,结果是-2,和ROUND_UP模式相同。

ROUND_HALF_UP(四舍五入)

定义与行为:

如果舍弃部分大于或等于0.5,则在非零舍弃部分的左边一位加1;否则,直接舍弃。这是最常见的舍入模式,符合我们日常的 “四舍五入” 概念。

示例:

将1.5舍入到整数,结果是2,因为小数部分0.5满足大于或等于0.5的条件,所以将整数部分1加1。

将1.4舍入到整数,结果是1,因为小数部分0.4小于0.5,所以直接舍弃。

ROUND_HALF_DOWN(五舍六入)

定义与行为:

如果舍弃部分大于0.5,则在非零舍弃部分的左边一位加1;否则,直接舍弃。与ROUND_HALF_UP的区别在于,当舍弃部分等于0.5时,不进位。

示例:

将1.5舍入到整数,结果是1,因为小数部分等于0.5,不进位。

将1.6舍入到整数,结果是2,因为小数部分0.6大于0.5,所以将整数部分1加1。

ROUND_HALF_EVEN(银行家舍入)

定义与行为:

也称为 “银行家舍入”。如果舍弃部分左边的数字是偶数,且舍弃部分等于0.5,则直接舍弃;如果舍弃部分左边的数字是奇数,且舍弃部分等于0.5,则在非零舍弃部分的左边一位加1。对于其他情况,和ROUND_HALF_UP类似。

示例:

将2.5舍入到整数,结果是2,因为整数部分2是偶数,且小数部分是0.5,所以直接舍弃。

将3.5舍入到整数,结果是4,因为整数部分3是奇数,且小数部分是0.5,所以将整数部分3加1。

9、最佳实践

public static void main(String[] args) {

// 使用字符串参数构造函数

BigDecimal a = new BigDecimal("10");

// 或使用BigDecimal.valueOf方法

BigDecimal b = BigDecimal.valueOf(30);

BigDecimal add = a.add(b);

BigDecimal subtract = a.subtract(b);

BigDecimal multiply = a.multiply(b);

//进行除运算时,指定精度和舍入模式,避免除不尽导致报错

BigDecimal divide = a.divide(b, 2, RoundingMode.HALF_UP);

System.out.println("加结果为:" + add);

System.out.println("减结果为:" + subtract);

System.out.println("乘结果为:" + multiply);

System.out.println("除结果为:" + divide);

}

输出如下: