如何测试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%,返回近似false。然而,对于任何大于90的数字,近似值将是true。如代码所示,将相对容差增加10倍至10%将导致灵敏度过低。

是否有一个参数/值/公式,我可以为isapprox()设置rtol,以正确返回true的任何所需的有效数字?

快速回答是例如,rtol没有固定值,您可以选择isapprox(x, y; rtol=rtol)来保证xy的值与一定数量的有效数字进行比较。这是因为isapprox是如何实现的:rtol是相对于norm(x)norm(y)最大值计算的。您必须为正在比较的每对xy计算不同的rtol

看起来您要求的是将xy四舍五入的值与特定数量的有效数字(以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只是"几乎"如果你把领带捆起来。Julia使用的默认RoundingModeRoundNearest,它将关系舍入为偶数。如果你想让领带四舍五入,使用RoundNearestTiesUp:

isapproxsigfigs2(a, b, precision) = 
round(a, RoundNearestTiesUp, sigdigits=precision) == 
round(b, RoundNearestTiesUp, sigdigits=precision)
isapproxsigfigs2(11.4, 10.5, 2)  # true, 11 == 11

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

  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不可交换,所以要小心。

最新更新