可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
While writing some test cases, and some of the tests check for the result of a NaN.
I tried using std::isnan
but the assert failes:
Assertion `std::isnan(x)' failed.
After printing the value of x
, it turned out it's negative NaN (-nan
) which is totally acceptable in my case.
After trying to use the fact that NaN != NaN
and using assert(x == x)
, the compiler does me a 'favor' and optimises the assert away.
Making my own isNaN
function is being optimised away as well.
How can I check for both equality of NaN and -NaN?
回答1:
This is embarrassing.
The reason the compiler (GCC in this case) was optimising away the comparison and isnan
returned false
was because someone in my team had turned on -ffast-math
.
From the docs:
-ffast-math
Sets -fno-math-errno, -funsafe-math-optimizations,
-fno-trapping-math, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and fcx-limited-range.
This option causes the preprocessor macro __FAST_MATH__ to be defined.
This option should never be turned on by any -O option since it can result in incorrect output for programs which depend on an exact implementation of IEEE or ISO rules/specifications for math functions.
Notice the ending sentence - -ffast-math
is unsafe.
回答2:
isnan()
is expected to have undefined behaviour with -ffast-math
.
This is what I use in my test suite:
#if defined __FAST_MATH__
# undef isnan
#endif
#if !defined isnan
# define isnan isnan
# include <stdint.h>
static inline int isnan(float f)
{
union { float f; uint32_t x; } u = { f };
return (u.x << 1) > 0xff000000u;
}
#endif
回答3:
This looks like a bug in your library's implementation of isnan()
to me. It works fine here on gcc 4.2.1 on Snow Leopard. However, what about trying this?
std::isnan(std::abs(yourNanVariable));
Obviously, I can't test it, since std::isnan(-NaN)
is true
on my system.
EDIT: With -ffast-math
, irrespective of the -O
switch, gcc 4.2.1 on Snow Leopard thinks that NAN == NAN
is true
, as is NAN == -NAN
. This could potentially break code catastrophically. I'd advise leaving off -ffast-math
or at least testing for identical results in builds using and not using it...
回答4:
There's C99 isnan() which you should be able to use.
If in your implementation it does not work correctly (which one is that?) you can implement your own, by reinterpret_casting to long and doing IEEE bit magic.
回答5:
You can check the bits of number. IEEE 754 has defined mask for NaN:
- A signaling NaN is represented by any bit pattern between X'7F80 0001' and X'7FBF FFFF' or between X'FF80 0001' and X'FFBF FFFF'.
- A quiet NaN is represented by any bit pattern between X'7FC0 0000' and X'7FFF FFFF' or between X'FFC0 0000' and X'FFFF FFFF'.
This might be not portable, but if you are sure about your platfofm it can be acceptable. More: http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlf101l.doc/xlfopg/fpieee.htm
回答6:
This is based off the wikipedia article posted in the comments. Note that it's entirely untested -- it should give you an idea of something you can do though.
bool reallyIsNan(float x)
{
//Assumes sizeof(float) == sizeof(int)
int intIzedX = *(reinterpret_cast<int *>(&x));
int clearAllNonNanBits = intIzedX & 0x7F800000;
return clearAllNonNanBits == 0x7F800000;
}
EDIT: I really think you should consider filing a bug with the GLibc guys on that one though.