V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
hard2reg
V2EX  ›  Python

发现 Python 的一个坑。。。

  •  
  •   hard2reg · 2016-06-05 19:54:58 +08:00 · 9192 次点击
    这是一个创建于 3091 天前的主题,其中的信息可能已经有所发展或是发生改变。

    不知道是不是所有语言都是这样的。。

    #我用的是Python3
    a = 1 / 3
    # 这时候 a 打印出来应该是 0.333333333
    b = 1 / a
    # 这时候打印 b 居然输出的是 3
    

    这说明 Python 在内部是以分数的形式储存无限循环小数的?

    还是我孤陋寡闻了。。。。

    60 条回复    2016-06-06 20:07:42 +08:00
    clino
        1
    clino  
       2016-06-05 19:58:20 +08:00
    明明 1/3 是 0
    >>> print 1/3
    0
    hard2reg
        2
    hard2reg  
    OP
       2016-06-05 20:00:20 +08:00
    @clino 我用的是 Python3
    Yc1992
        3
    Yc1992  
       2016-06-05 20:00:44 +08:00
    明明 1/3 = 0
    b = 1/0 = ∞
    hard2reg
        4
    hard2reg  
    OP
       2016-06-05 20:02:02 +08:00
    @Yc1992 我 Python3 。。。。
    publicID002
        5
    publicID002  
       2016-06-05 20:02:18 +08:00 via Android
    应该是输出的时候位数默认不多有舍入
    wsy2220
        6
    wsy2220  
       2016-06-05 20:04:41 +08:00
    浮点计算不保证结果精确的,换一个数甚至换一个机器结果就不一样了
    Kisesy
        7
    Kisesy  
       2016-06-05 20:08:03 +08:00
    py
    0.3333333333333333 3.0
    go
    0.3333333333333333 3
    shuax
        8
    shuax  
       2016-06-05 20:08:48 +08:00   ❤️ 1
    print(2.999999999999999999999999999)
    billlee
        9
    billlee  
       2016-06-05 20:26:40 +08:00
    不要依赖未定义行为
    clino
        10
    clino  
       2016-06-05 20:32:10 +08:00
    py3 的表现也合理啊
    >>> print(1/3)
    0.3333333333333333
    >>> print(1/(1/3))
    3.0
    >>> print(1/0.3333333333333333)
    3.0
    justjavac
        11
    justjavac  
       2016-06-05 20:34:10 +08:00 via Android
    是你孤陋寡闻了。
    浮点数的内部表示和浮点数的显示是两码事
    justjavac
        12
    justjavac  
       2016-06-05 20:36:19 +08:00 via Android   ❤️ 2
    pimin
        13
    pimin  
       2016-06-05 20:40:41 +08:00 via Android
    我发现数学的一个坑
    1/3=0.3333333...
    0.33333*3<1
    3*1/3=1
    ipconfiger
        14
    ipconfiger  
       2016-06-05 20:44:07 +08:00
    楼举不是计算机科班出身的吧, 先搞清楚二进制怎么存储浮点数就清楚了,如果我记得没错应该是在计算机组成原理的课本里有
    hadoop
        15
    hadoop  
       2016-06-05 20:46:30 +08:00
    @pimin 0.3333.....≠ 0.33333
    hard2reg
        16
    hard2reg  
    OP
       2016-06-05 20:48:00 +08:00
    @justjavac 大神啊,要出版了
    hard2reg
        17
    hard2reg  
    OP
       2016-06-05 20:49:02 +08:00
    @pimin 我没有任何这个意思。。。
    fy
        18
    fy  
       2016-06-05 20:55:45 +08:00
    日常又发现一个坑。。。
    hard2reg
        19
    hard2reg  
    OP
       2016-06-05 21:03:36 +08:00
    @justjavac 貌似 JS 和 Python 都会出现 0.2 + 0.4 ≠ 0.6 的情况,但是我用 C++试了下输出的是 0.6 。请问这是为啥。。

    float a;
    a = 0.2 + 0.4;
    cout << a << endl;

    如果想要让 JS 和 Python 正确输出 0.6 改如何写。。
    jimmyye
        20
    jimmyye  
       2016-06-05 21:13:13 +08:00   ❤️ 1
    要用 decimal 之类的库
    SuperMild
        21
    SuperMild  
       2016-06-05 21:15:31 +08:00 via iPad
    楼主你看的是哪本入门教材,书中肯定有说到这个的,就在说基本类型的数字类型那章
    xuwenmang
        22
    xuwenmang  
       2016-06-05 21:31:37 +08:00
    @hard2reg C++你试试 1000000000000.2+1000000000000.2 还准不。
    ericls
        23
    ericls  
       2016-06-05 21:36:14 +08:00 via iPhone
    @hard2reg 输出和结果不一样很正常
    realpg
        24
    realpg  
       2016-06-05 22:05:44 +08:00   ❤️ 7
    mornlight
        25
    mornlight  
       2016-06-05 22:14:37 +08:00 via iPhone
    @realpg 你这输入法做多了 PY 交易
    crazykuma
        26
    crazykuma  
       2016-06-05 22:23:22 +08:00
    666 楼主这是没仔细看入门教程吧
    随便一本入门里都有啊
    justjavac
        27
    justjavac  
       2016-06-05 22:40:39 +08:00 via Android
    @hard2reg 输出 0.6 才是不正常的。 C++默认的浮点数输出有效位数为 6 ,即 setprecision(6)。
    justjavac
        28
    justjavac  
       2016-06-05 22:45:48 +08:00 via Android
    @hard2reg 如果想要精确计算,使用 Decimal 之类的库。

    但是要注意,初始化的时候不要使用

    d = decimal(0.2)

    而是用

    d = decimal("0.2")

    想想为什么?
    OnTheRoad
        29
    OnTheRoad  
       2016-06-05 22:53:46 +08:00
    除法不可避免的会产生误差。
    当误差处于精度下限时,会做近似处理。
    如:
    <code>
    print(0.3333333333333333 * 3)
    # Output: 1.0
    </code>
    bdbai
        30
    bdbai  
       2016-06-05 23:03:16 +08:00 via Android
    @justjavac 我猜一下,前者表达式的精度已经损失了。对吗?
    bramblex
        31
    bramblex  
       2016-06-05 23:30:15 +08:00
    @justjavac

    输出 0.6 很正常, C++ 编译器的优化会把一些没什么意义的数值提前算出来,根本就轮不到运算……
    bramblex
        32
    bramblex  
       2016-06-05 23:36:18 +08:00
    @hard2reg

    输出 0.6 太正常不过了

    a = 0.2 + 0.4 这行代码已经被 C++ 编译器编译优化成了 a = 0.6 了。所以在程序运行的时候根本就不存在 0.2 + 0.4 这个步骤,所以也就没有什么丢失精度的问题了。
    clino
        33
    clino  
       2016-06-05 23:43:17 +08:00 via Android
    0.2 0.4 在 10 进制世界里是有理数 在二进制的世界里是无理数
    所以二进制里无法用有限位数表达出 0.2 所以实际上计算机里没有对的 0.2 和 0.4 所以结果当然不一样 只是近似到误差非常小而已
    winterbells
        34
    winterbells  
       2016-06-05 23:51:00 +08:00 via Android
    @pimin 其实 0.33333 3 循环乘以 3 等于 1 。。。
    chairuosen
        35
    chairuosen  
       2016-06-05 23:57:42 +08:00
    还是我大 JS 屌, 1/0.3 = 3.3333333333333335
    ADMlN
        36
    ADMlN  
       2016-06-06 01:30:39 +08:00 via Android
    'http://'+str(.1+.2)+'.com'
    mikegreen7892003
        37
    mikegreen7892003  
       2016-06-06 01:37:18 +08:00
    https://en.wikipedia.org/wiki/IEEE_floating_point

    楼主可以看看文档。浮点数在计算机内一般以二进制来存储。
    浮点数的话,显示的结果和存储的内容不一定完全一致。只能说非常近似。
    veficos
        38
    veficos  
       2016-06-06 01:39:55 +08:00
    1 除以 0.3 是 3.3333 没错啊。。。。。。

    1 / 0.3 == 10 / 3

    这是数学问题好吧.....
    veficos
        39
    veficos  
       2016-06-06 01:40:36 +08:00
    换成分数去算就知道为什么了....
    veficos
        40
    veficos  
       2016-06-06 01:46:45 +08:00 via Android
    是我看错题义了。。。
    introom
        41
    introom  
       2016-06-06 01:59:56 +08:00 via Android
    好吧,我也不懂。
    是因为 0.333...被精确表示了么?别坑我, ieee745 没到无限循环小数那个份上啊。

    那最后为什么会变成 3.0 ,而不是 2.9999999 呢?

    要么是输出的时候四舍五入了,要么是 intel 的 fp 指令搞得鬼。

    我更相信前者。
    justjavac
        42
    justjavac  
       2016-06-06 08:03:08 +08:00 via Android
    @bramblex 编译器提前算的时候不是使用的浮点数?或者说编译器使用了其他当时计算的 0.2+0.4 的值
    tkisme
        43
    tkisme  
       2016-06-06 09:36:28 +08:00
    a=1/3.0
    b=1/a
    coddmajes
        44
    coddmajes  
       2016-06-06 09:36:54 +08:00
    最后解释一下整数的除法为什么也是精确的。在 Python 中,有两种除法,一种除法是 /:

    >>> 10 / 3
    3.3333333333333335
    /除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数:

    >>> 9 / 3
    3.0
    还有一种除法是 //,称为地板除,两个整数的除法仍然是整数:

    >>> 10 // 3
    3
    你没有看错,整数的地板除 //永远是整数,即使除不尽。要做精确的除法,使用 /就可以。

    因为 //除法只取结果的整数部分,所以 Python 还提供一个余数运算,可以得到两个整数相除的余数:

    >>> 10 % 3
    1
    无论整数做 //除法还是取余数,结果永远是整数,所以,整数运算结果永远是精确的。

    来自廖雪峰的网站 http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431658624177ea4f8fcb06bc4d0e8aab2fd7aa65dd95000
    Clarencep
        45
    Clarencep  
       2016-06-06 09:48:49 +08:00
    浮点数当然是有误差的, LZ 可以试试这样子:


    ```

    >>> '%.50lf' % (0.3)
    '0.29999999999999998889776975374843459576368331909180'
    >>> '%.50lf' % (1/3)
    '0.33333333333333331482961625624739099293947219848633'


    >>> '%.50lf' % (0.33333333333333333)
    '0.33333333333333331482961625624739099293947219848633'
    >>> '%.50lf' % (1/0.33333333333333333)
    '3.00000000000000000000000000000000000000000000000000'

    >>> '%.50lf' % (0.3333333333333333)
    '0.33333333333333331482961625624739099293947219848633'
    >>> '%.50lf' % (1/0.333333333333333)
    '3.00000000000000310862446895043831318616867065429688'

    ```
    Arnie97
        46
    Arnie97  
       2016-06-06 09:51:56 +08:00 via Android
    @clino 是分数,不是无理数。无理数是无限不循环小数。
    clino
        47
    clino  
       2016-06-06 09:53:52 +08:00
    @Arnie97 好吧 概念模糊 数学都还给体育老师了...
    magiclx
        48
    magiclx  
       2016-06-06 09:54:40 +08:00
    奇怪的是大家不知道数学上: 0.333...*3 = 1 ?
    Arnie97
        49
    Arnie97  
       2016-06-06 09:55:23 +08:00 via Android
    Haskell 大法好,有原生的分数类型。
    misaka19000
        50
    misaka19000  
       2016-06-06 10:16:00 +08:00
    @realpg ...
    isno
        51
    isno  
       2016-06-06 10:31:23 +08:00
    @justjavac 博客质量不错啊
    BlackKey
        52
    BlackKey  
       2016-06-06 11:29:35 +08:00 via Android
    LZ 请去自行了解 IEEE754
    menc
        53
    menc  
       2016-06-06 11:45:32 +08:00
    @justjavac 你的博客写得真麻烦,不如直接看 IEEE754
    justjavac
        54
    justjavac  
       2016-06-06 12:33:27 +08:00
    @menc IEEE754 是专业,我是业余
    hard2reg
        55
    hard2reg  
    OP
       2016-06-06 13:41:27 +08:00
    @bramblex soga~ thx
    dixyes
        56
    dixyes  
       2016-06-06 13:48:17 +08:00 via Android
    还以为要讨论.3 循环乘 3 等于一的问题(同时因为.3 循环乘三等于.9 循环所以.9 循环等于一
    newton108
        57
    newton108  
       2016-06-06 15:37:06 +08:00
    有几位是高中生吧?
    浮点数运算和 0.99...=1 有任何关系么?
    aREMbosAl
        58
    aREMbosAl  
       2016-06-06 17:21:39 +08:00
    分数在内部存储的时候好像是精确的,显示出来是近似的。。
    mauve
        59
    mauve  
       2016-06-06 19:51:39 +08:00
    有什么不对吗?
    ivechan
        60
    ivechan  
       2016-06-06 20:07:42 +08:00
    @pimin 0.33333333~~~~~=1/3,前提是 3 是无穷
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4830 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 09:43 · PVG 17:43 · LAX 01:43 · JFK 04:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.