【问题标题】:std::isinf does not work with -ffast-math. how to check for infinitystd::isinf 不适用于 -ffast-math。如何检查无穷大
【发布时间】:2014-05-20 19:14:18
【问题描述】:

示例代码:

#include <iostream>
#include <cmath>
#include <stdint.h>

using namespace std;

static bool my_isnan(double val) {
    union { double f; uint64_t x; } u = { val };
    return (u.x << 1) > (0x7ff0000000000000u << 1);
}

int main() {
    cout << std::isinf(std::log(0.0)) << endl;
    cout << std::isnan(std::sqrt(-1.0)) << endl;
    cout << my_isnan(std::sqrt(-1.0)) << endl;
    cout << __isnan(std::sqrt(-1.0)) << endl;

    return 0;
}

Online compiler.

使用-ffast-math,该代码打印“0, 0, 1, 1” - 没有,它打印“1, 1, 1, 1”。

正确吗?我认为std::isinf/std::isnan 在这些情况下仍应与-ffast-math 一起使用。

另外,如何使用-ffast-math 检查无穷大/NaN?您可以看到 my_isnan 这样做,它确实有效,但该解决方案当然非常依赖于架构。另外,为什么my_isnan 在这里工作而std::isnan 不工作? __isnan__isinf 怎么样。它们总是有效吗?

对于-ffast-mathstd::sqrt(-1.0)std::log(0.0) 的结果是什么。它是未定义的,还是应该是 NaN / -Inf?

相关讨论:(GCC) [Bug libstdc++/50724] New: isnan broken by -ffinite-math-only in g++(Mozilla) Bug 416287 - performance improvement opportunity with isNaN

【问题讨论】:

  • 我认为这取决于您使用的编译器和操作系统。在 OS X 上使用clang 5.1,您的示例代码将打印1
  • @SylvainDefresne:是的,但问题主要是:如何以始终有效的方式进行检查?
  • 打印std::log(0.0) 以查看std::logstd::isinf 是否计算出错误的结果可能会有所帮助。
  • 您的 my_isnan 不正确:my_isnan(45.0) == 1
  • @Jean-MichaëlCelerier 那是什么硬件?我猜my_isnan 仅适用于 x86-64 左右。

标签: c++ c++-standard-library fast-math


【解决方案1】:

请注意,-ffast-math 可能会使编译器忽略/违反 IEEE 规范,请参阅http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-Options

除了 -Ofast 之外的任何 -O 选项都不会打开此选项,因为它 可能导致依赖于精确的程序的输出不正确 为数学函数执行 IEEE 或 ISO 规则/规范。 但是,它可能会为不需要的程序生成更快的代码 这些规范的保证。

因此,使用-ffast-math 不能保证您在应该看到的地方看到无穷大。

特别是-ffast-math 开启-ffinite-math-only,见http://gcc.gnu.org/wiki/FloatingPointMath 表示(来自http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-Options

[...] 假设参数和结果不是 NaN 或 +-Infs 的浮点算术优化

这意味着,通过启用 -ffast-math,您向编译器承诺您的代码将永远不会使用无穷大或 NaN,这反过来又允许编译器通过例如替换对 isinf 的任何调用来优化代码或 isnan 由常量 false (并从那里进一步优化)。如果你违背了对编译器的承诺,编译器不需要创建正确的程序。

因此答案很简单,如果您的代码可能有无穷大或 NaN(这强烈暗示您使用isinfisnan),您不能启用-ffast-math,否则您可能会得到不正确的代码.

my_isnan 的实现(在某些系统上)有效,因为它直接检查浮点数的二进制表示。当然,处理器仍然可能进行(一些)实际计算(取决于编译器所做的优化),因此实际的 NaN 可能会出现在内存中,您可以检查它们的二进制表示,但如上所述,std::isnan 可能已经替换为常量false。同样也可能发生编译器将 sqrt 替换为甚至不会为输入 -1 生成 NaN 的版本。为了查看您的编译器做了哪些优化,请编译到汇编器并查看该代码。

做一个(不是完全不相关的)类比,如果你告诉你的编译器你的代码是 C++,你不能指望它正确编译 C 代码,反之亦然(有实际的例子,例如 @987654324 @)。

启用-ffast-math 并使用my_isnan 是个坏主意,因为这会使一切都非常依赖于机器和编译器,您不知道编译器整体做了哪些优化,因此可能存在其他相关的隐藏问题事实是您使用的是非有限数学,但要告诉编译器。

一个简单的解决方法是使用-ffast-math -fno-finite-math-only,它仍然会提供一些优化。

也可能是您的代码看起来像这样:

  1. 过滤掉所有无穷大和 NaN
  2. 对过滤后的值进行一些有限数学运算(我的意思是保证永远不会产生无穷大或 NaN 的数学运算,这必须非常非常仔细地检查)

在这种情况下,您可以拆分您的代码并使用优化#pragma__attribute__ 为给定的代码段有选择地打开和关闭-ffast-math(分别为-ffinite-math-only-fno-finite-math-only)(但是,我记得与此相关的某些版本的 GCC 存在一些问题)或者只是将您的代码拆分为单独的文件并使用不同的标志编译它们。当然,如果您可以隔离可能出现无穷大和 NaN 的部分,这也适用于更一般的设置。如果您无法隔离这些部分,则强烈表明您不能在此代码中使用-ffinite-math-only

最后,重要的是要了解-ffast-math 并不是一种无害的优化,它只会让您的程序更快。它不仅会影响代码的性能,还会影响代码的正确性(如果我没记错的话William Kahan 在他的主页上有一系列恐怖故事,另请参阅@987654326 @)。简而言之,您可能会得到更快的代码,但也会得到错误或意外的结果(参见下面的示例)。因此,只有在您真正知道自己在做什么并且绝对确定时,您才应该使用此类优化

  1. 优化不会影响特定代码的正确性,或者
  2. 优化引入的错误对代码并不重要。

程序代码的行为实际上可能完全不同,具体取决于是否使用此优化。特别是当启用诸如-ffast-math 之类的优化时,它的行为可能会出错(或至少与您的预期相反)。以下面的程序为例:

#include <iostream>
#include <limits>

int main() {
  double d = 1.0;
  double max = std::numeric_limits<double>::max();
  d /= max;
  d *= max;
  std::cout << d << std::endl;
  return 0;
}

在没有任何优化标志的情况下编译时将按预期生成输出1,但使用-ffast-math,它将输出0

【讨论】:

  • 我已经扩展了我的问题。为什么my_isnan 有效,而std::isnan 无效?
  • 所以,一个后续问题:-ffast-mathstd::sqrt(-1.0)std::log(0.0) 的结果是什么。它是否变得未指定,还是应该是 NaN / -Inf?
  • @Albert:它恢复到标准,即未定义行为 - 任何事情都可能发生。 “未指定”太强了,
  • 这是未定义的,因为使用 std::sqrt(-1.0) 您打破了仅使用有限值的要求,即没有无穷大或 NaN。可能会发生,编译器仍然为sqrt 生成“标准”代码并且实际计算产生-inf 表示的二进制文件,但也可能发生编译器使用无法处理输入@ 的sqrt 的一些不同实现987654367@。事实上,我的 GCC 版本在启用 -ffast-math 时会生成 call __sqrt_finite 而不是 call sqrt(虽然后者的行为似乎与前者相似,但不一定如此)。
  • __isnan__isinf 怎么样。他们不应该一直工作吗?
猜你喜欢
  • 2011-11-07
  • 2021-11-26
  • 2013-08-14
  • 2015-09-01
  • 1970-01-01
  • 2018-08-03
  • 2017-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多