Python3中Decimal与round函数结合使用时的注意事项

from decimal import Decimal

round(Decimal(0.123456789), 3) + Decimal(0.01) # out: Decimal(‘0.1330000000000000002081668171’)

round(Decimal(0.123456789) + Decimal(0.01), 3) # out: Decimal(‘0.133’)

测试环境 python 3.6.5 + ipython

:D


Python3中Decimal与round函数结合使用时的注意事项

20 回复

不是 python 的坑,是所有计算机指令的问题。 你在好好查一下,有专门的文档 decimal round_up 等处理这个问题。


在Python 3里,把Decimalround一起用,最大的坑就是round函数默认的舍入规则(rounding='round_half_even',也叫银行家舍入法)和Decimal自己默认的上下文规则(rounding=ROUND_HALF_EVEN)虽然名字一样,但处理Decimal对象时,round()函数的行为可能和你预期的不太一样,它依赖于当前线程的decimal上下文。

看个例子就明白了:

from decimal import Decimal, getcontext, ROUND_HALF_UP

# 例子1:直接用round函数处理Decimal
num = Decimal('2.675')
print(round(num, 2))  # 输出: 2.67 而不是 2.68

# 例子2:使用Decimal的quantize方法,并明确指定舍入方式
num = Decimal('2.675')
print(num.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))  # 输出: 2.68

为什么会这样? round(Decimal('2.675'), 2)返回2.67是因为2.675在二进制浮点数中无法精确表示(它实际上是2.67499999999999982236431605997495353221893310546875...),当round函数内部将其转换为二进制浮点数时,这个微小的表示误差导致它被视为2.674999...,因此“四舍六入五成双”时,它更靠近2.67

正确做法: 要对Decimal进行精确的四舍五入,永远不要使用内置的round()函数。应该使用Decimal对象自己的quantize()方法,并显式指定你需要的舍入规则(如ROUND_HALF_UP代表经典的“四舍五入”)。

from decimal import Decimal, ROUND_HALF_UP

def round_decimal(value: Decimal, places: int) -> Decimal:
    """对Decimal进行指定小数位的四舍五入"""
    if not isinstance(value, Decimal):
        value = Decimal(str(value)) # 确保输入是Decimal
    quantize_str = '1.' + '0' * places if places > 0 else '1'
    return value.quantize(Decimal(quantize_str), rounding=ROUND_HALF_UP)

# 使用示例
price = Decimal('123.456789')
print(round_decimal(price, 2))  # 输出: 123.46
print(round_decimal(price, 0))  # 输出: 123

简单说,处理Decimal的舍入,认准quantize()方法并明确舍入规则。

>>> round(Decimal(‘0.123456789’), 3) + Decimal(‘0.01’)
Decimal(‘0.133’)

Decimal 初始化时不要传 float 类型,要传字符串,因为传 float 时,float 值本身就有精度丢失问题
>>> Decimal(0.01)
Decimal(‘0.01000000000000000020816681711721685132943093776702880859375’)
>>> Decimal(‘0.01’)
Decimal(‘0.01’)

我试了没有 round 也一样
Decimal(0.123) + Decimal(0.01)

Construct a new Decimal object. ‘value’ can be an integer, string, tuple, or another Decimal object. If no value is given, return Decimal(‘0’). The context does not affect the conversion and is only passed to determine if the InvalidOperation trap is active.

使用 decimal 不就是为了避免 float 的误差吗?

对 decimal 使用 round 不就把精确表示又带回了 float 的不精确表示了吗?

对 decimal round 有自己的函数

感谢告知 :D

但是 round 返回的还是 decimal 类型


抱歉,我看错了,,我以为是把 decimal 加法结果传给了 round,,

还真是。。。。

但按照 python 设计理念,不是应该 Decimal 类里实现一个__round__吗,然后这个__round__的逻辑跟 decimal.round 的逻辑一样。

我还是为是说 [四舍六入五成双]

>>> round(1.5)
2
>>> round(2.5)
2

round(Decimal("0.123456789"), 3) + Decimal("0.01") #Decimal('0.133')

<br>from decimal import *<br><br>getcontext().prec<br><br>

不是 decimal 的坑, 初始化 decimal 变量,推荐使用 string 或者在运算时手动指定 percesion

初始化 decimal 别传浮点数进去

另外 decimal 有自己 round 方法。

#12 原文:“hjq98765 应该 JS 才会出现吧 /doge ”
======
回复:python3 也有这个坑,刚从 python2 转过来的时候着实被坑了一把


> 四舍六入五成双

C++ 也会

因为你传进去的时候就不是精确的

回到顶部