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
pc10201
V2EX  ›  Python

python 四舍五入问题

  •  
  •   pc10201 · 2014-10-23 11:03:05 +08:00 · 6995 次点击
    这是一个创建于 3463 天前的主题,其中的信息可能已经有所发展或是发生改变。
    from decimal import Decimal
    a='238.345'

    length=len(a[a.find('.'):])
    if length>=3:
    x='{:.2f}'.format(Decimal(a))
    print x

    结果是238.34
    为什么不是238.35呢?
    是最新的python 2.7 32位
    22 条回复    2014-10-24 11:14:14 +08:00
    wh1100717
        1
    wh1100717  
       2014-10-23 11:14:40 +08:00
    {:.2f}只是精度操作,表示的是保留小数点后两位,但不是四舍五入操作。
    hahastudio
        2
    hahastudio  
       2014-10-23 11:24:50 +08:00   ❤️ 2
    这是因为 decimal 的默认 context 是“四舍六入五留双”,rounding=ROUND_HALF_EVEN

    >>> from decimal import *
    >>> getcontext()
    Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[DivisionByZero, InvalidOperation, Overflow])

    你可以试试 a = '238.355',你最后会获得 238.36

    -你学过大学物理肯定清楚这个修约方式-
    davidli
        3
    davidli  
       2014-10-23 11:26:25 +08:00
    @wh1100717

    其实是因为python里无法精确表示一个float number.
    https://docs.python.org/2/tutorial/floatingpoint.html
    Decimal(234.345) 是234.3449999999
    sriuu
        4
    sriuu  
       2014-10-23 11:34:15 +08:00
    @davidli Decimal是十进制保存的 当字符串读入时就是234.345
    https://docs.python.org/3/library/decimal.html
    hahastudio
        5
    hahastudio  
       2014-10-23 11:35:37 +08:00
    @davidli 已然用了 decimal,就不是这个问题了
    你可以试试
    getcontext().rounding = ROUND_HALF_UP
    之后,a = '238.345' 就会得到 238.35
    wh1100717
        6
    wh1100717  
       2014-10-23 11:37:30 +08:00
    @davidli soga~ thx
    hahastudio
        7
    hahastudio  
       2014-10-23 11:38:51 +08:00   ❤️ 1
    关于修约的扩展阅读

    GB/T8170-2008 数值修约规则与极限数值的表示和判定
    http://www.cws.net.cn/xiazai/yuanxf20112149435658.pdf
    Numerical Rounding
    https://en.wikipedia.org/wiki/Rounding
    pc10201
        8
    pc10201  
    OP
       2014-10-23 11:43:46 +08:00
    @hahastudio 好像现在可以了

    from decimal import *
    getcontext().rounding = ROUND_HALF_UP
    a='238.345'
    length=len(a[a.find('.'):])
    if length>=3:
    x='{:.2f}'.format(Decimal(a))
    print x
    davidli
        9
    davidli  
       2014-10-23 11:44:05 +08:00
    @wh1100717
    楼上两位才是正解, 别被我误导了.
    Delbert
        10
    Delbert  
       2014-10-23 11:45:02 +08:00
    Python不是round到最近的偶数吗?
    stillzhl
        11
    stillzhl  
       2014-10-23 11:49:37 +08:00
    In [2]: from decimal import Decimal

    In [3]: def round(x):
    ...: return '{:.2f}'.format(Decimal(x))
    ...:

    In [4]: a = '238.345'

    In [5]: round(a)
    Out[5]: '238.34'

    In [6]: b = '238.346'

    In [7]: round(b)
    Out[7]: '238.35'

    In [8]: c = '238.355'

    In [9]: round(c)
    Out[9]: '238.36'

    In [10]: d = '238.356'

    In [11]: round(d)
    Out[11]: '238.36'

    这个问题并不像@wh1100717说的那样,{:.2f}确实对浮点数进行了四舍五入(round)。
    具体请查看IEEE的float point rounding rules: http://en.wikipedia.org/wiki/IEEE_floating_point#Rounding_rules

    至于为什么会这样我也还没有太弄明白,但是会进一步研究,等有了结果会再来回复。
    hahastudio
        12
    hahastudio  
       2014-10-23 11:52:46 +08:00
    @pc10201 对啊,因为你替换了修约规则,变成了“四舍五入”了
    -但四舍六入五留双才是国家标准啊-
    wodemyworld
        13
    wodemyworld  
       2014-10-23 11:52:51 +08:00
    有单独的处理舍入的库,你可以找找
    stillzhl
        14
    stillzhl  
       2014-10-23 11:55:28 +08:00
    我在写回复的时候并没有看到@hahastudio在2楼的回复

    请问@hahastudio , 为什么要 “四舍六入五留双”,具体的底层的考虑是什么?
    wh1100717
        15
    wh1100717  
       2014-10-23 11:58:32 +08:00
    @davidli
    @hahastudio
    @pc10201

    刚才看了一下decimal的定义,实际情况如下:

    from decimal import *
    b = Decimal('238.345')
    getcontext()

    返回的值为Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[DivisionByZero, Overflow, InvalidOperation])

    其中rounding为ROUND_HALF_EVEN.

    而ROUND_HALF_EVEN的描述为:Behave as for ROUND_HALF_UP if the digit to the left of the discarded fraction is odd; behave as for ROUND_HALF_DOWN if it's even. (Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case, rounds towards the even neighbor.)

    简单说就是如果为238.345 则会使用ROUND_HALF_DOWN,如果是238.355 则会使用ROUND_HALF_UP。

    测试代码如下:

    '{:.2f}'.format(Decimal('238.345')) //返回的是 238.34
    '{:.2f}'.format(Decimal('238.355')) //返回的是 238.36

    如有错误,请勘正~~
    wh1100717
        16
    wh1100717  
       2014-10-23 12:00:02 +08:00
    @stillzhl 开始确实没太仔细分析... 误导大家了 抱歉 哈哈
    stillzhl
        17
    stillzhl  
       2014-10-23 12:02:17 +08:00
    @wh1100717 没关系,我想知道的是为什么会有这样的规则,而且这时IEEE官方定义的,应该会比较严谨。
    hahastudio
        18
    hahastudio  
       2014-10-23 12:06:07 +08:00
    @stillzhl -因为是标准-

    “四舍五入”会造成结果偏高, “四舍六入五留双”能在一定程度上减少因为修约产生的偏差
    andychen20121130
        19
    andychen20121130  
       2014-10-23 13:49:31 +08:00
    a=a+0.5 保留一个小数。a=a+0.05保留两位小数,你试试
    sivacohan
        20
    sivacohan  
       2014-10-23 14:00:51 +08:00
    @stillzhl

    最开始接触到四舍五入5留双是在物理学里面。
    四舍五入的规约法则如下
    0 不需要舍入
    [1 ~ 4] = 0
    [5 ~ 9] = 10

    在上述情况下, 0 到 9 的舍入情况就对应于
    [0, -1, -2 , -3, -4, +5, +4, +3, +2, +1]
    造成的结果就是每10个数字做舍入操作,总和就增加了5

    为了解决这个问题,我们把上面的列表变成
    [0, -1, -2 , -3, -4, +/-5, +4, +3, +2, +1]
    这样的算总和的结果更接近真实值。

    而+/-5 前面的符号,就有前面的数字是奇数还是偶数确定。
    est
        21
    est  
       2014-10-23 14:11:56 +08:00
    @davidli 准确的说不是python无法处理浮点,而是大多数程序的浮点计算,底层都是调用CPU芯片的IEEE 754指令。你要自己撸一个超高精度的软浮点库也行,用纯python的都可以,只是享受不到硬件加速了。
    stillzhl
        22
    stillzhl  
       2014-10-24 11:14:14 +08:00
    这篇论文应该解释了这个问题:
    http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3078 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:51 · PVG 22:51 · LAX 07:51 · JFK 10:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.