Java浮点数类型(float和double)总结
记得刚⼊⾏的时候,就有前辈谆谆教导,在Java⾥⾯如果是要做数值的精确计算,⼀定不要⽤float/double,⽽要以BigDecimal代替,原因⼤概是浮点数类型会失真,记不准。当时就记住了这个结论,也⼀直没去深究,每次跟别⼈讨论还都振振有词,今天突然发现⾃⼰好像对其中的原理也没有太弄明⽩,今天正好整理⼀下。
1、浮点数类型为什么会失真?
2、既然浮点数会失真,那为什么还要⽤它呢?那为什么在Java⾥不⼲脆都⽤BigDecimal得了。
3、Java⾥浮点数运算有些什么注意事项
1、浮点数为什么会失真?
什么是浮点数?其实就是实数(real number)的⼀种表⽰⽅法。实数⼤家都知道,包括有理数和⽆理数,⽐如说3,3/4, 0.85, pi
()等。那在计算机⾥⾯,如何来表⽰实数呢?有两种⽅法:定点数和浮点数。定点数很直观,⽐如说你有5位数字位,你可以表⽰110.82或者0.0001,搁在计算机⾥⽆⾮就是把⼗进制换成⼆进制
就⾏。但是定点数有⼀个缺点,费空间,给定⼀个长度表⽰不了多⼤的数字。最直观的⽐较就是Java⾥integer和float,同为4个字节,但是能够表⽰的值的范围就差别很⼤。所以,为了在有限的存储空间⾥尽可能表现更⼤范围的实数,浮点数就应运⽽⽣了。
其实,我们对浮点数并不陌⽣,最常见的例⼦就是科学计数法(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
..
⼀直循环下去,所以得到(2).
三、浮点数格式
  IEEE标准754规定了三种浮点数格式:单精度、双精度、扩展精度。前两者正好对应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⾥可以⽤以下的⽅法来验证:
浮点数的基数什么意思
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);
参考:
IEEE Standard 754  Floating Point Numbers ()
Fundamentals of computer science II IEEE floating-point representations of real numbers ( )
Representing money:
Financial (monetary) computations using floating point arithmetic [in JAVA]:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。