std::isinf不适用于-fast数学.如何检查无穷大
std::isinf does not work with -ffast-math. how to check for infinity
示例代码:
#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;
}
在线编译器。
利用-ffast-math
;0,0,1,1"——如果没有;1,1,1";。
这是正确的吗?我认为std::isinf
/std::isnan
在这些情况下仍然应该与-ffast-math
一起工作。
此外,如何使用-ffast-math
检查无穷大/NaN?您可以看到my_isnan
在做这件事,它确实有效,但该解决方案当然非常依赖于体系结构。此外,为什么my_isnan
在这里工作,而std::isnan
不工作?那么__isnan
和__isinf
呢。他们总是工作吗?
对于-ffast-math
,std::sqrt(-1.0)
和std::log(0.0)
的结果是什么。它是未定义的,还是应该是NaN/-Inf?
相关讨论:(GCC)[Bug libstdc++/50724]新增:是否仅在g++中被finite数学打破,(Mozilla)Bug 416287-isnan 的性能改进机会
注意-ffast-math
可能会使编译器忽略/违反IEEE规范,请参阅http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-选项:
除了-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-选项)
[…]浮点运算优化,假设参数和结果不是NaNs或+-Infs
这意味着,通过启用-ffast-math
,您向编译器承诺您的代码永远不会使用无穷大或NaN,这反过来又允许编译器优化代码,例如,用常量false
替换对isinf
或isnan
的任何调用(并从中进一步优化)。如果你违背了对编译器的承诺,编译器就不需要创建正确的程序。
因此,答案很简单,如果你的代码可能有无穷大或NaN(这是由你使用isinf
和isnan
的事实强烈暗示的),你就不能启用-ffast-math
,否则你可能会得到错误的代码。
my_isnan
的实现可以工作(在某些系统上),因为它直接检查浮点数的二进制表示。当然,处理器仍然可能进行(一些)实际计算(取决于编译器所做的优化),因此实际的NaN可能会出现在内存中,您可以检查它们的二进制表示,但如上所述,std::isnan
可能已被常量false
取代。同样可能发生的情况是,编译器将sqrt
替换为甚至不为输入-1
生成NaN的某个版本。为了查看编译器进行了哪些优化,请编译到汇编程序并查看代码。
为了进行一个(并非完全无关的)类比,如果你告诉编译器你的代码是用C++编写的,你就不能指望它正确地编译C代码,反之亦然(有一些实际的例子,例如,在C和C++中都有效的代码在用每种语言编译时会产生不同的行为吗?)。
启用-ffast-math
并使用my_isnan
是个坏主意,因为这会使一切都非常依赖于机器和编译器。你不知道编译器总体上做了什么优化,所以可能还有其他隐藏的问题,因为你使用的是非有限数学,但告诉编译器其他的。
一个简单的修复方法是使用-ffast-math -fno-finite-math-only
,它仍然会提供一些优化。
也可能是你的代码看起来像这样:
- 过滤掉所有的无穷大和NaN
- 对过滤后的值进行一些有限的数学运算(我指的是保证永远不会创建无穷大或NaN的数学运算,这必须经过非常仔细的检查)
在这种情况下,您可以拆分代码,并使用优化#pragma
或__attribute__
为给定的代码段选择性地打开和关闭-ffast-math
(分别为-ffinite-math-only
和-fno-finite-math-only
)(然而,我记得与此相关的GCC的某些版本存在一些问题),或者只需将代码拆分为单独的文件,并使用不同的标志进行编译。当然,如果可以隔离可能出现无穷大和NaN的部分,这也适用于更通用的设置。如果您不能隔离这些部分,这强烈表明您不能将-ffinite-math-only
用于此代码。
最后,重要的是要理解-ffast-math
并不是一个无害的优化,它只是让你的程序更快。它不仅会影响代码的性能,还会影响代码的正确性(除此之外,如果我没记错的话,William Kahan在他的主页上有一系列恐怖故事,请参阅每个程序员都应该知道浮点运算的内容)。简而言之,您可能会得到更快的代码,但也会得到错误或意外的结果(请参阅下面的示例)。因此,只有当你真正知道自己在做什么,并且你已经绝对确定
- 优化不会影响特定代码的正确性,或者
- 优化引入的错误对代码来说并不重要
根据是否使用此优化,程序代码的行为实际上可能截然不同。特别是,当启用诸如-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
。
- valgrind-hellgrind与泄漏检查的结果不同
- C++模板来检查友元函数的存在
- 检查输入是否不是整数或数字
- 试图让变量检查数组中的某些内容
- 检查值是否在集合p1和p2中,但不在p3中
- C++概念:如何使用'concept'检查模板化结构的属性?
- 概念TS检查忽略私有访问修饰符
- 检查 std::shared_ptr<> 的当前底层类型是否为 T
- 在c++中检查长方体是否尽可能快地重叠(无迭代)
- 如何在C++中检查2D数组中负值的输入验证
- C++:正在检查LinkedList中的回文-递归方法-错误
- 使用for循环检查数组中的重复项
- 如何检查一个c++字符串中有多少相同的字符/数字
- 检查不带转换的扫描格式
- 如何检查线程是否锁定
- 清除前检查矢量
- 在C++中检查二重是否等于负无穷大的最佳方法
- 你能安全地检查无穷大的符号吗
- 如何正确检查一个值是否为无穷大或在c++(msvc2010)
- std::isinf不适用于-fast数学.如何检查无穷大