【发布时间】:2021-03-21 03:45:10
【问题描述】:
问题:在计算算术运算时,如何让 Python 的 Decimal 模块舍入到指定的小数位,而不是舍入到指定的精度(有效数字)?
信息
我一直在使用 Python 中的 Decimal 模块使用 setcontext 方法将值四舍五入到指定的精度。在我们开始交叉整数和小数之前,这很有效,因为有效数字不会区分两者。
import decimal as d
from math import pi
decimal_places = 0
d.setcontext(d.Context(prec=decimal_places+1, rounding=d.ROUND_HALF_UP))
# This works fine
num = pi
print(f"Rounding {num} to {decimal_places} decimal places:")
print(f"Traditional rounding (correct): {round(num, decimal_places)}")
print(f"Decimal rounding (correct): {+d.Decimal(num)}")
# This is were issues start to arise
num = pi/10
print(f"\nRounding {num} to {decimal_places} decimal places:")
print(f"Traditional rounding (correct): {round(num, decimal_places)}")
print(f"Decimal rounding (incorrect): {+d.Decimal(num)}")
Rounding 3.141592653589793 to 0 decimal places:
Traditional rounding (correct): 3.0
Decimal rounding (correct): 3
Rounding 0.3141592653589793 to 0 decimal places:
Traditional rounding (correct): 0.0
Decimal rounding (incorrect): 0.3
用例
为什么还要使用十进制模块而不是 Python 的 round 函数?那么小数模块的优点是它会在算术评估(PEMDAS)的所有步骤中应用该精度上限。
例如,如果我想在函数中对 x 进行四舍五入,我可以这样做:
function_str = "0.5 * (3*x) ** 2 + 3"
eval(function_str.replace("x", "(+d.Decimal(x))"))
一个更完整(更简单)的例子:
import decimal as d
decimal_places = 0
d.setcontext(d.Context(prec=decimal_places+1, rounding=d.ROUND_HALF_UP))
numerator = 5
denominator = 1.1
num_err = 0.5
new_num = numerator + num_err
print(f"Rounding {numerator}/{denominator} to {decimal_places} decimal places:")
print(f"Traditional rounding (incorrect): {round(new_num, decimal_places)/denominator}")
print(f"Decimal rounding (correct): {+d.Decimal(new_num) / d.Decimal(denominator)}")
Rounding 5/1.1 to 0 decimal places:
Traditional rounding (incorrect): 5.454545454545454
Decimal rounding (correct): 5
在这里,round 似乎仍然是一个更简单的解决方案,因为它可以放在输出周围,但随着函数复杂性的增加,它变得越来越不可行。在用户输入函数的情况下,传统舍入的可行性实际上为零,而使用小数模块就像function_str.replace("x", "(+d.Decimal(x))") 一样简单。
请注意,quantize 方法将不是一个可行的选项,因为它只对当前数字进行舍入,而不是对所有值进行舍入(这是设置上下文精度所做的)。
【问题讨论】:
-
听起来你想做定点运算,而
decimal是一个浮点库。如果你想做定点数学,你最好只使用整数并自己处理缩放。 -
我不确定我理解你的意思。我认为十进制模块用于定点算术“十进制定点和浮点算术”(docs.python.org/3/library/decimal.html)。你介意给我举个例子吗?
-
文档是这么说的,但实际上,
decimal模块所做的一切都是浮点数。如果您检查faq section,您会发现模拟定点基本上涉及在任何地方粘贴一堆quantize调用,这是您已经丢弃的不可行的选项。 -
(即使使用
quantize,抽象也会泄漏——例如,如果操作的结果太大,可能会丢失精度,使用quantize无法恢复。有些结果也可能由于双舍入问题导致舍入不正确,除非您对舍入处理非常小心) -
那么你的意思是decimal模块不支持这个功能,如果我想这样做我需要构建自己的定点库?
标签: python math decimal rounding fixed-point