浮点数的表示
import struct
while 1:
try:a=float(input("输入一个数,他会输出对应的浮点数结构,输入非数类退出:"))
except:break
else:
print(''.join(bin(h).replace('0b','').rjust(8,'0') for h in struct.pack('!f',a)))
我看到大大们的文章,喜欢用自定义方法来对某些函数加深认识,但是为什么浮点运算解释都那么模糊呢?
我看懂浮点运算的存储结构的计算方法了,今天晚了,明天码出来个手写的转浮点二进制的存储结构。
现在就先说说这些二进制是怎么来的
0 01111111 00000000000000000000000
正负标示 指数 基数
As was said, 5.2 is represented as a sign bit, an exponent and a mantissa. How do you encode 5.2?
符号位 指数 尾数
正负标示:0为正 1为负
指数:01111111:127即0,这样8位二进制对应的0~255就变成了-127~128
但:
(1) 当P = 0, M = 0时,表示0。(-127)
(2) 当P = 255, M = 0时,表示无穷大,用符号位来确定是正无穷大还是负无穷大。
(3) 当P = 255, M != 0时,表示NaN(Not a Number,不是一个数)。
指数E
①E不全为0,不全为1:
这时就用正常的计算规则,E的真实值就是E的字面值减去127(中间值),M的值要加上最前面的省去的1。
②E全为0
这时指数E等于1-127为真实值,M不在加上舍去的1,而是还原为0.xxxxxxxx小数。这样为了表示0,和一些很小的整数。
所以在进行浮点数与0的比较时,要注意。
③E全为1
当M全为0时,表示±无穷大(取决于符号位);当M不全为1时,表示这数不是一个数(NaN)
if num==0:
print("00000000000000000000000000000000")
continue
if num=="nan":
print("01111111110000000000000000000000")
continue
if num=="inf":
print("01111111100000000000000000000000")
continue
if num=="-inf":
print("11111111100000000000000000000000")
continue
以上是我正在码的一段,判断输入的是什么特殊的浮点数
因为有这几个定义,所以有小范围就缩小了些。这里不再深论了!
关键!关键!关键!
这是二进制,所以指数和基数都是用二进制换算出来
我们都熟悉了8421的数列,即20、21、22、23……
但浮点运算,用的是2-1、2-2、2-3、2-4……
对应的十进制就是0.5、0.25、0.125、0.0625……
看到的一个例子:0.0456他的二进制表示方法
浮点数的内存结构
比如0.0456,我们需要把他规格化,变为1.xxxx * (2 ^ n )的型式,要求得纯小数X对应的n可用下面的公式:
n = int( 1 + log (2)X );
0.0456我们可以表示为1.4592乘以以2为底的-5次方的幂,即1.4592 * ( 2 ^ -5 )。
我晕,他的文中是有长串的进制换算的,可惜我略过了,我硬生生的拼出来了换算方法。
0.0456要转变成1.xxx,他就需要乘以一个2的幂次方,1/0.0456=21.92982456140351,就近的2的幂次方就是32,即25,所以0.045632/32=1.45922-5
这样指数就有了,-5+127=122:1111010
而继续求算基数1.4592,十进制的1就是二进制0位上的1。二进制前后他都是1,所以忽略。只需要求算0.4592的二进制。
但这个二进制是从2的-1次方、-2、-3……
让我们先回头看看整数十进制转二进制怎么算的,例如55,首先我们不考虑高位,从低位算起。
1 54 2 52 4 48 8 40 16 24 余24 把这个余数往回看,加到某个数上,24-16=8,对应的16位上+1,对应的8位上+1
bin:111011 逆序 110111
话说当8+1的时候,先不考虑16是否+1,先把8的1加上去,这样就像是多米诺骨牌一下子累积到int:32上,之后再加16位上的1。
那回看现在的0.4592,她也是一个个的被2的次方消磨掉
0.5 0
0.25 1 0.2092
0.125 1 0.0842
0.0625 1 0.0217
0.03125 0
0.015625 1 0.006075
0.0078125 0
0.00390625 1 0.00216875
0.001953125 1 0.000215625
0.0009765625 0
0.00048828125 0
0.000244140625 0
0.0001220703125 1 0.0000935546875
0.00006103515625 1 0.00003251953125
0.000030517578125 1 0.000002001953125
0.0000152587890625 0
7.62939453125e-6 0
3.814697265625e-6 0
1.9073486328125e-6 1 0.0000000946044922 这里开始不精确了
9.5367431640625e-7 0
4.76837158203125e-7 0
2.384185791015625e-7 0
1.192092895507813e-7 0 这是最后一位 但可能是二进制的舍入,这里的结果是1
0.5960464477539063e-7 1 0.00000003499984742460937 这里超纲了
01110101100011100010001
以上都是笔算,windows的计算器程序员模式不支持浮点运算
那我们就把系统算的结果一直*2,看看结果:0 01111010 01110101100011100010001
01110101100011100010001
3852049,这是对应的十进制,怎么算呢?除以2循环直到小于1:0.4592000246047974
使用unpack得到:
01110101100011100010001 0.04560000076889992
01110101100011100010000 0.04559999704360962
以上是正好算到最后一个1就为0
In [13]: 2**-24
Out[13]: 5.960464477539063e-08
换成自然语言就是0.00000005960464477539063,把他再缩一缩加到0.04559999704360962
0.04560000704360962
总觉得哪里不对劲
0.04560000076889992-0.04559999704360962=
0.0000000037252903x windows计算器不能计入到最后一位
所以最后一个二进制1,可以理解为0.0000000037252903,我们把他的1/4加到0.04559999704360962,看看显示效果
0.0000000037252903/4:0.000000000931322575
0.0455999979749322
In [21]: struct.pack("!f",0.0455999979749322)
Out[21]: b'=:\xc7\x10'
看来舍入方法就是显示的最后一位是0,之后无法显示的那位是1,那么进位。
看得到精度是有问题的。
而以前我以为windows的计算器很diao,因为某软件需要小数点后13位,excel不支持,python,群友说的,那时我还没想再学这些东西。不过那时算出的精度也是有问题的!嘛~