背景:float 底层用4个字节32位来表示,为什么范围比int,long还要大?
整数在计算机底层采用补码的形式表示,除去首位的符号位,剩余的位数即表示数值的范围,也就是位数越多,范围越大,那么对于单精度浮点数float,和双精度浮点数double,它们底层的数据结构是什么,它们的范围又是如何计算的,它们的精度范围又是多少那,接下来将从6方面进行阐述。
1 IEEE754浮点数标准
IEEE754标准包含一组实数的二进制表示法。它有三部分组成:
- 符号位
- 指数位
- 尾数位
三种精度的浮点数各个部分位数如下:
用float来举例:
第一部分用来存储符号位(sign),用来区分正负数,0正1负。
第二部分用来存储指数(exponent),指数也有正负之分,指数位确定大小范围。
第三部分用来存储小数(fraction),小数位决定了计算精度,因为小数位能表示的数越大,精度越大,数值越准确。
备注:浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”
2 第二部分(指数位确定范围)
需要注意的是指数可能是负数,也有可能是正数,即指数是有符号整数,而有符号整数的计算是比无符号整数麻烦的。所以为了减少不必要的麻烦,在实际存储指数的时候,需要把指数转换成无符号整数。
那么怎么转换呢?注意到float的指数部分是8位,则指数的取值范围是 -126到+127,为了消除负数带来的实际计算上的影响(比如比较大小,加减法等),可以在实际存储的时候,给指数做一个简单的映射,加上一个偏移量,比如float的指数偏移量为127,对应的double类型,存储的时候指数偏移量是1023,这样就不会有负数出现了。
3 第三部分(小数位确定精度)
float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
float:2^23 = 8388608,一共七位,由于最左为1的一位省略了,这意味着最多能表示8位数,但绝对能保证的为7位,也即float的精度为7~8位有效数字(算上整数的一位),不算整数位的话能只有7位,所以能准确表示的10进制也就6位。
double:2^52 = 4503599627370496,一共16位,同理,double的精度为16~17位,能准确表示的10进制也就15位。
4 注意点
- 程序中应尽量避免浮点数的比较,例如System.out.println(2.00-1.10==0.90),永远返回false.
- float、double类型的运算往往都不准确
5 正确的方式
- 利用整数来表示小数int或者long,例如1.01元,则可以以分为单位来表示。
- 利用BigDecimal进行浮点型运算,但是注意一定要使用String类型的构造方法,如果使用浮点型构造方法也不能得到准确的结果。
6 福利彩蛋(浮点数保留2位的几种方法)
1> double dd = (double) (Math.round(1/(float)num*100)/100.0); //先乘以100后除以100,对浮点数做进位处理。
2> DecimalFormat df=new DecimalFormat("###.00")
3>String ss = String.format("%1.2f", 1.1256);
4> NumberFormat ddf1=NumberFormat.getNumberInstance() ; ddf1.setMaximumFractionDigits(2);
5> BigDecimal b = new BigDecimal(T); T = b.setScale(2,BigDecimal.ROUND_HALF_UP).floatValue();