《Python科学计算》的作者
分类: Python/Ruby
2012-02-21 19:59:38
在Python命令行中输入:
可以看到有些浮点数是精确的,而有些则是不精确的,和真实值之间会有一个很小的误差。
Python中的浮点数是所谓的双精度浮点数,它采用64个比特保存一个浮点数。
计算机中的数都是以二进制的形式储存,浮点数也不例外。二进制的小数转换为十进制的小数的方法和整数类似,二进制小数点后的每一位对应的值为,其中n为小数点后的位数。因此10.1011(2)对应的值为:
因为是有限小数,因此任意有限的二进制小数转换为十进制都是有限位的。然而反之则不尽然。下让让我们用程序将十进制小数转换为二进制小数。
为了将十进制小数转换为二进制小数,我们需要一个能精确表示十进制小数的对象。可以使用Python标准库中decimal模块的Decimal对象实现。
我们用Decimal对象表示十进制小数,这样不会产生任何误差。然后循环将此对象乘以2,若结果大于1,则输出1并减去1,否则输出0。通过这种方法可以产生二进制小数上的各个位。
下面用binary_float()输出0.1的二进制小数形式,由于浮点数字面量已经不精确,因此需要用字符串表示十进制浮点数:
从上面的结果可以看出0.1对应的二进制小数是一个无限循环小数。我们可以用下面的程序找到二进制小数的循环节:
程序中通过visited_set集合和visited_list列表保存已经每次运算之后的Decimal对象。如果某个数值重复出现,那么就找到了循环节。❶visited_set是一个集合,因此用来快速判断值是否重复。❷而数值重复的位置,即循环节开始的位置则需要从visited_list列表中去寻找。
通过上面的分析可以看出,许多十进制小数转换成二进制小数变成无限循环小数。而在Python中,浮点数是用64个比特保存的,因此会截去超出的部分,从而造成误差。通过浮点数对象的hex()方法可以查看其二进制形式,例如:
其中,“1.5800000000000”中每个数字都是一个16进制的数,我们把它重写为二进制得到:1.010110000000...,“p+1”部分类似于十进制的科学计数法,它表示小数点要向右移动一位,因此2.6875是使用二进制小数10.1011(2)表示的,这个值是完全精确的。下面再看0.1对应的二进制数:
把它展开为二进制小数,其结果为:
而0.1对应的真正的二进制小数为:
与真正的二进制小数相比,显然内存中所保存的稍大一些,二者之间相差了约:
即在小数点之后第58和59位有一个1,它对应的值为,约等于5.2e-18,四舍五入到第17位上为1。因此会有:
使用SymPy的N(),可以查看浮点数所对应的实际值,下面分别查看0.1和0.12到小数点后30位:
我们看到0.12其实也不精确,但是保留小数点后17位时正好四舍五入为0.12了。