如何测试 Julia 中特定数字有效数字的近似相等性

How to test approximate equality for a specific number significant digits in Julia

Julia 中的isapprox() 函数用于测试两个数字或数组是否近似相等。我希望能够测试任何所需数量的有效数字的近似相等性。如下面的代码示例所示,近似值的容差以绝对值或相对(百分比)偏差给出。

# Syntax
isapprox(a, b; atol = <absolute tolerance>, rtol = <relative tolerance>)

# Examples
# Absolute tolerance
julia> isapprox(10.0,9.9; atol = 0.1) # Tolerance of 0.1
true

# Relative tolerance
julia> isapprox(11.5,10.5; rtol = 0.1) # Rel. tolerance of 10%
true

julia> isapprox(11.4,10.5; rtol = 0.01) # Rel. tolerance of 1%
false

julia> isapprox(98.5, 99.4; rtol = 0.01) # Rel. tolerance of 1%
true

我在某个论坛上看到设置 rtol = 1e-n,其中 n 是有效数字的个数,将比较有效数字。 (不幸的是,我无法再次找到它。)无论如何,如示例所示,这显然不是真的。

鉴于本例中我们要近似等于两位有效数字,11.4和10.5都近似等于11。但是,两者之间的相对差异大于1%,returning近似值 false。但是,对于大于 90 的任何数字,近似值将为 true。将相对公差增加十倍至 10% 将导致灵敏度过低,如代码所示。

是否有 parameter/value/formula 我可以将 isapprox()rtol 正确设置为 return true 以获得任何所需的有效数字位数?

我认为您可能只需要为此定义自己的函数。一些棘手的细节:

  1. 你关心什么基地?
  2. 您想如何对待 +0 和 -0?
  3. 通常跨越 0 的数字?
  4. 非规范化数字?
  5. 接近无穷大的数字?

在大多数极端情况下可能会做正确事情的快速穷人版本是对两个数字进行格式打印,使其具有所需的精度,然后比较字符串。

using Formatting

julia> function isapproxsigfigs(a, b, precision)
         fmt = "{:.$(precision-1)e}"
         format(fmt, a) == format(fmt, b)
       end
isapproxsigfigs (generic function with 1 method)

julia> isapproxsigfigs(pi, 3.14, 3)
true

julia> isapproxsigfigs(pi, 3.14, 4)
false

应该以 10 为基数工作,始终将正数与负数视为不相等,并且可能对非规范化数字做正确的事情。您可能希望添加对无穷大和 NaN 的显式检查,因为此实现将无穷大视为相等,更糟糕的是将 NaN 视为相等。

注意:未来的读者也许可以做到

isapproxsigfigs(a, b, precision) = @sprintf("%.*e", precision, a) == @sprintf("%.*e", precision, b)

但 Julia 目前不支持其格式字符串中的 .* 精度规范。虽然有一个 PR,所以也许 v1.7 以后会支持它

您可以从 NumPy 中得到提示并实施:

less_equal(x,y) = x <= y
isclose(x, y, atol, rtol) = less_equal(abs(x-y), atol + rtol * abs(y))

矢量化效果不是很好,所以如果您想要矢量化版本,请务必查看 allclose。此外,使用 rtol 使其在 xy 上不可交换,因此请注意这一点。

快速回答是没有rtol没有固定值你可以选择保证isapprox(x, y; rtol=rtol)比较值xy 到一定数量的有效数字。这是因为how isapprox is implementedrtol是相对于norm(x)norm(y)最大值计算的。您必须为要比较的每对 xy 计算不同的 rtol

您要求的是一种将 xy rounded 的值与一定数量的有效数字进行比较的方法(以 10 为基数)。 round 方法有一个可能有用的关键字 sigdigits

isapproxsigfigs(a, b, precision) = round(a, sigdigits=precision) == round(b, sigdigits=precision)

isapproxsigfigs(10, 9.9, 1)  # true,  10 == 10
isapproxsigfigs(10, 9.9, 2)  # false, 10 != 9.9

isapproxsigfigs(11.4, 10.5, 1)  # true,  10 == 10
isapproxsigfigs(11.4, 10.5, 2)  # false, 11 != 10 (remember RoundingMode!)

isapproxsigfigs(11.4, 10.51, 1)  # true,  10 == 10
isapproxsigfigs(11.4, 10.51, 2)  # true,  11 == 11
isapproxsigfigs(11.4, 10.51, 3)  # false, 11.4 != 10.5

对于第二个示例,请记住,如果您四舍五入,则 10.5 仅“接近 11”。默认RoundingMode used by Julia is RoundNearest, which rounds ties to even. If you want ties to round up, use RoundNearestTiesUp:

isapproxsigfigs2(a, b, precision) = 
    round(a, RoundNearestTiesUp, sigdigits=precision) == 
    round(b, RoundNearestTiesUp, sigdigits=precision)

isapproxsigfigs2(11.4, 10.5, 2)  # true, 11 == 11