To understand what the string parameter to the std::nan
function is really intended to be used for, let's consider a different example:
#include <cmath>
#include <iostream>
int main()
{
const double d = std::nan("123");
uint64_t u;
assert(sizeof(u) == sizeof(d));
memcpy(&u, &d, sizeof(d));
std::cout << "d = " << d << ", hex = " << std::hex << u << std::endl;
unsigned int sign = u >> 63;
unsigned int exponent = (u >> 52) & 0x7ff;
uint64_t significand = u & 0xfffffffffffffLL;
std::cout << "sign: " << sign;
std::cout << ", exp: " << exponent;
std::cout << ", signif: " << significand << std::endl;
}
The first line printed by this program is
d = nan, hex = 7ff800000000007b
showing that the number in d
is indeed a NaN, with a hexadecimal representation of 7ff800000000007b
.
The rest of the program extracts the sign, exponent, and significant portions of the number, assuming IEEE-754 double-precision format, with 11 bits for the sign and 52 bits for the fractional significand. The second line printed is
sign: 0, exp: 7ff, signif: 800000000007b
indicating that those components are 0
, 0x7ff
, and 0x800000000007b
, respectively.
Since the exponent is all 1's, this is a special number.
Since the significand is nonzero, this is a NaN. (If the significand were zero, this would be an infinity.)
And then, since there are 252-1 different ways for the significand to be nonzero, we can interpret the significand value as the "payload" of the NaN. So, with that thought in mind, what could the value 0x800000000007b
mean?
Let's ignore the leading 8
for the moment. The remaining part is hexadecimal 7b
, or in decimal... 123.
That's right, when you say std::nan("123")
, you're setting the payload of the NaN. (In most implementations, I believe you can also do so directly in hexadecimal: std::nan("0x7b")
. Or probably std::nan("0173")
, for that matter.)
And then the leading 8
(which is the high-order bit of the 52-bit field) says that this is a "quiet" as opposed to a "signaling" NaN. Quiet NaNs are usually what you want, so evidently std::nan()
sets this for you automatically, whether you ask for it or not. (I'm not sure whether it's possible to use std::nan()
to construct a signaling NaN.)
See What is the difference between quiet NaN and signaling NaN? for more on that point.
Calling std::nan("Hello")
, on the other hand, doesn't mean much — the payload is always intended to be numeric. When I tried it, std::nan("Hello")
was, not too surprisingly, essentially equivalent to std::nan("0")
.
(Since there are 51 or 52 payload bits available, as pointed out in a comment, you could theoretically jam several actual characters in there. One possible way would be std::nan("0x48656c6c6f")
, although besides going "around the barn", it's kinda big-endian.)
These "NaN payloads" are, at least in my experience, a relatively unknown and little-used aspect of the IEEE 754 standard. What were they intended to be used for? One example is that, since there are quite a few bits available, it is possible that, upon encountering certain kinds of exceptional floating-point conditions, a CPU could actually insert the current Program Counter value as the payload of the resulting NaN, to aid in later debugging.
Anyway, in answer to this question's title, the only way I know of to get a NAN's embedded "payload" back is to use IEEE-754-specific bit manipulations, as I've demonstrated here. AFAIK, there are no Standard or portable or machine-independent facilities for doing so.
std::variant<double, ...>
. So, a NAN could also communicate why it's nan: because of an error in a computation, because it represents missing data, because it represents data that has yet to be computed, or whatever other necessity arises. Storing an integer in that space would also work, of course. – Enlargementdouble
has 51 bits available for a NaN payload, which is enough to encode the OP’s string “Hello”. – Poul'H', 'e', 'l', 'l', 'o', 0x00, 0x00, 0x00
, and itsdouble
member prints as a NaN, but printing the first five bytes yields “Hello”. If you got 2.36e-312, you did something different. – Poul