浮点数的相对比较

计算科学 浮点
2021-12-02 03:26:00

我有一个数值函数f(x, y)返回一个双浮点数,它实现了一些公式,我想检查它对于所有参数组合的解析表达式是否正确x并且y我感兴趣。比较计算和分析浮点数?

假设这两个数字是ab到目前为止,我一直在确保绝对 ( abs(a-b) < eps) 和相对 ( abs(a-b)/max(abs(a), abs(b)) < eps) 误差都小于 eps。这样,即使数字在 1e-20 左右,它也会发现数字不准确。

但是,今天我发现了一个问题,数值a和解析值为b

In [47]: a                                                                     
Out[47]: 5.9781943146790832e-322

In [48]: b                                                                     
Out[48]: 6.0276008792632078e-322

In [50]: abs(a-b)                                                              
Out[50]: 4.9406564584124654e-324

In [52]: abs(a-b) / max(a, b)                                                  
Out[52]: 0.0081967213114754103

所以绝对误差[50](显然)很小,但相对误差[52]很大。所以我认为我的程序中有一个错误。通过调试,我意识到这些数字是不正常的。因此,我编写了以下例程来进行适当的相对比较:

real(dp) elemental function rel_error(a, b) result(r)
real(dp), intent(in) :: a, b
real(dp) :: m, d
d = abs(a-b)
m = max(abs(a), abs(b))
if (d < tiny(1._dp)) then
    r = 0
else
    r = d / m
end if
end function

tiny(1._dp)我的计算机上返回 2.22507385850720138E-308 的位置。现在一切正常,我只是得到 0 作为相对错误,一切都很好。特别是上面的相对误差[52]是错误的,它只是由于非正规数的准确性不足造成的。我的rel_error功能实现是否正确?我应该只检查abs(a-b)小于微小(=非正常)并返回 0 吗?或者我应该检查一些其他组合,比如 max(abs(a), abs(b))

我只想知道“正确”的方式是什么。

3个回答

isnormal()您可以使用from math.h(C99 或更高版本,POSIX.1 或更高版本)直接检查非规范化。在 Fortran 中,如果模块ieee_arithmetic可用,您可以使用ieee_is_normal(). 为了更准确地了解模糊等式,您必须考虑非正规的浮点表示并确定您的意思,以使结果足够好。

更重要的是,要相信任一结果都是正确的,您必须确保在中间步骤没有丢失太多数字。使用非正规计算通常是不可靠的,应该通过在内部重新调整算法来避免。feenableexcept()为确保您的内部缩放成功,我建议使用C99 或ieee_arithmeticFortran 中的模块激活浮点异常。

尽管您可以让您的应用程序捕获浮点异常引发的信号,但我尝试过的所有内核都重置了硬件标志,因此fetestexcept()不会返回有用的结果。使用 运行时-fp_trap,PETSc 程序将(默认情况下)在引发浮点错误时打印堆栈跟踪,但不会识别违规行。如果您在调试器中运行,则调试器会保留硬件标志并中断有问题的表达式。您可以通过从调试器调用来检查确切原因,fetestexcept其中结果是以下标志的按位或(值可能因机器而异,请参阅fenv.h;这些值适用于带有 glibc 的 x86-64)。

  • FE_INVALID = 0x1
  • FE_DIVBYZERO = 0x4
  • FE_OVERFLOW = 0x8
  • FE_UNDERFLOW = 0x10
  • FE_INEXACT = 0x20

Donald Knuth 在“计算机编程艺术”的第 2 卷“半数值算法”中提出了浮点比较算法的建议。它是由 Th 在 C 中实现的。Belding(参见fcmp 包)并且在GSL中可用。

最佳舍入的非规范化数字确实可能具有较高的相对误差。(将其刷新为零,同时仍称其为相对错误是误导性的。)

但是接近于零,计算相对误差是没有意义的。

因此,即使在达到非规范化数字之前,您也应该切换到绝对精度(即在这种情况下要保证的精度)。

因此,我建议测试计算的y反对真实x通过检查公式的有效性,例如|yx|absacc+relaccmax(|x|,|y|). 比如说 relacc=1e-12 和 absacc=1e-150。

然后,您的代码的用户准确地知道他们真正拥有多少准确性。