Java浮点数使用小结

1.    引子

    平常在代码中,从不缺少使用浮点数的地方。浮点数可以使用float和double类型进行定义。默认都是使用的double类型,如果需要声明为float类型,需要显示地加F或者f,比如Float fNumber = 1.234F。往常个人在使用的时候,使用float居多(在java的基本类型中,Float是32位,4个字节;Double是64位,8个字节;所以float相对来说节省内存),近来踩了两个坑,觉得还是double比较省心。记录如下。

 

2.    问题1 – 精度错误

2.1 表现形式问题一示例如下。

    需要将一组对象转换成json,使用的json包是json-lib 2.2版本。


图2.1 json-lib包引用

 

图2.2 问题1示例
 
图2.3 问题1结果


    可以看到在将浮点数转换成json数据的时候,Float类型的浮点数会出现精度错误的问题。

 

2.2 问题原因

    排查原因,发现在json-lib的库里面,对于Float类型的数据,是先将其转换成Double类型之后,然后再进行处理的。处理代码如下。

 

图2.4 json-lib对于Float的处理


    Float的doubleValue方法是直接进行类型转换,即return (double)value。
    Float和Double在内存中的存储长度不同,将Float转换成Double类型的时候,如果直接取Float的doubleVlaue方法,会进行位数的补全;特别是在有小数部分的时候,这样是不能保证精度的,这样就引起了我们现在的问题。

 
2.3 解决方法

 

    解决问题的方法有两个思路。

  • 在整个系统中以double类型存储,替换掉现有的以float类型存储的方式;
  • 找到float类型转换成double类型的安全的方法。

    第一个思路,可以彻底的解决这个问题,但是对于一个系统来说,进行全局替换需要慎重,所以暂时不进行这样的修复;
    第二个思路,可以有以下几种方案。

  • 因为我们的精度要求保留到小数点之后的两位即可,所以可以将原值value乘以100,然后取long值,将结果再除以100.0转换成double类型;
  • 使用BigDecimal;
  • 先将float类型的数值转换成String,然后将String转换成Double类型的即可。比较了一下,调用Double.valueOf(String value)方法和直接新建Double对象(new Double(String value))都是可以的;但是前者只需要创建对象一次,后者需要新建两个Double对象,为了节省开销,使用了第一个方法进行了处理。

    解决问题的代码如下。
 

图2.5 问题解决


    先判断要处理的值是float类型,然后调用Double的valueOf方法进行处理。


3.    问题2 – 大数表示问题

3.1 表现形式


    解决了上面这个json输出的问题之后,又发现了一个问题。对于一些较大的数值,比如9999999.99;最终的输出结果为1.0E7,而不是预期的数值。


3.2 原因分析


    按理来说,float存储的范围应该远大于这个数值,经过一系列的调试,发现是float类型转换成String的时候出现的问题。进一步深入,发现是float类型的存储结构导致的。
    在java中,float类型存储的长度是32bit,double类型的存储长度是64bit。如下表所示。


表3.1 float和double在java中的存储

 符号位(bit)指数位(bit)尾数位(bit)
Float(32bit)1823
Double(64bit)11152


    其中:

  • 符号位表示数值的正负;
  • 指数位表示数值的取值范围,以补码形式存储;
  • 尾数位表示数值的精度。

    对于float来说,2^23=8388608;所以float类型的数值,如果有效数字的数值超过了8388608这个值,系统会自动找一个近似的结果进行代替。这也是我们这里存储9999999.99的时候,结果异常的原因。这个值在存储的时候就已经被处理成了1.0E7,所以不论采用什么方式进行处理,都不会有改善的。这里也说明,float类型,可以保证6-7位的有效数字(6位及以下是绝对可以保证的,但是7位的时候,这个有效数字的数值如果超过了8388608,就无法进行表示了)。
    对于double类型,2^52=4503599627370496;所以double类型的有效数字在15-16位。
    所以我们遇到的问题2,目前是没有办法进行解决的。后续还是安排使用double类型替换float类型。

 

4.    小结

     本文从两个问题说明了float类型的“坑爹”之处,如果不是对业务以及业务的发展特别有把握,还是建议使用double类型。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值