HOWTO · Python

如何在 Python 中将数字四舍五入到有效数字

了解如何使用 round()、math、格式化、Decimal、sigfig 和 NumPy 将 Python 数字四舍五入到有效数字。

本页内容

若要将数字四舍五入到所选的有效数字位数,请找出它的十进制数量级,并将相应的小数位数传递给 round()。例如,1,342 四舍五入到一位有效数字会变成 1,000。对于单个值,内置函数就足够了;重复计算时使用辅助函数,结果应保持为文本时使用格式化,十进制四舍五入规则重要时使用 Decimal,数组则使用 NumPy。

使用 Python’s round() 四舍五入到有效数字

round(number, ndigits) 会四舍五入到若干小数位。对于有效数字,将 ndigits 计算为 digits - 1 - floor(log10(abs(number)))。对于 1,342 和一位有效数字,这是 1 - 1 - 3,即 -3,因此 Python 会四舍五入到最接近的千位。

运行此示例:

print(round(1342, -3))
1000

Python 对于正好位于中间的情况使用四舍五入到偶数(ties-to-even),因此当值正好位于两个四舍五入值的中间时,最后保留的数字是偶数。这与总是将中间情况远离零四舍五入不同。负的 ndigits 会四舍五入到十位、百位或更大的 10 的幂。round() 返回一个数字;它不会将尾随零保留为显示约定。对于浮点输入,存储的二进制近似值也可能影响表面上的十进制中间情况。

使用 math 辅助函数四舍五入到有效数字

辅助函数可以从以 10 为底的数量级计算小数位数。以下版本接受正整数位数和有限的数值,并将零原样返回。对于不受支持的位数或非有限输入,它会引发 ValueError,因为 log10() 无法为这些值确定有限的十进制数量级。

from math import floor, isfinite, log10


def round_significant(value, digits):
    if not isinstance(digits, int) or digits < 1:
        raise ValueError("digits must be a positive integer")
    if not isfinite(value):
        raise ValueError("value must be finite")
    if value == 0:
        return value
    places = digits - 1 - floor(log10(abs(value)))
    return round(value, places)


print(round_significant(1324, 2))

该公式适用于正和负的有限值,因为它使用 abs(value) 来找出数量级,而 round() 会保留符号。它遵循 Python 的四舍五入到偶数规则。在计算对数之前,使用 math.isfinite() 来决定输入是否受支持;如果您的应用程序需要,请单独处理 NaN 和无穷大。

使用字符串格式化四舍五入到有效数字

%g 格式将其精度用作有效数字位数。当精度为 p 时,如果十进制指数小于 -4 或大于或等于 p,它使用科学记号;否则,它使用普通记号。对于精度 3,0.0001 和 999 使用普通记号,而 0.00001 和 1000 使用科学记号。格式化返回一个字符串,当目标是呈现时这很有用。保留该字符串以保留其所选的表示方式;仅在需要后续数值运算时才使用 float() 转换它。转换为数字会丢弃尾随零等显示细节。

例如,"%.3g" % 1324 会以三位有效数字格式化该值。结果是文本,普通记号和科学记号之间的确切选择由格式化程序做出。

使用 sigfig 四舍五入到有效数字

第三方包 sigfig 通过其 sigfigs 参数接受有效数字的位数。在使用之前,请在您的环境中安装该包。例如,包调用是 sigfig.round(1324, sigfigs=2)。当包的行为和格式化适合您的应用程序时,这很方便;对于上述内置或辅助方法,它不是必需的。

使用 decimal.Decimal 四舍五入到有效数字

Decimal 支持十进制算术和显式的四舍五入模式。当输入中的十进制数字重要时,请从字符串构造它;转换现有的浮点数无法恢复该浮点数未存储的信息。此示例将有限的非零十进制数缩放至所要求的精度,并使用 ROUND_HALF_UP 将中间情况远离零四舍五入。

from decimal import Decimal, ROUND_HALF_UP


def round_decimal_significant(value, digits):
    if not isinstance(digits, int) or digits < 1:
        raise ValueError("digits must be a positive integer")
    number = Decimal(str(value))
    if not number.is_finite():
        raise ValueError("value must be finite")
    if number.is_zero():
        return number
    quantum = Decimal(1).scaleb(number.adjusted() - digits + 1)
    return number.quantize(quantum, rounding=ROUND_HALF_UP)


print(round_decimal_significant("1324", 2))

结果仍保持为 Decimal。ROUND_HALF_UP 只是一种策略;当它符合您的应用程序规则时,请选择如 ROUND_HALF_EVEN 的模式。十进制算术仍可能受到活动上下文精度的限制,因此它不保证无限的精度。

使用 NumPy 将数组四舍五入到有效数字

当输入是数组时,NumPy 很有用,但 numpy.around() 接受的是小数位而不是有效数字。对于有限的非零值,使用 digits - 1 - floor(log10(abs(value))) 计算小数位数,并将其传递给 numpy.around()。对于数组,逐元素计算数量级,并在取对数之前处理零和非有限值。NumPy 是一个额外的依赖项;此方法遵循浮点四舍五入行为,并且不保留显示零。

以下示例仅四舍五入有限的非零元素。它将零、NaN 以及正或负无穷大保持不变。返回的值是具有浮点元素的 NumPy ndarray。NumPy 是一个额外的依赖项,因此请在运行此示例之前安装它。

import numpy as np


values = np.array([1234.5, -1234.5, 0.0, np.nan, np.inf, -np.inf])
digits = 3
rounded = values.copy()
finite_nonzero = np.isfinite(values) & (values != 0)

for index in np.flatnonzero(finite_nonzero):
    places = digits - 1 - int(np.floor(np.log10(abs(values[index]))))
    rounded[index] = np.around(values[index], decimals=places)

print(rounded.tolist())
[1230.0, -1230.0, 0.0, nan, inf, -inf]