首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Java浮点数施用小结

2013-06-26 
Java浮点数使用小结1.??? 引子??? 平常在代码中,从不缺少使用浮点数的地方。浮点数可以使用float和double类

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版本。


Java浮点数施用小结 图2.1 json-lib包引用

?

Java浮点数施用小结图2.2 问题1示例
?
Java浮点数施用小结图2.3 问题1结果


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

?

2.2 问题原因

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

?

Java浮点数施用小结图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对象,为了节省开销,使用了第一个方法进行了处理。

??? 解决问题的代码如下。
?

Java浮点数施用小结图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)1823Double(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类型。

热点排行