Java浮点数运算的精确度和四舍五入的问题

时间:2023-01-08 09:54:59

浮点运算有时是不精确的,只要是超过精度能表示的范围就会产生误差。往往产生误差不是因为数的大小,而是因为数的精度,产生的结果接近但不等于想要的结果,所以在使用 float 和 double 作精确运算的时候往往采用一些方案来实现运算的准确。

Java中声明的小数默认是double类型的。double d=2.1;如果声明float f=2.1;则会报错,而应该写为float f=2.1f;或float f=(float)2.1;float 内存分配4个字节,占32位,double型内存分配8个字节,占64位。

1.float和double类型转换

两种类型可以互相转换,如两种类型数据求和,float类型可以直接转换为double,因为Java在运算时会自动的提升变量的精度来进行运算,double比float精度更高,所以可以自动的从float转化至double再进行运算;而double不可直接转换为float,同样必须先强制转换。但是转换后计算结果可能出现错误。因为float到double需要补位,直接转换会设计到精度问题,所以需要借助字符串,保证不丢失数据。如:

float f=2.1f;
double d = Double.parseDouble(String.valueOf(f));

或用

float f=2.1f;
BigDecimal bd = new BigDecimal(String.valueOf(f));
double d = bd.doubleValue();

2.运算时的不准确

在小数运算时也会出现这样的不准确现象,此时需要使用BigDecimal进行精确的小数运算,将浮点数转为String。BigDecimal是Java提供的一个不可变的、任意精度的有符号十进制数。

如计算3.564与5.13的和,需要用String参数的构造函数来构造BigDecimal对象,不能使用double类型参数的构造函数,否则没有效果。

double d1=3.564;
double d2=5.13;
BigDecimal b1 = new BigDecimal(Double.toString(d1)); 
BigDecimal b2 = new BigDecimal(Double.toString(d2));
System.out.println(b1.add(b2));

在这个类上定义了很多进行加减乘除运算的方法:add()、subtract()、multiply()、divide(),另外还有进行小数点移位运算的movePointLeft()和movePointRight()等。

3.四舍五入的问题:

在运算时,需要对计算结果四舍五入,有如下几种方式可选用。

(1)使用BigDecimal

对结果进行四舍五入时,scale参数指定精度,RoundingMode是舍入模式,它是枚举类型。

BigDecimal b = new BigDecimal(Double.toString(v));
return b.setScale(scale, RoundingMode).doubleValue();

在除法运算中,当发生除不尽的情况时,可由scale参数指定精度,之后的数字四舍五入

BigDecimal b1 = new BigDecimal(Double.toString(v1));  
BigDecimal b2 = new BigDecimal(Double.toString(v2));  
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 

(2)使用DecimalFormat

DecimalFormat对数值格式化时可以进行四舍五入,但不是一般认识上的四舍五入,在DecimalFormat的API中如是介绍:DecimalFormat 提供 RoundingMode 中定义的舍入模式进行格式化。默认情况下,它使用 RoundingMode.HALF_EVEN。例如:

DecimalFormat decFormat = new DecimalFormat("0.00");
System.out.println(decFormat.format(sim));

如用以上代码进行格式化,则3.456~3.46;3.454~3.45;3.455~3.46;3.465~3.46(舍弃部分左边的数字为偶数),可以修改RoundingMode,如添加设置decFormat.setRoundingMode(RoundingMode.HALF_UP);即为通常的四舍五入模式。

(3)使用格式控制

double d = 3.1415926;
String result = String .format("%.2f",d);

其中%.表示小数点前任意位数,2表示两位小数,格式后的结果为f表示浮点型。

此外,还有Math.round()方式,但它并不能正确的满足条件,它返回最接近参数的long,结果将舍入为整数。

如有不正确之处,欢迎指正指教~