I stumbled across this one here in 2023.
Things haven’t improved much.
- C11 supports
nan*()
functions (if QNaN is supported on your target processor), but
- MSVC 2022 does not actually implement payload compiling
- Payload must specified as a string anyway, and
- There is still no Standard way to get the data.
- (C23 proposes the GNU extension
getPayload()
, but it returns yet another double
, which is far less interesting than an integer would have been.)
However
It has always been possible to get a QNaN payload, assuming you have a proper IEEE 754 QNaN with payload data. It has been put to good use on systems that do in things like Javascript and Lua, for example.[citation needed]
According to Wikipedia, after discussing some dinosaurs: [link]
It may therefore appear strange that the widespread IEEE 754 floating-point standard does not specify endianness.[3] Theoretically, this means that even standard IEEE floating-point data written by one machine might not be readable by another. However, on modern standard computers (i.e., implementing IEEE 754), one may safely assume that the endianness is the same for floating-point numbers as for integers, making the conversion straightforward regardless of data type. Small embedded systems using special floating-point formats may be another matter, however.
Emphasis added
So as long as you aren’t leaking abstractions outside of internal use or playing with specialized (or ancient) hardware then you should be good to play with stuffing stuff in your QNaNs.
As this question is tagged C++
we will have to resort to slightly uglier code than strictly necessary in C
, as type-punning with a union is (probably) UB in C++.[more link] The following should work in both C and C++ and produce just as well-optimized code either way.
Da codez or go home
qnan.h
#ifndef QNAN_H
#define QNAN_H
// Copyright stackoverflow.com
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt )
#include <assert.h>
#include <math.h>
#include <stdint.h>
#ifndef NAN
#error "IEEE 754 Quiet NaN is required."
#endif
#ifndef UINT64_MAX
#error "uint64_t required."
#endif
static_assert( sizeof(double) == 8, "IEEE 754 64-bit double-precision is required" );
double qnan ( unsigned long long payload );
unsigned long long qnan_payload ( double qnan );
#endif
qnan.c
// Copyright stackoverflow.com
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt )
#include <string.h>
#include "qnan.h"
double
qnan( unsigned long long payload )
{
double qnan = NAN;
uint64_t n;
memcpy( &n, &qnan, 8 );
n |= payload & 0x7FFFFFFFFFFFFULL;
memcpy( &qnan, &n, 8 );
return qnan;
}
unsigned long long
qnan_payload( double qnan )
{
uint64_t n;
memcpy( &n, &qnan, 8 );
return n & 0x7FFFFFFFFFFFFULL;
}
These two functions allow you access to all 51 bits of payload data as an unsigned integer.
Note, however, that unlike the weird-o getPayload()
function the qnan_payload()
function does not bother to fact-check you about your choice of input — it assumes you have given it an actual QNaN.
If you are unsure what kind of double
you have, the isnan()
function from <math.h>
works just fine to check for QNaN-ness.
Similar code will give you access to a four-byte float
or a N-byte long double
(which is probably just an 8-byte double
, unless it isn’t, and is probably more trouble supporting than it’s worth).