使用浮点数时避免不准确?

Avoiding inaccuracy when using floating points numbers?

我正在尝试使用 Monte Carlo 积分来近似给定图形下的区域以计算其面积。为此,我需要计算出的 y_min 和 y_max 是准确的。因此,作为示例,我将使用从 0 到 pi 的 sin(x) 图表。要找到 y_min 和 y_max 我有以下功能:

def y_range(f, x_min, x_max, n=100):
    # Step size
    h = float((x_max - x_min)) / n

    # Calculate y for n points between x_min and x_max
    y = [f(x * h) for x in range(0, n + 1)]

    # Get minimum and maximum y
    y_max = max(y)
    y_min = min(y)

    return y_min, y_max

打印 y_min 和 y_max 得到:

y_max = 1.0
y_min = -3.21624529935e-16

我知道 y_min 应该等于 0.0,那么我该如何纠正这个错误?

当精度如此重要时,我不会使用 float。我建议你看看decimal.Decimal。假设您将 x_minx_max 更改为 Decimal,您只需删除第 3 行的 float

This post 可能有助于解释不准确的来源:

pi 是不合理的,因此无论您使用哪种表示方式,您都必须接受它只能以一定的精度表示。如果您使用的是浮点数,那么精度将按照 10**(-16) 的顺序排列 here。最简单的方法是使用 round(value,15) 将结果四舍五入为 15 位小数。如果您需要比 15 位十进制数字更高的精度,您确实可以研究其他类型来表示变量,例如 decimal

根本问题是 max 不能作为 min + 100*h 的结果导出,对于任何 h。有几个潜在的原因,但最直接的一个就是它们之间的步数不能被 100 整除。

如何做得更好取决于你想要多小心。您需要做的两件事是在两个值之间进行插值(而不是基于起点和步骤),并以准确的方式执行插值本身。

以下代码将产生可靠的结果:

def interp_at_step(a, b, i, n):
    # separate calculation of alpha and beta to avoid catastrophic cancellation
    alpha = (n-i)/n
    beta = i/n
    return a*alpha + b*beta

def y_range(f, x_min, x_max, n=100):
    # Calculate y for n points between x_min and x_max
    y = [f(interp_at_step(x_min, x_max, x, n)) for x in range(0, n + 1)]

    # Get minimum and maximum y
    y_max = max(y)
    y_min = min(y)

    return y_min, y_max

当然,正如 Denys 提到的,pi 不能精确表示。因此,这将减少插值的错误,但不一定会减少操作数本身的错误。