java double 浮点数_Java浮点数类型(float和double)总结

现在我们的程序中,时常会用到金额以及其他数据的计算,但往往计算结果和我们预计的会出现差异。比如: double a = 0.7;double b = 0.11;System.out.print

记得刚入行的时候,就有前辈谆谆教导,在Java里面如果是要做数值的精确计算,一定不要用float/double,而要以BigDecimal代替,原因大概是浮点数类型会失真,记不准。当时就记住了这个结论,也一直没去深究,每次跟别人讨论还都振振有词,今天突然发现自己好像对其中的原理也没有太弄明白,今天正好整理一下。 1、浮点数类型为什么会失真? 2、既然浮点数会失真,那为什么还要用它呢?那为什么在Java里不干脆都用BigDecimal得了。 3、Java里浮点数运算有些什么注意事项 1、浮点数为什么会失真? 什么是浮点数?其实就是实数(real number)的一种表示方法。实数大家都知道,包括有理数和无理数,比如说3,3/4, 0.85, pi (3.1415926...)等。那在计算机里面,如何来表示实数呢?有两种方法:定点数和浮点数。定点数很直观,比如说你有5位数字位,你可以表示110.82或者0.0001,搁在计算机里无非就是把十进制换成二进制就行。但是定点数有一个缺点,费空间,给定一个长度表示不了多大的数字。最直观的比较就是Java里integer和float,同为4个字节,但是能够表示的值的范围就差别很大。所以,为了在有限的存储空间里尽可能表现更大范围的实数,浮点数就应运而生了。 而浮点数存储的标准大多遵从于IEEE754标准,而该标准的实现原理就决定了有些浮点数不可能被精确表述,只能是一个近似值。关于该标准有兴趣的可以去看看那巨长的pdf:IEEE Standard 754 for Binary Floating-Point Arithmetic (http://www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF). 此外还有和这相关的一片文章:What Every Computer Scientist Should Know About Floating-Point Arithmetic (http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html) 其实,我们对浮点数并不陌生,最常见的例子就是科学计数法(scientific notation),比如把987654.321表示为9.87654321X10^5。浮点数的通用表示方法包括一个基数(base,通常为偶数)和一个精度p。比如 = 10 and p = 3,那么0.1就表示为1.00 X 10^-1. 如果 = 2 and p = 24, 那么0.1就不能被精确表述,只能是个近似值:1.10011001100110011001101 × 2-4.因为计算机里都是以二进制来表示一个浮点数,所以这也就是浮点数会失真的原因。至于0.1是如何转化成1.10011001100110011001101 × 2-4,具体过程如下: 用2乘十进制数的小数部分,取乘积的整数为转换后的二进制数的最高位数字; 再用2乘上一步乘积的小数部分,取新乘积的整数为转换后二进制小数低一位数字; 重复第二步操作,直至乘积部分为0,或已得到的小数位数满足要求,结束转换过程。 ---0.1 * 2 0 0.2 * 2 0 0.4 * 2 0 0.8 * 2 1 0.6 * 2 1 0.2 * 2 0 0.4 * 2 0 0.8 * 2 1 0.6 * 2 .. 一直循环下去,所以得到0.0001100110011...(2). 知道了如何用二进制来表示一个小数还不够,IEEE754这个标准不是把转换后的二进制字符串直接存在内存里,而是做了一些处理。下面是直接摘抄自“解读IEEE标准754:浮点数表示” (作者为soloforce@linuxsir.org, http://bbs.linuxsir.org/showthread.php?t=262207) 三、浮点数格式

IEEE标准754规定了三种浮点数格式:单精度、双精度、扩展精度。前两者正好对隐约记得,浮点数判断大小好像有陷阱,因为底层的二进制数不能精确表示所有的小数。有时候会产生让人觉得莫名其妙的事情。 如在java中,

0.99999999f=应C语言里头的float、double或者FORTRAN里头的real、double精度类型。限于篇幅,本文仅介绍单精度、双精度浮点格式。

★ 单精度:N共32位,其中S占1位,E占8位,M占23位。

★ 双精度:N共64位,其中S占1位,E占11位,M占52位。 ---引用结束 还是用0.1做例子,如果它被申明为float,那它转换为2进制就成了1.10011001100110011001101 × 2-4,那它的存储形式为: S=0 M=10011001100110011001101(小数点前的1是省略的) E=01111011(细心的一看不对呀,这个二进制转换成10进制是123,不是-4呀?其实E的存储是采用的补码的形式,也就是它存储的是实际的值+127之后的值,-4+127=123,这就对了) 合起来就是“001111011 10011001100110011001101”了,要想知道这个推理这个对不对,在Java里可以用以下的方法来验证: Integer.toBinaryString(Float.floatToIntBits(0.1f))//结果是111101110011001100110011001101,那是因为前面两个0在输出的时候被去掉了 Long.toBinaryString(Double.doubleToLongBits(0.1d)) 2、既然浮点数会失真,那为什么还要用它呢?那为什么在Java里不干脆都用BigDecimal得了。 因为double跟BigDecimal比,有效率上和空间上的优势,double节省memory,cpu,而且用double的代码比BigDecimal也更简洁,当对结果的正确性有一定的容忍程度的地方可以使用double。而如果你要精确表示某个值,经常举的例子就是比如涉及到钱的字段,推荐使用BigDecimal。当然还有一种办法是使用long,通过将小数乘以某个倍数(比如100,1000)来将其转换为一个整数。 3、Java里浮点数运算有些什么注意事项 如果你用double来表示price或者quantity之类的信息,或者这些字段的定义是你没有办法更改或决定的,那你至少要保证的一点就是利用BigDecimal来完成算术运算。因为如果使用double来直接运算,运算过程中就会产生很多误差。最简单的例子: System.out.println(10.1d - 9.93d);System.out.println(5.85d - 3.21d);System.out.println(0.1d+0.1d+0.1d+0.1d+0.1d+0.1d+0.1d+0.1d+0.1d+0.1d); 参考: 浮点数在计算机中存储方式 作者: jillzhang 原文出自:http://www.cnblogs.com/jillzhang/archive/2011/10/13/793901.html IEEE Standard 754

Floating Point Numbers ( http://steve.hollasch.net/cgindex/coding/ieeefloat.html) Fundamentals of computer science II IEEE floating-point representations of real numbers ( http://www.cs.grinnell.edu/~stone/courses/fundamentals/IEEE-reals.html) Representing money:

http://www.javapractices.com/topic/TopicAction.do?Id=13 Financial (monetary) computations using floating point arithmetic [in JAVA]:

http://blog.udby.com/archives/13

最近在项目中碰到了一个业务逻辑计算,代码如下(示例代码) double val1 = ...; double val2 = ..., double dif = ..., if (Math.abs(val1 - val2-dif) == 0){   /

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值