java基础-浮点数陷阱
2018-02-19 本文已影响26人
李2牛
计算机的数值采用的是二进制形式存储。
根据IEEE754标准,
单精度浮点数为32位:
- | sign | exponent | fraction |
---|---|---|---|
位数 | 1 bit | 8 bit | 23 bit |
编号(右起) | 31 | 30 ~ 23 | 22 ~ 0 |
双精度浮点数为64位:
- | sign | exponent | fraction |
---|---|---|---|
位数 | 1 bit | 11 bit | 52 bit |
编号 | 63 | 62 ~ 52 | 51 ~ 0 |
0.99用的有效数字部分(fraction):
0.99 * 2 = 1+0.98 --> 1
0.98 * 2 = 1+ 0.96 --> 1
0.96 * 2 = 1+0.92 -- >1
0.92 * 2 = 1+0.84 -- >1...............
所以计算机二进制无法精确表示浮点数,精度有损失。示例代码如下:
public class FloatTrap {
public static void main(String[] args) {
double a = 1;
double b = 0.99;
System.out.println(a - b);
}
}//:output
//0.010000000000000009
java的浮点类型有double(双精度)和float(单精度)。对于精确的浮点数运算可以使用java.math.BigDecimal类,但是这样依然会有精度损失。最佳的方法是使用string转double或者float,这样是没有精度损失的。
使用BigDecimal类示例
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
double a = 2.0;
double b = 1.9;
System.out.println(2.0 - 1.9);
System.out.println(Main.round(Main.subtract(a,b)));
}
/*精确的浮点数计算*/
private static double subtract(double m, double n){
BigDecimal tm = new BigDecimal(Double.toString(m));
BigDecimal tn = new BigDecimal(Double.toString(n));
return tm.subtract(tn).doubleValue();
}
/*四舍五入保留*/
private static double round(double target){
BigDecimal tmp = new BigDecimal(Double.toString(target));
BigDecimal one = new BigDecimal(Double.toString(1.0));
return tmp.divide(one,BigDecimal.ROUND_HALF_UP).doubleValue();
}
private static double div(double tm,double tn){
BigDecimal tempM = new BigDecimal(Double.toString(tm));
BigDecimal tempN = new BigDecimal(Double.toString(tn));
return tempM.divide(tempN,10, BigDecimal.ROUND_HALF_UP).doubleValue();
}
private static double add(double tm, double tn){
BigDecimal tempM = new BigDecimal(Double.toString(tm));
BigDecimal tempN = new BigDecimal(Double.toString(tn));
return tempM.add(tempN).doubleValue();
}
}