c++ SQRT保证精度,上限/下限

c++ sqrt guaranteed precision, upper/lower bound

本文关键字:上限 下限 精度 SQRT c++      更新时间:2023-10-16

我必须检查一个包含平方根的不等式。为了避免由于浮点不精确和舍入而导致的不正确结果,我使用std::nextafter()来获得上/下界:

#include <cfloat> // DBL_MAX
#include <cmath> // std::nextafter, std::sqrt
double x = 42.0; //just an example number
double y = std::nextafter(std::sqrt(x), DBL_MAX);

a) y*y >= x保证使用GCC编译器吗?

b)对于+ - * /甚至std::cos()std::acos()等其他操作是否也适用?

c)是否有更好的方法来获得上/下界?

更新:我读到这不是c++标准的保证,但应该根据IEEE-754工作。这将与GCC编译器工作吗?

一般情况下,浮点操作会导致一些ULP错误。IEEE 754要求大多数操作的结果在0.5 ULP以内是正确的,但是错误可能会累积,这意味着结果可能不在精确结果的一个ULP以内。精度也有限制,因此根据结果值中的位数,您也可能无法处理相同量级的值。超越函数也因在计算中引入错误而臭名昭著。

然而,如果您使用GNU glibc, sqrt将正确到0.5 ULP以内(四舍五入),因此您的特定示例将工作(忽略NaN, +/-0, +/-Inf)。虽然,最好是定义一个作为容错范围然后用它作为界。例如,

bool gt(double a, double b, double eps) {
  return (a > b - eps);
}

根据您需要的计算精度级别,您也可能希望使用long double来代替。

那么,为了回答你的问题…

a) y*y>= x保证使用GCC编译器?

假设您使用GNU glibc或SSE2 intrinsic,可以。

b)这是否适用于其他操作,如+ - */或std::cos()和std::acos()?

假设您使用GNU glibc和一个操作,是的。尽管有些超越数不能保证正确舍入。

c)是否有更好的方法来获得上/下界?

你需要知道你在计算中的容错是多少,并把它作为一个epsilon(它可能大于一个ULP)。

对于GCC,本页面建议使用GCC内置的sqrt函数__builtin_sqrt

另外,这种行为将取决于你如何编译你的代码,以及它在

上运行的机器
  1. 如果处理器支持SSE2,那么你应该用标志-mfpmath=sse -msse2编译你的代码,以确保所有的浮点操作都是使用SSE寄存器完成的。

  2. 如果处理器不支持SSE2,那么你应该为浮点值使用long double类型,并使用-ffloat-store标志进行编译,以强制GCC不使用寄存器来存储浮点值(这样做会导致性能损失)

关于

c)是否有更好的方法来获得上/下界?

另一种方法是使用不同的舍入模式,即FE_UPWARDFE_DOWNWARD而不是默认的FE_TONEAREST。请参阅https://stackoverflow.com/a/6867722这可能较慢,但是一个更好的上/下界。