Java精度损失

对于Java的float和double类型,都存在精度损失的问题。
精度损失产生的原因在于Java的数据存储采用的都是2进制形式,二进制不能准确的表示1/10等分数,只能无限趋近。

对比float,double,BigDecimal的精度:

public static void main(String[] args){
    float a = (float) 1.0;
    float b = (float) 0.965;
    double a1 = 1.0;
    double b1 = 0.965;
    BigDecimal a2 = new BigDecimal(a1);
    BigDecimal b2 = new BigDecimal(b1);
    BigDecimal a3 = new BigDecimal(new String("1.0"));
    BigDecimal b3 = new BigDecimal(new String("0.965"));

    System.out.println("float:"+(a-b));
    System.out.println("double:"+(a1-b1));
    System.out.println("BigDecimal use Double:"+a2.subtract(b2));
    System.out.println("BigDecimal use String:"+a3.subtract(b3));
}

 运行结果如下:
float:0.035000026
double:0.03500000000000003
BigDecimal use Double:0.03500000000000003108624468950438313186168670654296875
BigDecimal use String:0.035

总结:
1.float的精度损失最严重,然后依次是double,和使用参数类型为double的BigDecimal。
2.使用BigDecimal中参数为String类型的构造方法可以避免精度损失。

源码分析:
BigDecimal是如何处理精度损失的,通过查看源码发现:
针对传入参数为double类型和传入参数为String类型的处理方式有很大的不同。

传入参数为double类型:

//将一个double类型转化为long,该long类型的值中的第1个数字代表符号,后11个数字代表指数,最后52个数字代表具体的值
long valBits = Double.doubleToLongBits(val);
    //通过右移获取符号
    int sign = ((valBits >> 63) == 0 ? 1 : -1);
    //通过右移获取指数
    int exponent = (int) ((valBits >> 52) & 0x7ffL);
    //右移获取有效数字
    long significand = (exponent == 0
            ? (valBits & ((1L << 52) - 1)) << 1
            : (valBits & ((1L << 52) - 1)) | (1L << 52));

例如使用 System.out.println(Double.doubleToLongBits(0.1)); 输出的值为4591870180066957722,将其转化为二进制,结果是11111110111001100110011001100110011001100110011001101000000000,将该二进制分割成三部分:
1(<-这个是符号) 11111101110(<-这个是指数) 0110011001100110011001100110011001100110100000000(<-这个是具体的值)
后续对指数和数值进行了额外的一些操作,比较多就没仔细看,整体的思路是这样。


传入参数为String类型:
对于参数为String的情况,通过调试发现也差不多是使用指数,符号,有效数值来存储。其中有这样几个参数:intCompact是有效数值,precision代表精度(每对数值的每一位进行操作精度加一),scale代表指数,例如3,则有效数字需乘上10^-3(当发现小数点后,每处理一位scale加一)

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值