0x7fffffff
在内存中会被表示为ff ff ff 7f
。value
的排序方式与0x7f800000
相同,因此所有操作都是对齐的(没有字节交换)。如果有人能在大端平台上测试一下,我会很感兴趣。 - Bryan W. Wagner0x7fff1234
is also a NaN. So is 0xffffffff
- Steve Hollaschinline bool IsNan(float f)
{
const uint32 u = *(uint32*)&f;
return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF); // Both NaN and qNan.
}
inline bool IsNan(double d)
{
const uint64 u = *(uint64*)&d;
return (u&0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (u&0xFFFFFFFFFFFFFULL);
}
如果sizeof(int)
为4且sizeof(long long)
为8,则此方法有效。
在运行时,只有比较操作,强制转换不需要任何时间。它只是更改比较标志的配置以检查相等性。
memcpy
来确保通过字节数组进行转换,具体代码请参见我的第二个答案(https://dev59.com/YHRB5IYBdhLWcg3wn4YL#42138465)。 - Cheers and hth. - Alf不依赖于具体IEEE NaN表示的可能解决方案如下:
template<class T>
bool isnan( T f ) {
T _nan = (T)0.0/(T)0.0;
return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
考虑到在某些情况下(例如使用-ffast-math选项时),(x != x)不能总是保证为NaN,因此我一直在使用:
#define IS_NAN(x) (((x) < 0) == ((x) >= 0))
数字不能同时小于0和大于等于0,因此仅当数字既不小于零也不大于或等于零时,此检查才能通过。 这基本上不是任何数字,或NaN。
如果您喜欢,也可以使用以下内容:
#define IS_NAN(x) (!((x)<0) && !((x)>=0)
我不确定这是否会受到-ffast-math的影响,所以结果可能会有所不同。
f != f
一样有瑕疵。
我曾经看到 llvm 优化了几乎相同的代码。编译器可以传播第一个比较的信息,并找出如果第一个比较是真的,那么第二个比较可能永远不会成立。(如果编译器严格遵守 IEEE 规则,那么 f != f
也会简单得多) - Markus-ffast-math
选项。可以在 Visual C++ 中使用。请参见(https://dev59.com/YHRB5IYBdhLWcg3wn4YL#42138465)。 - Cheers and hth. - Alf对于我来说,解决方案可能是使用宏将其显式地内联,从而足够快。这也适用于任何浮点类型。它的基础是只有在值不是数字时才存在值不等于自身的情况。
#ifndef isnan
#define isnan(a) (a != a)
#endif
这是有效的:
#include <iostream>
#include <math.h>
using namespace std;
int main ()
{
char ch='a';
double val = nan(&ch);
if(isnan(val))
cout << "isnan" << endl;
return 0;
}
输出:isnan
。在我看来,最好的跨平台方法是使用联合并测试双精度浮点数的位模式以检查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;
}
union
在两种类型之间进行类型转换可能无法按预期工作(:sad_panda:)。正确的方法(尽管实际上并不像期望的那样可移植)是完全避免使用联合,并从double
执行memcpy到另一个uint64_t
变量,然后使用该辅助变量进行测试。 - Eljay测试NaN、无穷大和有限数字可以通过检查最大指数来轻松完成。无穷大是带零尾数的最大指数,NaN是带非零尾数的最大指数。指数存储在最高位符号位之后的下一位中,因此我们只需左移以去掉符号位并使指数成为最高位,不需要掩码(operator&
):
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; }
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;
}
ucomisd
指令在任意参数为 NaN 时设置奇偶标志位。这就是在未指定 -ffast-math
选项时 std::isnan
的工作原理。1
时,
且
尾数不为零,
该数是一个NaN
。
Double类型有1
个符号位,11
个指数位和52
个尾数位。
进行一次位检查。正如上面的评论所述,a!= a在g++和其他一些编译器中不起作用,但这个技巧应该可以。它可能不是很高效,但仍然是一种方法:
bool IsNan(float a)
{
char s[4];
sprintf(s, "%.3f", a);
if (s[0]=='n') return true;
else return false;
}
FLT_MAX
,则是340282346638528859811704183484516925440.000
,否则就是t'will
。他必须使用char s[7]; sprintf(s, "%.0g", a);
,这将是6个字符,如果a=-FLT_MAX
,则为-3e+38
。 - boboboboif(strchr(s,"nan")!=nullptr)
用作判断条件应该可以解决这个问题。 - Jakub Homola
0.f/0.f
的执行要比事后检查代码中的nan
要好得多。如果允许nan
不断蔓延,它会给你的程序带来严重破坏,并可能引入难以找到的 bug。这是因为nan
是有毒的,(5*nan
=nan
),nan
不等于任何东西 (nan
!=nan
),nan
不大于任何东西 (nan
!> 0),nan
不小于任何东西 (nan
!< 0)。 - bobobobo