在计算机中,浮点数N是用尾数M、基数R、阶码E的形式表示的,N = M * R ^ E。通常情况下,R为2。M用定点小数的形式表示,决定了浮点数的精度;E用整数的形式表示,决定了浮点数的表示范围。
如下程序,运行结果我们希望是5,但由于浮点数的计算误差,最终打印的结果却是4.999999999999998
double x = 0; for(int i = 0; i < 50; i++){ x += 0.1; } System.out.println(x);
而在进行整形转换时,误差更大,如下语句期望结果是29,但是Java运行结果打印出来的是28
System.out.println((int)(29.0 * 0.01 * 100));
正是以上的舍入误差,导致相等比较运算不能简单地使用==运算符来进行。通常程序员都会选择一个可接受的误差范围,与目标值比较是否在此误差范围内。然而,可接受的误差范围本身可能也是无法准确给出的。
这样的表示形式决定了,浮点数无法精确表示,在计算中也容易产生误差。因此在对计算精度有较高要求的场景中,就不能使用浮点数,在Java中需要使用BigDecimal进行精确计算。
BigDecimal 是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示 BigDecimal ,换算因子表示左移小数点多少位,从而得到所期望范围内的值。因此,用 BigDecimal 表示的数的形式为 unscaledValue*10^scale。
用于加、减、乘和除的方法给 BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的BigDecimal 对象。因此,因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则 BigDecimal 可以很好地胜任该任务。
bigDecimal = new BigDecimal(new char[]{'1','2'});//char[] bigDecimal = new BigDecimal(1.2);//double bigDecimal = new BigDecimal(5);//int bigDecimal = new BigDecimal(5L);//long bigDecimal = new BigDecimal("1.2");//String
BigDecimal a = new BigDecimal("5.5"); BigDecimal b = new BigDecimal("1.2"); BigDecimal c = null; c = a.add(b); //c = a + b c = a.subtract(b); //c = a - b c = a.multiply(b); //c = a * b c = a.divide(b); //c = a / b
设置小数位数,及取舍方式的方法为 setScale(newScale, roundingMode)
newScale为小数位数,roundingMode有如下几种:
BigDecimal.ROUND_CEILING; //向正无穷方向取整,正数同ROUND_UP,负数同ROUND_DOWN
BigDecimal.ROUND_DOWN; //向0方向取整
BigDecimal.ROUND_FLOOR; //向负无穷方向取整,正数同ROUND_DOWN,负数同ROUND_UP
BigDecimal.ROUND_HALF_DOWN; //小数>0.5向非0方向进位,其他向0方向进位
BigDecimal.ROUND_HALF_EVEN; //小数偶数同ROUND_HALF_DOWN,奇数同ROUND_HALF_UP
BigDecimal.ROUND_HALF_UP; //小数>=0.5向非0方向进位,其他向0方向进位
BigDecimal.ROUND_UNNECESSARY;//断言所请求的操作具有精确的结果,因此不需要舍入。 如果在产生不精确结果的操作上指定此舍入模式,则抛出ArithmeticException
BigDecimal.ROUND_UP; //向非0方向取整
虽然BigDecimal能够精确表示数值,但是不同类型的构造函数得到的数字未必完全一样。
如下面的语句
System.out.println(new BigDecimal(0.333));
System.out.println(new BigDecimal("0.333"));
打印为
0.333000000000000018207657603852567262947559356689453125
0.333
这是因为double类型构造函数有一定的不可预知性,double类型的误差导致BigDecimal接收到的值会有误差,而String类型的构造函数结果可以预知。当必须使用double类型构造BigDecimal时,先使用Double.toString(double)方法,然后使用BigDecimal(String)构造方法,将double转换为String。
使用BigDecimal进行除法运算,如果结果出现无限小数,会抛出异常,如
Java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. at java.math.BigDecimal.divide(Unknown Source)
此时需要对除法计算设置小数位数,方法为divide(BigDecimal divisor, int scale, int roundingMode)。
BigDecimal的equals() 方法会考虑数字的换算值,即使看起来相同的数字 equals() 比较未必相等。如对于1.00和1.0000两个数字,BigDecimal的equals() 方法比较结果是不相等的,而 compareTo() 方法会认为这两个数是相等的。因此比较两个BigDecimal数值是否相等,应使用compareTo() 方法而不是equals()方法。
本文来源:程序之心,转载请注明出处!
最新内容
© 2016 - 2023 chengxuzhixin.com All Rights Reserved.