是否存在isnan()函数?
注:我在MinGW(如果这有区别的话)。
我使用isnan()从<math.h>解决了这个问题,这在<cmath>中不存在,我一开始是#包括在内的。
是否存在isnan()函数?
注:我在MinGW(如果这有区别的话)。
我使用isnan()从<math.h>解决了这个问题,这在<cmath>中不存在,我一开始是#包括在内的。
当前回答
在x86-64上,您可以使用非常快速的方法来检查NaN和无穷大,不管- fast-math编译器选项如何,这些方法都可以正常工作。(f != f, std::isnan, std::isinf使用- fast-math总是产生false)。
NaN、无穷大和有限数的测试可以通过检查最大指数轻松完成。无穷大是最大指数和零尾数,NaN是最大指数和非零尾数。指数存储在最上面的符号位之后的下一位,这样我们就可以左移来去掉符号位,让指数成为最上面的位,不需要屏蔽(操作符&):
static inline uint64_t load_ieee754_rep(double a) {
uint64_t r;
static_assert(sizeof r == sizeof a, "Unexpected sizes.");
std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
return r;
}
static inline uint32_t load_ieee754_rep(float a) {
uint32_t r;
static_assert(sizeof r == sizeof a, "Unexpected sizes.");
std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
return r;
}
constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);
// The shift left removes the sign bit. The exponent moves into the topmost bits,
// so that plain unsigned comparison is enough.
static inline bool isnan2(double a) { return load_ieee754_rep(a) << 1 > inf_double_shl1; }
static inline bool isinf2(double a) { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1 < inf_double_shl1; }
static inline bool isnan2(float a) { return load_ieee754_rep(a) << 1 > inf_float_shl1; }
static inline bool isinf2(float a) { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a) { return load_ieee754_rep(a) << 1 < inf_float_shl1; }
isinf和isfinite的std版本从.data段加载2个double/float常量,在最坏的情况下,它们会导致2个数据缓存失败。上面的版本不加载任何数据,inf_double_shl1和inf_float_shl1常量被编码为立即操作数进入程序集指令。
更快的isnan2只是2个组装指令:
bool isnan2(double a) {
bool r;
asm(".intel_syntax noprefix"
"\n\t ucomisd %1, %1"
"\n\t setp %b0"
"\n\t .att_syntax prefix"
: "=g" (r)
: "x" (a)
: "cc"
);
return r;
}
如果任何参数为NaN,则使用ucomisd指令设置奇偶校验标志的事实。这就是在没有指定- fast-math选项时std::isnan的工作方式。
其他回答
根据IEEE标准,NaN值有一个奇怪的性质,涉及它们的比较总是假的。也就是说,对于浮点数f, f != f只有在f为NaN时才为真。
请注意,正如下面的一些评论所指出的,并不是所有编译器在优化代码时都尊重这一点。
对于任何声称使用IEEE浮点数的编译器,这个技巧都应该有效。但我不能保证它在实践中会起作用。如果有疑问,请检查编译器。
在我看来,最好的真正跨平台的方法是使用联合,并测试double的位模式来检查nan。
我还没有彻底测试这个解决方案,可能有一种更有效的方法来处理比特模式,但我认为它应该有效。
#include <stdint.h>
#include <stdio.h>
union NaN
{
uint64_t bits;
double num;
};
int main()
{
//Test if a double is NaN
double d = 0.0 / 0.0;
union NaN n;
n.num = d;
if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
{
printf("NaN: %f", d);
}
return 0;
}
南预防
我对这个问题的回答是不要对nan使用追溯检查。取而代之的是对表单0.0/0.0的划分使用预防性检查。
#include <float.h>
float x=0.f ; // I'm gonna divide by x!
if( !x ) // Wait! Let me check if x is 0
x = FLT_MIN ; // oh, since x was 0, i'll just make it really small instead.
float y = 0.f / x ; // whew, `nan` didn't appear.
Nan是0.f/0运算的结果。F,或0.0/0.0。Nan是代码稳定性的一个可怕的克星,必须非常小心地检测和防止1。nan不同于普通数的特性:
Nan是有毒的,(5* Nan = Nan) Nan不等于任何东西,甚至不等于它本身(Nan != Nan) Nan不大于任何东西(Nan !> 0) Nan不小于任何值(Nan !< 0)
最后列出的2个属性是反逻辑的,将导致依赖于与nan数比较的代码的奇怪行为(最后3个属性也很奇怪,但您可能永远不会看到x != x ?)在你的代码中(除非你在检查nan(不可靠))。
在我自己的代码中,我注意到nan值往往会产生难以发现的错误。(请注意,这不是inf或-inf的情况。(-inf < 0)返回TRUE, (0 < inf)返回TRUE,甚至(-inf < inf)返回TRUE。因此,在我的经验中,代码的行为通常仍然是理想的)。
在奶奶手下该怎么办
您希望在0.0/0.0下发生的事情必须作为特殊情况处理,但是您所做的事情必须取决于您期望从代码中得到的数字。
在上面的例子中,(0.f/FLT_MIN)的结果基本上是0。你可能想让0.0/0.0生成HUGE。所以,
float x=0.f, y=0.f, z;
if( !x && !y ) // 0.f/0.f case
z = FLT_MAX ; // biggest float possible
else
z = y/x ; // regular division.
在上面,如果x = 0。F, inf会导致(实际上如上所述,它具有相当好的/非破坏性行为)。
记住,整数除以0会导致运行时异常。所以你必须总是检查整数除以0。仅仅因为0.0/0.0悄悄地计算为nan并不意味着您可以偷懒,在它发生之前不检查0.0/0.0。
通过x != x检查nan有时是不可靠的(x != x被一些破坏IEEE遵从性的优化编译器剥离,特别是当- fast-math开关启用时)。
Boost中还提供了一个仅头文件的库,该库具有处理浮点数据类型的简洁工具
#include <boost/math/special_functions/fpclassify.hpp>
你会得到以下函数:
template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);
如果你有时间,那么看看Boost的整个数学工具包,它有许多有用的工具,并且正在快速增长。
此外,当处理浮点和非浮点时,查看数字转换可能是一个好主意。
当前c++标准库中没有可用的isnan()函数。它是在C99中引入的,并被定义为宏而不是函数。C99定义的标准库元素既不是当前c++标准ISO/IEC 14882:1998的一部分,也不是更新版ISO/IEC 14882:2003的一部分。
2005年提出了技术报告1。TR1为c++带来了与C99的兼容性。尽管它从未被正式采用成为c++标准,但许多实现(GCC 4.0+或Visual c++ 9.0+ c++实现)确实提供了TR1特性,全部或仅部分(Visual c++ 9.0不提供C99数学函数)。
如果TR1可用,那么cmath包含C99元素,如isnan(), isfinite()等,但它们被定义为函数,而不是宏,通常在std:: TR1:: namespace中,尽管许多实现(例如Linux上的GCC 4+或Mac OS X 10.5+上的XCode)将它们直接注入std::,因此std::isnan定义良好。
此外,c++的一些实现仍然使C99 isnan()宏对c++可用(通过cmath或math.h包含),这可能会引起更多的混淆,开发人员可能认为这是一种标准行为。
关于visualc++的一个注意事项,如上所述,它不提供std::isnan,也不提供std::tr1::isnan,但它提供了一个定义为_isnan()的扩展函数,该扩展函数自visualc++ 6.0以来一直可用
在XCode中,有更多的乐趣。如前所述,GCC 4+定义了std::isnan。对于旧版本的编译器和库形式的XCode,似乎(这里是相关的讨论),还没有机会检查自己)定义了两个函数,Intel上的__inline_isnand()和Power PC上的__isnand()。