小数模块中的有效数字

5 投票
6 回答
9428 浏览
提问于 2025-04-11 09:19

我决定通过写一些Python脚本来解决我的物理作业问题。遇到的一个问题是,数字的有效位数有时候似乎不太对。例如,下面这个代码能正确处理有效位数:

from decimal import Decimal
>>> Decimal('1.0') + Decimal('2.0')
Decimal("3.0")

但这个就不行:

>>> Decimal('1.00') / Decimal('3.00')
Decimal("0.3333333333333333333333333333")

所以我有两个问题:

  1. 我是不是对的,这个结果的有效数字不符合预期,还是我需要再复习一下有效数字的计算?
  2. 有没有办法做到这一点,而不需要手动设置小数位数?当然,我知道可以用numpy来解决这个问题,但我只是想知道有没有办法用decimal模块来实现,出于好奇。

6 个回答

1

出于好奇,想问一下...使用十进制模块真的有必要吗?为什么不直接用浮点数,然后在需要显示的时候对数字进行有效数字的四舍五入呢?或者你是在试图跟踪计算中的有效数字(比如当你需要对结果进行误差分析时,计算误差是如何受到不确定性影响的)?如果你想要一个从数字左边进行四舍五入的函数,可以试试:

def lround(x,leadingDigits=0): 
    """Return x either as 'print' would show it (the default) 
    or rounded to the specified digit as counted from the leftmost 
    non-zero digit of the number, e.g. lround(0.00326,2) --> 0.0033
    """ 
    assert leadingDigits>=0 
    if leadingDigits==0: 
            return float(str(x)) #just give it back like 'print' would give it
    return float('%.*e' % (int(leadingDigits),x)) #give it back as rounded by the %e format  

当你打印这些数字或者把它们转换成字符串时,它们看起来会很正常,但如果你在命令行中工作而没有明确打印出来,它们可能会显得有点奇怪:

>>> lround(1./3.,2),str(lround(1./3.,2)),str(lround(1./3.,4))
(0.33000000000000002, '0.33', '0.3333')
3

小数不会像那样丢掉小数位。如果你真的想把精度限制在小数点后两位,可以试试下面的方法:

decimal.getcontext().prec=2

补充说明:你也可以在每次进行乘法或除法时调用 quantize() 方法(加法和减法会自动保持两位小数)。

8

把小数的计算精度改成2位数字其实并不是个好主意,除非你只打算做一次简单的计算。

你应该始终在比结果重要性更高的精度下进行计算,最后再把结果四舍五入。如果你在一连串的计算中,每一步都四舍五入到重要数字的位数,错误就会慢慢累积。小数模块并不知道某个操作是长计算中的一步,还是最终结果,所以它会假设不应该多余地四舍五入。理想情况下,它应该使用无限精度,但这太耗费资源了,所以Python的开发者选择了28位数字。

当你得到最终结果后,你可能想用的是量化(quantize):

>>> (Decimal('1.00') / Decimal('3.00')).quantize(Decimal("0.001"))
Decimal("0.333")

你需要手动跟踪重要性。如果你想要自动跟踪重要性,可以使用区间算术。有一些库可以在Python中使用,比如pyintervalmpmath(支持任意精度)。使用小数库实现区间算术也很简单,因为它支持有方向的四舍五入。

你可能还想看看小数算术常见问题:小数算术的“重要性”算术是什么?

撰写回答