【问题标题】:Accurant percentage calculation of decimal values in PythonPython中十进制值的准确百分比计算
【发布时间】:2022-06-11 02:51:50
【问题描述】:

我的目标是在 Python 中准确地从 18 位小数的值中添加或删除 0.05%,而不将它们转换为浮点数。我提出了以下两个解决方案,它们对我来说似乎是正确的,但我对 Python 中的数字非常不熟悉,因此我想知道是否有更好的(就准确性而言)解决方案。

price_in_wei = 1000000000000000000 # = 1

# -0.05%
price_with_fee = (price_in_wei/1000)*995

# +0.05%
price_with_fee = (price_in_wei/1000)*1005

# -0.05%
price_with_fee = (price_in_wei*995)/1000

# +0.05%
price_with_fee = (price_in_wei*1005)/1000

【问题讨论】:

  • 只是好奇您的准确度阈值是多少?精确到 18 位十进制数字?

标签: python


【解决方案1】:

如果您想避免转换为浮点数,请务必使用integer division。这可确保值保持为整数。

从逻辑上讲,您的代码看起来不错。

【讨论】:

  • 从逻辑上讲,OP 的代码并不是很好。如果 OP 遵循您的建议并简单地将 / 替换为 // 则 OP 将失去前两个计算的精度。应该指出,乘法必须在整数除法之前执行,以保持精度。例如(price_in_wei*995)//1000 是要走的路,而不是 (price_in_wei//1000)*995
【解决方案2】:

为了准确,您的第二组计算会更准确。也就是说,你应该先乘,然后除。 Python 似乎接受非常大的整数。在我的电脑上,我不确定我是否可以超过 1E308。

另外,正如@RyanZhang 所建议的,您应该使用integer/floored division// 运算符。根据文档,// 运算符的结果不能保证为“int”类型,因此您可能需要始终显式转换为 int:

# -0.05%
price_with_fee = int((price_in_wei*995)//1000)

# +0.05%
price_with_fee = int((price_in_wei*1005)//1000)

【讨论】:

    【解决方案3】:

    我建议您使用类decimal.Decimal 使用十进制算术。即使您需要最终结果为整数值,使用十进制算术进行中间计算也会提供更高的准确性。对于您提供的示例,您在第二组计算中所做的工作足够好,但这仅仅是因为使用了特定的值。如果price_in_wei 改为 1000000000000000001 会怎样?您的计算将产生 9.95e+17,或者,如果转换为 int,则为 99500000000000000:

    >>> price_in_wei
    1000000000000000001
    >>> price_with_fee = price_in_wei*995/1000
    >>> price_with_fee
    9.95e+17
    >>> int(price_with_fee)
    995000000000000000
    

    但十进制算术提供更高的精度:

    >>> from decimal import Decimal
    >>> price_with_fee = Decimal(price_in_wei) * 995 / 1000
    >>> price_with_fee
    Decimal('995000000000000000.995')
    >>> price_with_fee = int(price_with_fee.quantize(Decimal(1))) # round to an integer and convert to int
    >>> price_with_fee
    995000000000000001
    

    但假设您的货币是美元,它支持小数点后两位精度(美分)。如果你想要那个精度,你应该专门使用十进制算术。例如:

    >>> from decimal import Decimal, ROUND_HALF_UP
    >>> price_in_wei = Decimal('1000000000000000003')
    >>> price_with_fee = (price_in_wei * 995 / 1000).quantize(Decimal('1.00'), decimal.ROUND_HALF_UP) # round to two places
    >>> price_with_fee
    Decimal('995000000000000002.99')
    

    【讨论】:

      【解决方案4】:

      使用decimal 模块将是一种可行的方法,因为decimal 模块具有用户可更改的精度(默认为 28 位),该精度可以根据给定问题的需要而定。

      我认为,这些链接会有所帮助:

      https://docs.python.org/3/tutorial/floatingpoint.html

      https://docs.python.org/3/library/decimal.html

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多