标准对 std::pow、std::log 等 cmath 函数有何规定?

What does standard say about cmath functions like std::pow, std::log etc?

标准是否保证函数 return 在所有实现中得到完全相同的结果?

例如 pow(float,float) 表示 32 位 IEEE 浮点数。如果传入相同的两个浮点数,所有实现的结果是否相同?

或者标准允许的一些灵活性取决于用于实现的算法的微小差异pow

不,C++ 标准不要求 cmath 函数的结果在所有实现中都相同。对于初学者,您可能无法获得 IEEE-754/IEC 60559 浮点运算。

也就是说,如果一个实现确实使用了 IEC 60559 并定义了 __STDC_IEC_559__,那么它 必须 遵守 C 标准的附录 F(是的,你的问题是关于 C++,但 C++ 标准遵从 C header 的 C 标准,如 math.h)。附件 F 指出:

  • The float type matches the IEC 60559 single format.
  • The double type matches the IEC 60559 double format.
  • The long double type matches an IEC 60559 extended format, else a non-IEC 60559 extended format, else the IEC 60559 double format.

此外,它说正常运算必须遵循 IEC 60559 标准:

  • The +, , *, and / operators provide the IEC 60559 add, subtract, multiply, and divide operations.

它进一步要求 sqrt 遵循 IEC 60559:

  • The sqrt functions in <math.h> provide the IEC 60559 square root operation.

然后继续描述其他几个 floating-point 函数的行为,对于这个问题,您可能对其中的大部分不感兴趣。

最后,它到达 math.h header,并指定各种数学函数(即 sincosatan2exp, 等)应该处理特殊情况(即 asin(±0) returns ±0, atanh(x) returns a NaN 并引发 "invalid" floating-point |x| > 1 等例外)。但它从未确定正常输入的确切计算,这意味着您不能依赖所有产生 精确 相同计算的实现。

所以不,它不需要这些函数在所有实现中表现相同,即使所有实现都定义了 __STDC_IEC_559__.


这都是从理论上讲的。实际上,情况更糟。 CPU 通常执行 IEC 60559 算法,但可以有不同的舍入模式(因此结果会因计算机而异),并且编译器(取决于优化标志)可能会做出一些不严格符合您的标准的假设浮点运算。

因此在实践中,它甚至比理论上更不严格,您很可能会看到两台计算机在某些时候产生的结果略有不同。


一个真实世界的例子是 glibc,GNU C 库实现。 They have a table of known error limits for their math functions 跨不同的 CPU。如果所有 C 数学函数都是 bit-exact,那么那些 table 将全部显示 0 个错误 ULP。但他们没有。 tables 表明它们的 C 数学函数中确实存在不同数量的错误。我觉得这句话是最有意思的总结:

Except for certain functions such as sqrt, fma and rint whose results are fully specified by reference to corresponding IEEE 754 floating-point operations, and conversions between strings and floating point, the GNU C Library does not aim for correctly rounded results for functions in the math library[...]

glibc 中唯一 bit-exact 的东西是 C 标准的附件 F 要求 bit-exact 的东西。正如您在他们的 table 中看到的那样,大多数情况并非如此。