python 四舍五入问题

2014-10-23 11:03:05 +08:00
 pc10201
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位
7003 次点击
所在节点    Python
22 条回复
wh1100717
2014-10-23 11:14:40 +08:00
{:.2f}只是精度操作,表示的是保留小数点后两位,但不是四舍五入操作。
hahastudio
2014-10-23 11:24:50 +08:00
这是因为 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
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
2014-10-23 11:34:15 +08:00
@davidli Decimal是十进制保存的 当字符串读入时就是234.345
https://docs.python.org/3/library/decimal.html
hahastudio
2014-10-23 11:35:37 +08:00
@davidli 已然用了 decimal,就不是这个问题了
你可以试试
getcontext().rounding = ROUND_HALF_UP
之后,a = '238.345' 就会得到 238.35
wh1100717
2014-10-23 11:37:30 +08:00
@davidli soga~ thx
hahastudio
2014-10-23 11:38:51 +08:00
关于修约的扩展阅读

GB/T8170-2008 数值修约规则与极限数值的表示和判定
http://www.cws.net.cn/xiazai/yuanxf20112149435658.pdf
Numerical Rounding
https://en.wikipedia.org/wiki/Rounding
pc10201
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
2014-10-23 11:44:05 +08:00
@wh1100717
楼上两位才是正解, 别被我误导了.
Delbert
2014-10-23 11:45:02 +08:00
Python不是round到最近的偶数吗?
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
2014-10-23 11:52:46 +08:00
@pc10201 对啊,因为你替换了修约规则,变成了“四舍五入”了
-但四舍六入五留双才是国家标准啊-
wodemyworld
2014-10-23 11:52:51 +08:00
有单独的处理舍入的库,你可以找找
stillzhl
2014-10-23 11:55:28 +08:00
我在写回复的时候并没有看到@hahastudio在2楼的回复

请问@hahastudio , 为什么要 “四舍六入五留双”,具体的底层的考虑是什么?
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
2014-10-23 12:00:02 +08:00
@stillzhl 开始确实没太仔细分析... 误导大家了 抱歉 哈哈
stillzhl
2014-10-23 12:02:17 +08:00
@wh1100717 没关系,我想知道的是为什么会有这样的规则,而且这时IEEE官方定义的,应该会比较严谨。
hahastudio
2014-10-23 12:06:07 +08:00
@stillzhl -因为是标准-

“四舍五入”会造成结果偏高, “四舍六入五留双”能在一定程度上减少因为修约产生的偏差
andychen20121130
2014-10-23 13:49:31 +08:00
a=a+0.5 保留一个小数。a=a+0.05保留两位小数,你试试
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 前面的符号,就有前面的数字是奇数还是偶数确定。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/140941

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX