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函数看双精度浮点数的精度问题相关推荐

  1. numpy.arange()函数解析

    numpy包中的使用arange函数创建数值范围并返回 ndarray 对象,函数格式如下: numpy.arange(start, stop, step, dtype) 根据 start 与stop ...

  2. python 中arange函数_Python numpy.arange函数方法的使用

    numpy.arange numpy.arange([start, ]stop, [step, ]dtype=None) 返回给定间隔内的均匀间隔的值. 在半开间隔[start,stop)(换句话说, ...

  3. python numpy.arange() 函数的使用方法 (在给定间隔内返回均匀间隔的值)

    numpy.arange(start, stop, step, dtype = None) 在给定间隔内返回均匀间隔的值 参数: start -- 开始位置,数字,可选项,默认起始值为0 stop - ...

  4. python range函数与numpy arange函数,xrange与range的区别

    转自:https://www.cnblogs.com/ymjyqsx/p/6426764.html 1.range()返回的是range object,而np.arange()返回的是numpy.nd ...

  5. python range函数小数_python range函数与numpy arange函数,xrange与range的区别

    转自:https://www.cnblogs.com/ymjyqsx/p/6426764.html 1.range()返回的是range object,而np.arange()返回的是numpy.nd ...

  6. Python – numpy.arange()

    Being a linear sequence generator, the numpy.arange() function is used to generate a sequence of num ...

  7. 双精度浮点数的取绝对值

    背景:做到一题四则运算,除数不为零的题目 发现当除数为0.00000000000000001时,测试点输出为ERROR,而只判除数是否为0的程序输出是100...000. 问题:在于双精度浮点型的数值 ...

  8. python 精度损失_Python的浮点数损失精度问题

    本篇讨论的现象可以从下面这段脚本体现出来: >>> x = 0.0 >>> for i in range(10): x += 0.1 print(x) 0.1 0. ...

  9. Numpy 中的 arange 函数

    1. 概述 Numpy 中 arange() 主要是用于生成数组,具体用法如下: 2. arange() 2.1 语法 numpy.arange(start, stop, step, dtype = ...

最新文章

  1. 递归删除目录下的所有文件
  2. 疫情之下,MWC 2020正式取消!此前仍有中国公司表态坚持参展
  3. win8音频服务器未响应,大神为你详解win8系统右键点击文件提示“未响应”的解决教程...
  4. linux安装virtualbox命令,在Linux中从命令行查找Virtualbox Version的方法
  5. 你真的会玩SQL吗?简单的数据修改
  6. java基础—IO流概述
  7. VMWare 虚拟机安装及新建虚拟机
  8. 深度学习之浅谈全连接层
  9. [学习笔记]后缀平衡树
  10. springboot实现图片验证码登录
  11. 酷软正在连接服务器,蜗牛星际:我的B款双网口机箱PVE+爱快+LEDE 双软路由 保姆级教程...
  12. 安卓4.0后新控件TextureView解决SurfaceView在修改默认屏幕方向后(硬件导致)视频方向无法翻转的问题
  13. tableau必知必会之学做三个集合的维恩图(文氏图)Venn diagram(二)
  14. Apostrophe not preceded by \
  15. Windows AMD 安装 PyTorch
  16. Android O 版本(Android 8.0) 存储空间不足时提醒
  17. Nginx官方文档(四十七)【ngx_stream_js_module|ngx_stream_keyval_module|ngx_stream_limit_conn_module】
  18. 有哪些鲜为人知,但是很有意思的网站?
  19. IT人员转行写小白文可以么?
  20. 世界上最远的距离_泰戈尔

热门文章

  1. hbuilderx安装教程_HBuilderX 的安装使用教程
  2. 计算机图形学直线算法程序,计算机图形学直线生成算法实现.doc
  3. 数据可视化如何实现?4大基本流程了解一下
  4. LightGBM模型简单预测股票涨跌情况
  5. bilibili手机视频下载目录整理脚本
  6. Android应用权限大全(Manifest.permission)
  7. IOS10 无法获取手机传感器
  8. 基于Java+SpringBoot+Thymeleaf+Mysql新冠疫苗预约系统设计与实现
  9. Python repr函数——学习笔记
  10. 捷报|数说故事入选「广州市人工智能应用场景典型案例TOP100」