33

What's the best way to use NaNs in C++?

I found std::numeric_limits<double>::quiet_NaN() and std::numeric_limits<double>::signaling_NaN(). I'd like to use signaling_NaN to represent an uninitialized variable as follows:

double diameter = std::numeric_limits<double>::signaling_NaN();

This, however, signals (raises an exception) on assignment. I want it to raise an exception on use, not on assignment.

Is there any way to use signaling_NaN without raising an exception on assignment? Is there a good, portable alternative to signaling_NaN that will raise a floating point exception when used?

Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
  • hmm... I'm playing with this because I'm curious now but I couldn't get mine to raise an exception. What did you do to get the exception? – Jeffrey Martinez Oct 25 '08 at 00:08
  • @JeffreyMartinez It's not a normal C++ exception, if that's what you're thinking. It's a floating point exception: see the notes [here](http://en.cppreference.com/w/cpp/numeric/fenv). – bames53 Nov 05 '12 at 15:55

6 Answers6

11

After looking into this some more, it looks like signaling_NaN is useless as provided. If floating point exceptions are enabled, then calling it counts as processing a signaling NaN, so it immediately raises an exception. If floating point exceptions are disabled, then processing a signaling NaN automatically demotes it to a quiet NaN, so signaling_NaN doesn't work either way.

Menkboy's code works, but trying to use signaling NaNs runs into other problems: there's no portable way to enable or disable floating point exceptions (as alluded to here and here), and if you're relying on exceptions being enabled, third party code may disable them (as described here).

So it seems like Motti's solution is really the best choice.

Community
  • 1
  • 1
Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
9

What signaling NAN means is that when the CPU encounters it a signal is fired, (hence the name). If you want to detect uninitialized variables then raising the warning level on your compiler usually detects all paths that use uninitalized values. Failing that you can use a wrapper class that stores a boolean saying if the value is initialized:

template <class T>
class initialized {
    T t;
    bool is_initialized;
public:
    initialized() : t(T()), is_initialized(false) { }
    initialized(const T& tt) : t(tt), is_initialized(true) { }
    T& operator=(const T& tt) { t = tt; is_initialized = true; return t; }
    operator T&() {
         if (!is_initialized)
             throw std::exception("uninitialized");
         return t; 
   }
};
Motti
  • 110,860
  • 49
  • 189
  • 262
  • 4
    `boost::optional` is a good alternative to this, and works well for return values, local variables, and members. Further, it doesn't call the default constructor, and works for the most part with reference types. – rvalue Dec 08 '11 at 03:06
3

Simple answer: Do something like this in the header file and use it everywhere else:

#define NegativeNaN log(-1)

If you wish to do some kind of manipulations on them better write some extended wrapper function around exp() like extended_exp() and so on!

Community
  • 1
  • 1
Rahul
  • 132
  • 5
3

You can write a signalling NaN into a variable without triggering an exception with something like this (nb: untested)

void set_snan( double &d )
{
    long long *bits = (long long *)&d;
    *bits = 0x7ff0000080000001LL;
}

It'll work most places, but no, it's not 100% portable.

Menkboy
  • 1,595
  • 10
  • 12
3

Well, looking after the definition of both quiet and signaling NaN, I can't really make out any difference.

You could use the code that is used in those functions yourself, maybe it prevents an exception that way, but seeing no exception in those two functions, I think it might be related to something else.

If you want to directly assign the NaN:

double value = _Nan._Double;
HS.
  • 2,593
  • 2
  • 20
  • 28
0

Your C++ implementation may have an API for accessing the floating point environment to test for and clear certain floating point exceptions. See my answer to a related question for more information.

Community
  • 1
  • 1
jwfearn
  • 28,781
  • 28
  • 95
  • 122