学习王争专栏浮点数总结
目录
1,实数的二进制科学计数法
1)实数的概念
实数是一种概念,可以理解为小数。整数也可以看作是实数比如5,可以看作是5.0。
而在java中实数可以理解为float,double两种浮点数。
整数在计算机中是以补码的方式存储的,而实数在计算机中是以一种科学计算法进行存储的。
2)十进制的科学计算数
对于十进制的科学计算法,使用excel的应该都有过一串长数变成 1.23E6。这就是简化后的科学计数法。
正常如12300000000 科学计数法为 1.23* 10^10。而再省略则可以变成xEy这样的模式,即E代表10,而y表示指数。如上可以缩减为1.23E10。
3)二进制的科学计数法
首先二进制的科学计数法已经有一套规范文件:IEEE754标准。绝大多数计算机是以此为基础存储浮点数。
3-1)浮点数转换为二进制
假设对于一个小数如12.375,要将其转换为二进制,可以考虑将其整数和小数分别转换为二进制,再以'.'进行串联,最后转换成 1100.011这样的模式,而负数-12.375,则为-1100.011。
对于整数部份的转换比较简单,上一章也有写,主要写下小数部份使用的乘2取整数。
此方法王争的我没看明白,以知乎上看到的为例。
我们对十进制计算小数的时候,小数后的每一位都对应一个权值,1/10, 1/10^2, 1/10^3。
那么同理,二进制的小数后面的值也有对应的权值, 1/2, 1/2^2, 1/2^3.
那么以0.375为例,
先以0.375/(1/2),不够,那么此位为0
再以0.375/(1/2^2),够,此位为1,剩下0.125
再以0.125/(1/2^3),够且刚好相等,此位为1,结果
得到011,与整数一串联就是 1100.011.
但是此时上面的都是除,和乘2取整法无关,可以转换为(假设floor函数为计算当前位是否够的,即是否大于1的):小数点后第一位:floor(0.375/(1/2)) = floor(0.375/0.5) = floor(0.375 * 2) = floor(0.75) 小于1,此位为0
小数点后第二位:floor(0.75 /(1/2)) = floor(0.75 * 2) = floor(1.5) 够1,去掉1,剩下0.5. 为什么此处只/1/2,而不是/1/2^2,是因为第一步取的没有取出来
.....
再加上看的方便,可以直接取整。
另注意点:
上面的是可以除尽或乘尽的,但是有些是没有办法除尽的或除尽的,比如0.1
其小数点后的二进制为 00001 0001 0001,无限循环,这也是浮点数会精度缺失的原因。
二,实数二进制的存储:定点数
上面的存储方式,计算机还是没法看懂,可以用如下的定点数方式存储(点号是固定的)
第一位决定正负,然后固定的位数存储整数,固定的位数存取小数
但该方式还有个问题:
1,会浪费很多的存储空间,假如都是整数或小数
2,取值范围会很窄。比如整数部份是20位,那么最大的整数就是2^20。同样的4字节,比int范围小很多
三,实数二进制的存储:浮点数
为了有效利用空间,于是发明了浮点数,即点号在二进制中不固定的。
对应到java的就是float和double.
1)浮点数的公式:二进制科学计数法
公式:(-1)^S*M*2^E
其中S是符号位,0表示正数,1表示负数, M表示的是有效数字域,E表示的是指数域。
例如上面的12.375 可以表示为(-1)^0 * 1.100011*2^3。 其中S为0,
E为3 其二进制为 1000 0010 并不是0000 0011(具体的下面讲),
M为100011 完全体float的E是 1000 1100 0000 0000 0000 000 (前面是少了个1,具体的下面讲解)
2)浮点数公式详解。
都在float进行说明。
2-1)符号位S
对于S比较好理解,同整数一样的,用于表示符号位,根据公式,当为0时,刚好-1^0 为0,且示正,1时为-1,表示负数。
2-2)有效数字域M
有效数字域M。
因IEEE754规定,其整数必须为1,因此整数位可以不需要存储,只存储小数即可,整数位默认为1。
因此对于23位有效数字域的单点数浮点数float,实即有24位有效数字。
整数默认为1了,此时没法表示0了,此时使用指数域来表示,当E为0时,IEEE754规范要求,从有效数字域读取的二进制位不需要在前面添加1.
2-3)指数域E
指数域没有使用原码或补码来存储,float的指数域的范围是[-126, 127]。这里需要说明一下,指数域没有使用原码与反码,也就没有符号位这一说。 指数域是8位,实际范围是0-255 (0000 0000 -- 1111 1111)。但是0-255没有办法表示负数,因此存储的时候都需要对值进行+127操作,这也是上面的值为什么是1000 0010 并不是0000 0011的原因,而当使用时,就需要-127。此时还有两个值0与255不在其范围中,这两个值有特殊用处
先看一下-12.375的二进制科学法的表示。
指数域值为0时:用来辅助表示浮点数0
有效数字域M。默认整数位1,那么就无法表示0时的情况了。
那么当指域数值为0时,就不再有效数字域的二进制位前加1,即实现了整数位的默认0.
此时如果有效数字域M里的值全为0,指数域E也为0,那么就表示浮点数0了。
指数域值为255时,用来表示无穷大或无效数NaN
当指数域值为255时,如果M有效数字域里的值全为0,那么当S为0时表示正无穷大,S值为1时,表示负无穷大。
如果M的数字域里的值不为0,那么表示无效数NaN。
Java jdk里Float里的,可以用来表示正无穷和负无穷以及无效数Nan
public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
public static final float NaN = 0.0f / 0.0f;
四,浮点数的表示范围和精度
1)浮点数的表示范围
浮点数有效位最大情况下为23位全是1,加上整数的1。为1.11111
而指数位的范围为-126-127.
那么最大值为1.111....1111*2^127
最小值为-1.1111.111*2^127
比同为4字节的int大的多,比long都大
但是牺牲了精度,很可能会有不同的整数最终存为float的是一样的。
根据鸽笼原理,32个bit,最多存储2^32个实数,表达范围大了,必然有重复的。
2)浮点数的精度(重)
浮点数的有效位是24位,那么如果要表达的数的个数超过了24位,那么就就会做出取舍(不是直接截取,特有的四舍五入)。就会导致精度丢失。
3)浮点数整数能准确表示的整数范围以及最小正数(重)
1,整数范围
因为有效位是24位,无符号位,那么最多能表示的数据为2^24个,范围则为0-2^24-1,负数则为 -2^24+1 - 0。所以完整的范围为 -2^24 + 1 ~~ 2^24 - 1。
后续的则是在此基础上*2,除2,或者根据浮点数的截取方式定。
如果截图完整一段,那么对于2的整数倍的数是可以准确表示的,但现在并不确定是怎么截取(没有深入研究了)。
2,最小正数
E的取值范围为-126-127,要最小取-126
M最小的值为1.000,即23位上全是0
因此最小正数为1*2^-126。
五,浮点数的替代品:BigDecimal
1)为何使用BigDecimal
浮点数容易精度丢失,而BigDecimal不会。在BigDecimal中整数位与小数位是分开计算的。
2)使用注意事项
不能传入float来构造,因为在float传入时精度就丢失了,需要传入的是字符串。
六,double双精度的
暂时理解为,除了范围变大了,内核是一样的。只是能精准表示出来的值更多了。
粗略算了下,E的域为0 ~ 2047 (无符号位)
那么有效范围为-1022 ~ 1023。float是加127,double为1023.
float的0对应double的0
float的255对应double的2047.
双精度double有效位M为52+1的有效位,精度大很多。
而指数域