我和Python的Py交易》》》》》》 浮点数的身世之谜

什么是浮点数?html

在数据类型中写道,浮点数是带小数点的小数,这个概念是不许确的;浮点数是除了无限不循环小数以外的小数,也就是能够用分数表示的带小数点的数。spa

好了,浮点数就这些内容,讲完了,各回各家,各找各妈。code

不想回家的就继续往下看咯。。。htm

 

虽然浮点数中没有无限不循环小数可是有无限循环小数啊,计算机内存是有限的是怎么放下无限多个数的?blog

很简单啊,既然放不下这么多我就四舍五入一下把它变成能够放下的有限小数。内存

那这就涉及到的怎么对无限循环小数超出计算机内存储存范围的有限小数进行四舍五入,在小数点后几位小数进行四舍五入。get

咱们就须要一个标准对上述等问题进行规范,已经有人给出了这个标准IEEE给出了IEEE 754 数学

IEEEIEEE 754是什么东西?既然都这么问了,那就给出百度百科的连接好了。it

 

n=0
for i in range(10):
    n+=0.1
    print(n)

# 打印结果
# 0.1
# 0.2
# 0.30000000000000004
# 0.4
# 0.5
# 0.6
# 0.7
# 0.7999999999999999
# 0.8999999999999999
# 0.9999999999999999

不对啊,0.1是有限小数啊,不用四舍五入啊!干吗会出现这样的鬼结果。class

 嘿嘿,这个怪我。

上文所说的的“计算机内存是有限的是怎么放下无限多个数的”’是我故意误导,计算机储存信息是经过二进制数进行储存的,这个浮点数也要转换二进制才能够储存在计算机中,0.1的二进制表示是无限循环的。

因此舍入操做针对的是浮点数转换的二进制数,且舍入的规则也就变成了0舍1入,IEEE 754 中文叫作IEEE二进位浮点数算术标准

在这也顺带科普一下,小数如何转换为二进制数。

是否是很爽,原来浮点数就是这么被放的计算机里面去的,那你又错了

二进制小数里的小数点怎么存进计算机的内存中?(计算机储存数据都是用二进制来表达)

爽不爽,是否是有个人冲动,嘿嘿。解决方法也在IEEE 754

 

IEEE 754 中规定了四种表示浮点数值的方式:单精确度(32位比特储存浮点数)、双精确度(64位比特储存浮点数)、延伸单精确度(43位以上,不多使用)与延伸双精确度(79位以上)。

C语言的float一般是指IEEE单精确度,而double是指双精确度,而Python中只有双精确度(float)

如今只说说双精确度(64位比特储存浮点数,下文看懂了,剩下的三种都天然而然就懂。

你们还记得科学计数法吗?不记得的问百度去,省得压不住小学数学老师的棺材板。

十进制  0.75    科学计数法  7.5*10^-1

十进制  2.75    科学计数法  2.75*10^0

类比

二进制  0.11    科学计数法  1.1*2^-1

二进制 10.11   科学计数法  1.011*2^1

能够得出一个通式

V=(-1)^S*M*2^E

S为 0 or 1

1<=M<2

E是整数

如今咱们就开始分配那储存浮点数的64位比特

 

最高的1位比特(bit)Sign储存的是符号位S(-1)^S表示符号(正负号,S=0正号,S=1为负)

接着的11位比特(bit)Exponent储存的是2^E幂运算的指数E固然E的储存也要转换为二进制数进行E为负数小数点向前移|E|位,为正小数点向后移|E|位);

剩下的52位比特(bit)Significant储存有效数字M这也是Python的浮点数大约精确的17位的缘由,为何,本身想去

 E转换为二进制数储存,和浮点的储存不一样

E有正负,E转换为二进制储存,不像浮点数同样,用单独1位比特(bit)来储存符号,它是借助一个中间值,来实现正负数的表达。

中间值:11位比特(bit)Exponent储存的是E,因此E的取值范围理论上是 [0,2047]    2^11-1=2047  中间值=1023(取值范围最中间的那个数

IEEE754规定中间值1023那个位置表示E=0,小于中间值为负数(小多少就是负多少),大于中间值为正数(大多数就是正多少),以后在将通过中间值处理的数转换为二进制,存在Exponent

例如:E=11

有效数M的储存也本身的规则(也是能够储存小数点的缘由)

IEEE 754规定Significant储存的只是M中小数点后的数(存了52位小数点后的二进制数,因此应该存了52位有效数包括开头的1),

由于M的都是以1.开头的(别问我为何,说明你二进制科学计数法,还不是很明白),因此只要在取出Significant储存的二进制数前面加上1.IEEE 754就是用这个方法绕开储存如何小数点问题的)。


 

讲了这么多,可是仍是没讲到痛点啊!

0.1连续的相加10次为何不是1,而是0.999…

走过路过不要错过啊,止痛良药来了。

咱们就用上述的IEEE754的标准把十进制0.1储存进计算机内存中

 

                                                                                                                                                                                                            0011(0舍1入)

                     S=0     E=1111 1110 11   Significant中存的是 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010

         64位比特(bit)存: 0  1111 1110 11  1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010

提取内存中刚存进去的0.1的二进制表达式

        Significant中存的是 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010  前面添加 0.0001就是提取出来的二进制0.1(由于E=-4,且IEEE 754帮你存着1.)

         因此从内存提取出来的0.1的二进制是 0001 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010
下面是计算二进制小数转十进制小数的脚本  

import re
a=re.sub(' ','','00011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010')
# 去空格
n=0
sum=0
for i in a :
    n-=1
    sum+=int(i)*2**(n)
print( "%.17f" %sum)  # 格式化
# 输出结果
# 0.10000000000000001

 

最后提一个问题,E转换为二进制时,为何要有中间值的方式,这样更帅?

对哦,中间值的转换为二进制恰好为11个1,1111 1111 111哦。。。。

欢迎评论,番茄,鸡蛋都砸过来吧!!!

 

想看Python其余数据类型知识就点我

相关文章
相关标签/搜索