如何将 Python Decimal 数值保留两位小数?

14

我有一个Python的Decimal对象(一个货币数额),我想将它舍入到两个小数位。我尝试使用常规的round()函数来实现这一点。不幸的是,这会返回一个浮点数,使其无法继续使用:

>>> from decimal import Decimal
>>> a = Decimal('1.23456789')
>>> type(round(a, 2))
<type 'float'>

在decimal模块中,我看到了几个关于四舍五入的内容:

  • ROUND_05UP
  • ROUND_CEILING
  • ROUND_DOWN
  • ROUND_FLOOR
  • ROUND_HALF_DOWN
  • ROUND_HALF_EVEN
  • ROUND_HALF_UP
  • ROUND_UP
  • 舍入

我觉得这些都不符合我的要求(或者我错了吗?)。

所以我的问题是:有没有人知道如何可靠地将Python Decimal四舍五入到两个小数位,以便我可以继续使用Decimal?欢迎所有提示!


2
使用整数(以分为单位而不是美元)会更有意义,这也会更快。 - Tim Pietzcker
设置十进制数的精度:decimal.getcontext().prec=2 - ooga
2
@ooga:这涉及到有效数字的数量,而不是小数位数,因此不适用(它将12345678转换为1.2E+7,但仅在对其进行计算后才能这样做,而不是在定义后立即进行)。 - Tim Pietzcker
@TimPietzcker 我明白了。在这种情况下,我同意使用整数。 - ooga
4
值得注意的是,round 在 Python 3 中直接返回一个 Decimal 实例;只有在 Python 2 中才会转换为 float。即使如此,如果你想要除了默认的“银行家舍入”模式以外的其他模式,仍然需要使用 quantize 方法。 - Mark Dickinson
8个回答

18

4
注意,在较新的 Python 版本中,您可以使用内置的 round() 函数,因为如果其中一个参数为 Decimal,则四舍五入现在会返回一个 Decimal。 - kramer65
@kramer65:没错,但它仍然只使用了“ROUND_HALF_EVEN”方法。 - Tim Pietzcker

14
自 Python 3.3 版本开始,您可以使用 round() 函数并传入一个 Decimal 类型的参数,它会返回一个 Decimal 类型值。
>>> from decimal import Decimal
>>> round(Decimal('3.14159265359'), 3)
Decimal('3.142')

请参阅此答案的详细信息。


round(Decimal(0.5), 0) 返回 0 吗? - Nimitz14
1
“round” 使用 ROUND_HALF_EVEN,其中偶数(包括0)向下舍入,奇数向上舍入。这有助于在处理大数据集时避免结果略微偏斜。 - jobu1342

7
你尝试的第一个对我来说很好用。
import decimal

type(round(decimal.Decimal('1.23456789'),2))
是一个Python中的class(类),它用于处理浮点数精度问题。

1
"round" 会将结果转换为 "float" 类型的对象。 - Rohan
1
我刚在 Python 3.8 版本中测试了一下,发现你是对的。我猜这可能是在我测试之后发布的某个 Python 版本中改变了。 - kramer65
sys.version_info(major=3, minor=8, micro=5, releaselevel='final', serial=0) gives <class 'decimal.Decimal'> for print(type(round(decimal.Decimal(1.1233999999999999541699935434735380113124847412109375), 2))) - Uroš Dukanac

1
如果您正在使用Jupyter Notebook,则只需添加魔术函数%precision 2来保留两位小数,以此类推。
但是,如果您的值不是float()类型,则可以使用以下方法:
 from decimal import getcontext, Decimal
 getcontext().prec = 2
 # for example, take integer value
 mynum = 2
 type(mynum) # Int
 print round(Decimal(mynum), 2) # 10.0

1
在Jupyter Notebook中,%precision似乎只能为浮点值设置漂亮打印精度,而不能为Decimal值设置。 - Gary
2
另外,getcontext().prec=2会导致Decimal操作保留2个有效数字,而不是2个有效的小数位。例如,Decimal('1') * Decimal('123')将得到Decimal('1.2E+2') - Gary

1
这个想法浮现在我的脑海中:

import decimal
decimal.Decimal(str(round(a, 2)))

但我不知道它有多快。

0
@Do Anh Tu,很好的回答...只需要进行一些小的更改,以便处理浮点输入,除了十进制和字符串输入外,可以将Decimal(n)更改为Decimal(str(n)),以下是整体代码:
from decimal import Decimal, ROUND_HALF_EVEN

def decimalize(n, prec=8):
    n = n if isinstance(n, Decimal) else Decimal(str(n))
    fmt = ".{}1".format("0" * (prec - 1))
    return n.quantize(Decimal(fmt), rounding=ROUND_HALF_EVEN)

这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - Tugrul Ates

0
基于@countinglambdastosleep的答案,更Pythonic的代码。
from decimal import Decimal, ROUND_HALF_EVEN

def decimalize(n, prec=8):
    n = n if isinstance(n, Decimal) else Decimal(n)
    fmt = ".{}1".format("0" * (prec - 1))
    return n.quantize(Decimal(fmt), rounding=ROUND_HALF_EVEN)

为什么要使用ROUND_HALF_EVEN?你可以在这里找到答案。


0

不确定为什么他们没有内置函数,但我自己写了一个。 我知道它在技术上并不是“trueRound”,但如果你想的话可以更改。 在Python 3.9上测试通过。

    def trueRound(num: float or str, decimalPlaces: int) -> float:
        a = Decimal(str(num))
        places_str = "0."
        for i in range(decimalPlaces - 1):
            places_str += "0"
        places_str += "1"
        PLACES = decimal.Decimal(places_str)
        result = a.quantize(PLACES)
        return float(result)

1
抱歉,您需要执行以下操作:导入decimal模块并从中导入Decimal类。我知道只有一个Decimal()和另一个decimal.Decimal不太好,但我不确定是否可以编辑我的答案。 - countinglambdastosleep

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接