舍入小数的问题(python)

时间:2012-01-15 10:25:44

标签: python decimal rounding

在我的程序中,十进制精度非常重要 我的很多计算必须精确到许多小数位(例如50)。

因为我正在使用python,所以我一直在使用十进制模块  (使用context()。prec = 99. ie; 设置为在实例化小数对象时具有99个小数位精度
因为pythonic浮标不允许任何接近这样的准确度。

由于我希望用户指定计算精度的小数位,我必须在我的代码中实现几个round()函数。
不幸的是,内置的圆函数和小数对象不能很好地相互作用。

round(decimal.Decimal('2.000000000000000000001'),50)

# Number is 1e-21.   There are 21 decimal places, much less than 50.

然而结果是 2.0 而不是 2.000000000000000000001
圆函数不会四舍五入到50.更不用了!

是的,我确保在Decimal对象的实例化时不会发生过度舍入,但是在调用后。 我总是将表示浮点数的字符串传递给Decimal构造函数,而不是pythonic浮点数。

为什么圆函数对我这样做?
(我意识到它可能最初设计用于pythonic浮点数,它永远不会有这么多的小数位,但文档声称Decimal对象完美地集成到python代码中并与内置的python函数兼容!)

非常感谢! (这让我非常不安,因为这个问题破坏了整个程序的使用)

规格:
python 2.7.1
Windows 7
十进制模块(内置)

5 个答案:

答案 0 :(得分:39)

由于 round() 强制将其输入强制转换为常规二进制浮点数,因此使用 quantize() 方法舍入小数对象的首选方法是:< / p>

>>> from decimal import Decimal
>>> d = Decimal('2.000000000000000000001')

>>> d.quantize(Decimal(10) ** -20)     # Round to twenty decimal places
Decimal('2.00000000000000000000')

要删除尾随零,请将 normalize() 应用于舍入结果:

>>> d = Decimal('2.000000000000000000001')
>>> d.quantize(Decimal(10) ** -20).normalize()
Decimal('2')

答案 1 :(得分:7)

问题尽可能地确定,round()正在返回Python float类型,而不是Decimal类型。因此,您在decimal模块上设置的精确度无关紧要,因为一旦您致电round(),您就不再拥有Decimal

要解决此问题,您可能需要采用另一种方法来舍入不依赖round()的数字。比如雷蒙德的建议。

您可能会发现这个简短的构思示例:http://ideone.com/xgPL9

答案 2 :(得分:4)

round()用于任何目的有困难:

(1)在Python 2.7中,round(Decimal('39 .142'))产生39.14,即浮点数; 3.2产生一个十进制。

>>> import decimal; round(decimal.Decimal('2.000000000000000000001'),50)
2.0   <<<=== ***float***

(2)round()只有一种舍入模式; Decimal.quantize有很多。

(3)对于所有输入类型,单个舍入模式已更改:Python 2.7从0开始(在大多数业务应用程序中预期),而Python 3.2舍入到最接近的偶数倍10 ** - n(业务预期较少) )。

使用Decimal.quantize()

答案 3 :(得分:4)

尝试以下方法......

from decimal import Decimal, ROUND_HALF_UP, getcontext

getcontext().prec = 51

def round_as_decimal(num, decimal_places=2):
    """Round a number to a given precision and return as a Decimal

    Arguments:
    :param num: number
    :type num: int, float, decimal, or str
    :returns: Rounded Decimal
    :rtype: decimal.Decimal
    """
    precision = '1.{places}'.format(places='0' * decimal_places)
    return Decimal(str(num)).quantize(Decimal(precision), rounding=ROUND_HALF_UP)

round_as_decimal('2.000000000000000000001', decimal_places=50)

希望有所帮助!

答案 4 :(得分:2)

如上所述,如果提供了Decimal,round()将返回一个float。 为了精确计算,我建议不要向下舍入十进制数,并且为了舍入最终结果,使用函数:

    def dround(decimal_number, decimal_places):
        return decimal_number.quantize(Decimal(10) ** -decimal_places)