C++ std::isinf不能与-ffast math一起使用。如何检查无穷大
示例代码:C++ std::isinf不能与-ffast math一起使用。如何检查无穷大,c++,c++-standard-library,fast-math,C++,C++ Standard Library,Fast Math,示例代码: #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; } int m
#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;
}
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;
}
#包括
#包括
#包括
使用名称空间std;
静态布尔值my_isnan(双val){
并{double f;uint64_t x;}u={val};
返回(u.x 0x7FF0000000U;
}
int main(){
cout注意,-ffast math
可能会使编译器忽略/违反IEEE规范,请参阅:
除了-Ofast之外,任何-O选项都不会启用此选项,因为它
可能会导致依赖于精确表达式的程序输出不正确
数学函数IEEE或ISO规则/规范的实施。
但是,它可能会为那些不需要代码的程序生成更快的代码
这些规范的保证
因此,使用-ffast math
并不能保证在应该看到的地方看到无穷大
特别是,-ffast math
仅启用-ffinite math
,请参见其中的含义(从)
[…]假定参数和结果不是NAN或+-INF的浮点算术优化
这意味着,通过启用-ffast math
,您向编译器承诺您的代码永远不会使用无穷大或NaN,这反过来允许编译器优化代码,例如,将对isinf
或isnan
的任何调用替换为常量false
(并从那里进一步优化).如果您违背了对编译器的承诺,编译器不需要创建正确的程序
因此答案很简单,如果您的代码可能有无穷大或NaN(这一点由您使用isinf
和isnan
这一事实强烈暗示),则无法启用-ffast math
,否则您可能会得到错误的代码
您对my_isnan的实现(在某些系统上)是有效的,因为它直接检查浮点数的二进制表示形式。当然,处理器仍可能进行(某些)实际计算(取决于编译器进行的优化),因此实际的NaN可能会出现在内存中,您可以检查它们的二进制表示形式,但如上所述,std::isnan
可能已被常量false
所取代。编译器也可能会用一些甚至不为输入生成NaN的版本来代替,例如,sqrt
-1
。要查看编译器进行了哪些优化,请编译到汇编程序并查看该代码
为了做出一个(不是完全无关的)类比,如果你告诉编译器你的代码是C++,你不能期望它正确编译C代码,反之亦然(有实际的例子,例如)。
启用
-ffast math
并使用my_isnan
是一个坏主意,因为这将使一切都非常依赖于机器和编译器。您不知道编译器总体上做了哪些优化,因此可能存在与使用非有限数学相关的其他隐藏问题,但会告诉编译器其他情况
一个简单的修复方法是使用-ffast math-fno finite math only
,这仍然会提供一些优化
也可能是您的代码如下所示:
过滤掉所有的无穷和非
对过滤后的值做一些有限数学(我指的是保证永远不会产生无穷大或N的数学,必须非常非常仔细地检查)
在这种情况下,您可以拆分代码,并使用optimize#pragma
或uuu属性uuu
为给定代码段选择性地打开或关闭-ffast math
(分别为-ffinite math only
和-fno finite math only
)(然而,我记得与此相关的GCC版本存在一些问题)或者只需将代码拆分为单独的文件,并使用不同的标志编译它们。当然,如果可以隔离可能出现无穷大和N的部分,这也适用于更一般的设置。如果无法隔离这些部分,这强烈表明您不能对此代码使用-ffinite math only
最后,重要的是要理解,-ffast math
不是一种无害的优化,它只会使程序更快。它不仅影响代码的性能,而且影响代码的正确性(如果我还记得right的主页上有一系列恐怖故事的话,这是所有关于浮点数的问题中最重要的一个。简言之,你可能会得到更快的代码,但也会得到错误或意外的结果(见下面的示例)。因此,只有当您真正了解自己在做什么,并且您已经完全确定这两种情况时,才应该使用此类优化
优化不会影响特定代码的正确性,或者
优化引入的错误对代码来说并不重要
根据是否使用此优化,程序代码的行为实际上可能完全不同。特别是当启用优化(如-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;
}
#包括
#包括
int main(){
双d=1.0;
double max=std::numeric_limits::max();
d/=最大值;
d*=最大值;
std::我想这可能取决于编译器,也可能取决于您使用的操作系统。在OSX上使用clang
5.1,您的示例代码将打印1
@SylvainDefresne:是的,但问题主要是:如何以始终有效的方式进行检查?打印std::log(0.0)可能会有所帮助
查看std::log
或std::isinf
是否计算了错误的结果。我对我的问题进行了一点扩展。为什么my_isnan
有效,而std::isnan
无效?那么,接下来的问题是:-ffast math
的计算结果是什么