float为什么比int表示的范围广?
什么是单精度和双精度?
float表示小数的时候为什么会有精度丢失?
带着这几个问题,我们来探究下java中float类型在计算机的表示形式。
java中int占用4个字节,float也是占用4个字节,但是为什么float表示的范围要比int大呢,因为两者在计算机内中表示的方式不一样,int是4个字节32位,每一位都是二进制小数表示,最高位0代表是正数,最高位为1代表是负数,所以int的范围是-2^31~2^31-1;
float也是4个字节,遵循IEEE-754格式标准,在计算机中表示有三个部分组成:符号s底数m和指数e。
符号s:最高位,0为正数;1为负数,占用一位;
指数e:是该浮点数的指数,二进制表示,最高位为指数符号为1表示大于1,0表示小于1,其实他是用的偏移量表示的,偏移大小为127,没有用补码表示,占用一个字节8位;
底数m:是该浮点数的实际值,二进制形式表示,m的范围是【1,2】或者【0,1】占用23位。
所以float在计算机的表示如下面所示:
SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
我们先说下10进制和二进制的快速转换方法。
10进制整数2进制转换是除2求商和余数,比如十进制17的二进制计算为:
17 /2=16余1
16 /2= 8 余0
8 /2= 4 余0
4 /2= 2 余0
2 /2= 1 余0
1 /2= 0 余1
一直计算到商为0为止,17的二进制表示就是:10001
10进制小数2进制表示是乘2求积,比如0.16的二进制计算为:
0.16*2=0.32取0
0.32*2=0.64取0
0.64*2=1.28取1
0.28*2=0.56取0
0.56*2=1.12取1
0.12*2=0.24取0
。。。
所以0.16的二进制表示就是001010….. 一直循环下去,所以除非小数最后为5才能*2取整,否则都会无线循环下去,这就是为什么浮点小数精度丢失的问题。
下面我们距离来说明10进制浮点数在计算机的表示, 拿17.16来说:
1.首先为正数,s为0
2.二进制为10001.0010100011110101….小数点左移4位为1.00010010100011110101…
3.指数位为4+127=10000011,所以指数位位10000011
4.由于默认最左边为1,所以底数为00010010100011110101…
5.最后17.16的二进制表示就是:01000001100010010100011110101……总共32位,后面的舍弃
再拿0.16举例:
1.首先为整数,s为0
2.二进制为0.0010100011110101……小数点右移4位为1.0100011110101
3.指数位为4+127=10000011,然后取反为01111100,所以指数位位01111100
4.底数位0100011110101…
5.最后0.16的二进制表示就是:0011111000100011110101…
float的(正数)范围:
最小值:Float.MIN_VALUE=1.4E-45 (2的-149次方:指数位127+底数22)
最大值:Float.MAX_VALUE=3.4028235E38 (2的128次方-1)
float是单精度浮点数,4个字节;double是双精度浮点数,8个字节,e占11位,m占52位。
注意事项:
1.价格在数据库中保存建议是无符号整形,避免在java转换成浮点数时出现精度丢失的问题;
2.如果需要用浮点型数据进行运算,建议使用BigDecimal,但是BigDecimal的构造函数一定要使用字符串的,否则一样会出现精度丢失的问题:BigDecimal(“123f”)而不是BigDecimal(123f)。
3.Float.MIN_VALUE是:1.4E-45,不是一个负数,double也是的。但是整型的MIN_VALUE都是负数
4.float的m只有23位,2^23=8388608,总共有7位10进制数字,由于最左边的1省略了,所以表示的有效数字最高精确度位7~8位,包括整数部分,8位后的数字肯定不是准确的