// nan_discussion.cc
// cxe- Mon Feb 23 16:43:39 PST 2004
// When I was writing a vector geometry library, I encountered a very
// strange problem. While performing some math operations, I kept
// getting errors which I finally tracked down to one of my functions
// which was supposed to return an angle. Instead of producing an
// angle, it was giving me the apparent character string "nan". It
// turns out that "nan" is actually a numerical value and has no
// character properties at all.
// The value "nan" stands for "not a number". This value is something
// C uses to deal with difficult situations during computations.
// There is a related value called "inf" which stands for "infinity".
// Mostly these situations relate to division by zero situations.
// Here's how to produce them:
// 1 / zero = inf
// zero / zero = nan
// -1 / zero = -inf
// It's surprising that C doesn't always simply produce an automatic
// error when dividing by zero. Actually, C has a double standard
// because it will let your program calculate an inf, but the compiler
// will flag any blatant division by zero attempts (like
// "float x=3/0;"). Of course, if inf shows up in your program, there
// will probably be a related error soon enough.
// In my program, I had specifically precluded division by zero
// conditions by checking the denominators for zero before doing
// anything. What I didn't check for was very teensy weensy
// denominators. It turns out that very tiny denomenators can even
// cause results to equal "inf".
// That would have been fine, I could have checked for that, but what
// had me confused was the nan. Where did this come from? It turns out
// that if you send an inf to the acos function, the result is a nan.
// The most fascinating thing about nan is how to detect it. For
// complicated reasons, nan does not equal anything, including itself.
// This means that you can check for a nan like this:
// if ( !(x==x) ) cout << "Must be a nan!" << endl;
// For more information on the topic, here's the last word:
// http://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html
#include <iostream>
#include <cmath>
int main () {
float x, y, y2, s, s2, zero=0;
// Note the very unusual conditional. It is very rare to see a finite
// loop with something like this.
for ( x=1, y=1, y2=1; s==s; x /= 100, y /=100) {
// x and y get small together while y2 stays big.
s = y/x; s2=y2/x;
cout << "X=" << x << " Y1=" << y << " Y2=" << y2
<< " X/Y1=" << s << " X/Y2=" << s2 << endl;
}
cout << "---------------------------------" << endl;
// More fun....
cout << "---- incomparable nan ---" << endl;
// The fact that nan is unwilling to compare itself to anything is a
// clue how to spot it. If something doesn't seem to equal itself,
// then it probably is a nan.
cout << "s=" << s << endl;
if (!( s == s )) // !!!!!! VERY SURPRISING !!!!!!
cout << "(s == s) returns a false!!!" << endl;
if (!( s <= s ) || !( s >= s ))
cout << "s is neither lte nor gte to itself" << endl;
if (!( s < s ) || !( s > s ))
cout << "s is neither less nor greater than itself" << endl;
cout << "---- isnan() function test ---" << endl;
// The isnan() function lives in the standard math library.
if (isnan(s)) cout << "isnan(s) function is true" << endl;
if (!isnan(zero)) cout << "isnan(zero) is not true" << endl;
cout << "---- nan's pal inf ---" << endl;
// Here's nan's close friend, the inf.
cout << "1/zero=" << (1/zero) << endl;
// The inf has a negative twin.
cout << "1/-zero=" << (1/-zero) << endl;
// Here's a strange nan. One might expect zero, but no, it's nan.
cout << "(1/zero)-(1/zero)=" << (1/zero)-(1/zero) << endl; // = nan
cout << "---- classic nan ---" << endl;
// This is the classic textbook nan.
cout << "zero/zero=" << (zero/zero) << endl;
cout << "---- stubborn nan ---" << endl;
// Can't subtract it away.
cout << "(zero/zero)-(zero/zero)=" << (zero/zero)-(zero/zero) << endl;
// Even zero is powerless to change it.
cout << "(zero/zero)*zero=" << (zero/zero)*zero << endl;
cout << "---- acos nonsense ---" << endl;
// This kind of thing can be very insidious, trignometric nan.
cout << "acos(1/zero)=" << acos(1/zero) << endl;
cout << "acos(1/-zero)=" << acos(1/-zero) << endl;
return 0;
}
|