由numpy.arange函数看双精度浮点数的精度问题
1.发现问题
想得到一个长度为57的、从0开始的、间隔为0.01的数组,想当然地如下coding,结果意料之外:
>>> import numpy as np
>>> t1 = np.arange(0, 0.01*57, 0.01)
>>> t1.shape
(58L,)
但是如果用同样的方法,可以得到正确的长度为58的、从0开始的、间隔为0.01的数组,结果如下:
>>> t2 = np.arange(0, 0.01*58, 0.01)
>>> t2.shape
(58L,)
到底是什么出了问题?
2.浮点数的精度问题
查找了一些资料,比较乱,通过总结和整理,列出一些关键内容,以便理解为什么会出现上述问题。
(1)在计算机中用二进制表示小数
python中的浮点型(float)类似于C语言中的double类型(我觉得其实就是一样的,毕竟标准的python是用C写的),是双精度浮点型,可以用直接的十进制或科学计数法表示。每个双精度浮点数占8个字节(64位),完全遵守IEEE754号规范(52M/11E/1S),即其中的52位用于表示底数,11位用于表示指数,剩下一个位表示符号。看上去相当完美,但是实际精度依赖于机器架构和创建python解释器的编译器。
我们将0.1表示为二进制的形式,如下计算,可以看到0.1在十进制下明明是一个有限小数,在二进制下表示却是无限循环的(实际上0.0~0.9这十个小数中只有0.0和0.5在二进制形式表示下是有限的):
根据IEEE754标准,有效位数只能表示52位,在python下,0.1其实是这样表示的(python没有提供
.bin
函数,只有.hex
函数):>>> (0.1).hex() '0x1.999999999999ap-4'
0x
表示十六进制,p-4
表示小数点要向左移动4位;IEEE754标准中的52位在这里是不包括小数点前面的那个1的,所以将'0x1.999999999999ap-4'
展开,结果如下:# 原表示:0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 ···# IEEE754标准下的表示:0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010
- 可以看到,由于计算机存储的结构限制,在IEEE754标准下,本该无限循环的0.1被“1进”了(“0舍1进”,类似于十进制中的“四舍五入”)
(2)IEEE754标准下浮点数精度带来的问题
在涉及精度计算的过程中产生问题
python
>>> print '%.20f'%(0.1) 0.10000000000000000555 >>> print '%.20f'%(0.1+0.1) 0.20000000000000001110 >>> print '%.20f'%(0.2) 0.20000000000000001110 >>> print '%.20f'%(0.1+0.2) 0.30000000000000004441 >>> print '%.20f'%(0.1*3) 0.30000000000000004441 >>> print '%.20f'%(0.3) 0.29999999999999998890
C++
D:\develop λ vi test.cpp#include<iostream>#include<iomanip>using namespace std; int main() {double a=0.1,b=0.2,c=0.3;int d=3;cout<<"0.1 = "<<setprecision(20)<<a<<endl;cout<<"0.1+0.1 = "<<setprecision(20)<<a+a<<endl;cout<<"0.2 = "<<setprecision(20)<<b<<endl;cout<<"0.1+0.2 = "<<setprecision(20)<<a+b<<endl;cout<<"0.1*3 = "<<setprecision(20)<<a*d<<endl;cout<<"0.3 = "<<setprecision(20)<<c<<endl; }:wqD:\develop λ g++ test.cpp -o test.exe && .\test.exe 0.1 = 0.10000000000000000555 0.1+0.1 = 0.2000000000000000111 0.2 = 0.2000000000000000111 0.1+0.2 = 0.30000000000000004441 0.1*3 = 0.30000000000000004441 0.3 = 0.2999999999999999889
可以看到,C++和python的情况是一致的。
在编程语言上的0.3实际是比数学上的0.3要小一点点的;而0.1+0.2或0.1*3运算,在编程语言上的误差会跟着进行运算,运算结果会积累误差造成其要比数学上的0.3要大一点点。
解析numpy.arange使用浮点数的问题
根据以上的分析,就能解释使用numpy.arange所出现的现象了,我们可以验证一下:>>> import numpy as np >>> np.arange(0, 0.1*3, 0.1) array([ 0. , 0.1, 0.2, 0.3]) >>> np.arange(0, 0.3, 0.1) array([ 0. , 0.1, 0.2]) >>> >>> print '%.20f'%(0.57) 0.56999999999999995115 >>> print '%.20f'%(0.01*57) 0.57000000000000006217 >>> print '%.20f'%(0.01*58) 0.57999999999999996003
- 也就是说,第二个表示终值的参数如果使用0.1*3,其实是使用了一个稍微比0.3大一点点的数,那么根据numpy.arange的使用规则(产生的数组是不包含终值的),就会包含到0.3(但实际上是比0.3小一点点的数)。而直接使用0.3作为终值,由于其实际上是比0.3小一点点的数,因此得到的数组就不会包含0.3。
- 同理,0.01*57是比0.57稍微大一点点的数,所以产生的数组自然就包含了0.57(但实际上是比0.57小一点点的数),就得不到期望的数组了
不过讲道理,后来我才发现人家写numpy.arange的时候已经说了如果是用非整数作为step,最好使用numpy.linspace(不过似乎我遇到的是stop的问题)
>>> help(np.arange) ... arange(...) arange([start,] stop[, step,], dtype=None) ... When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use ``linspace`` for these cases.
3.解决方案
(1)解决numpy.arange问题的方案
尽量避免使用arange!!可以使用xrange:
>>> import numpy as np
>>> t1 = np.array([x*0.01 for x in xrange(57)])
>>> t1.shape
(57L,)
>>> t2 = np.array([x*0.01 for x in xrange(58)])
>>> t2.shape
(58L,)
(2)解决十进制浮点数精度问题的方案:使用decimal
使用decimal模块中的Decimal类:
>>> from decimal import Decimal
>>> d1 = Decimal(.1)
>>> d2 = Decimal(".1")
>>> d3 = Decimal("1.0")
>>> d1
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> d2
Decimal('0.1')
>>> d3
Decimal('1.0')
>>> d1+1.0
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'Decimal' and 'float'
>>> d1+d3
Decimal('1.100000000000000005551115123')
>>> d2+d3
Decimal('1.1')
>>> print d2+d3
1.1
参考书: 《Python核心编程(第二版)》,人民邮电出版社
python版本: 2.7.14 (64bit)
由numpy.arange函数看双精度浮点数的精度问题相关推荐
- numpy.arange()函数解析
numpy包中的使用arange函数创建数值范围并返回 ndarray 对象,函数格式如下: numpy.arange(start, stop, step, dtype) 根据 start 与stop ...
- python 中arange函数_Python numpy.arange函数方法的使用
numpy.arange numpy.arange([start, ]stop, [step, ]dtype=None) 返回给定间隔内的均匀间隔的值. 在半开间隔[start,stop)(换句话说, ...
- python numpy.arange() 函数的使用方法 (在给定间隔内返回均匀间隔的值)
numpy.arange(start, stop, step, dtype = None) 在给定间隔内返回均匀间隔的值 参数: start -- 开始位置,数字,可选项,默认起始值为0 stop - ...
- python range函数与numpy arange函数,xrange与range的区别
转自:https://www.cnblogs.com/ymjyqsx/p/6426764.html 1.range()返回的是range object,而np.arange()返回的是numpy.nd ...
- python range函数小数_python range函数与numpy arange函数,xrange与range的区别
转自:https://www.cnblogs.com/ymjyqsx/p/6426764.html 1.range()返回的是range object,而np.arange()返回的是numpy.nd ...
- Python – numpy.arange()
Being a linear sequence generator, the numpy.arange() function is used to generate a sequence of num ...
- 双精度浮点数的取绝对值
背景:做到一题四则运算,除数不为零的题目 发现当除数为0.00000000000000001时,测试点输出为ERROR,而只判除数是否为0的程序输出是100...000. 问题:在于双精度浮点型的数值 ...
- python 精度损失_Python的浮点数损失精度问题
本篇讨论的现象可以从下面这段脚本体现出来: >>> x = 0.0 >>> for i in range(10): x += 0.1 print(x) 0.1 0. ...
- Numpy 中的 arange 函数
1. 概述 Numpy 中 arange() 主要是用于生成数组,具体用法如下: 2. arange() 2.1 语法 numpy.arange(start, stop, step, dtype = ...
最新文章
- 递归删除目录下的所有文件
- 疫情之下,MWC 2020正式取消!此前仍有中国公司表态坚持参展
- win8音频服务器未响应,大神为你详解win8系统右键点击文件提示“未响应”的解决教程...
- linux安装virtualbox命令,在Linux中从命令行查找Virtualbox Version的方法
- 你真的会玩SQL吗?简单的数据修改
- java基础—IO流概述
- VMWare 虚拟机安装及新建虚拟机
- 深度学习之浅谈全连接层
- [学习笔记]后缀平衡树
- springboot实现图片验证码登录
- 酷软正在连接服务器,蜗牛星际:我的B款双网口机箱PVE+爱快+LEDE 双软路由 保姆级教程...
- 安卓4.0后新控件TextureView解决SurfaceView在修改默认屏幕方向后(硬件导致)视频方向无法翻转的问题
- tableau必知必会之学做三个集合的维恩图(文氏图)Venn diagram(二)
- Apostrophe not preceded by \
- Windows AMD 安装 PyTorch
- Android O 版本(Android 8.0) 存储空间不足时提醒
- Nginx官方文档(四十七)【ngx_stream_js_module|ngx_stream_keyval_module|ngx_stream_limit_conn_module】
- 有哪些鲜为人知,但是很有意思的网站?
- IT人员转行写小白文可以么?
- 世界上最远的距离_泰戈尔
热门文章
- hbuilderx安装教程_HBuilderX 的安装使用教程
- 计算机图形学直线算法程序,计算机图形学直线生成算法实现.doc
- 数据可视化如何实现?4大基本流程了解一下
- LightGBM模型简单预测股票涨跌情况
- bilibili手机视频下载目录整理脚本
- Android应用权限大全(Manifest.permission)
- IOS10 无法获取手机传感器
- 基于Java+SpringBoot+Thymeleaf+Mysql新冠疫苗预约系统设计与实现
- Python repr函数——学习笔记
- 捷报|数说故事入选「广州市人工智能应用场景典型案例TOP100」