This is a technique that allows us to store data inside a double by
punning the value of a NaN. This is used inside javascript engines
to represent all possible user types as a single NaN.
This is well explained at the SpiderMonkey internals page
#include <assert.h>#include <iostream>#include <limits>using namespace std;// https://en.wikipedia.org/wiki/NaNunion PunDouble { double d; struct { uint64_t m : 51; uint32_t qnan: 1; uint32_t e : 11; uint32_t s : 1; } bits; PunDouble(double d) : d(d) {}; PunDouble(uint32_t s, uint32_t e, uint64_t m) { bits.s = s; bits.e = e; bits.qnan = 1; bits.m = m; }};union PunInt { int32_t i; uint32_t bits; PunInt(int32_t i): i(i) {};};using namespace std;struct Box { inline bool is_int() const { auto pd = PunDouble(d); return pd.bits.e == 0b11111111111 && pd.bits.qnan == 1; } inline bool isdouble() const { auto pd = PunDouble(d); return (pd.bits.e != 0b11111111111) || (pd.bits.qnan == 0); } int32_t get_int() const { assert(is_int()); uint64_t m = PunDouble(d).bits.m; return PunInt(m).i; } double get_double() const { assert(isdouble()); return d; } Box operator +(const Box &other) const; static Box mk_int(int32_t i) { return Box(PunDouble(1, 0b11111111111, PunInt(i).bits).d); } static Box mk_double(double d) { return Box(d); } double rawdouble() const { return d; } private: double d; Box(double d) : d(d) {}};// = 64 bitsBox Box::operator + (const Box &other) const { if (isdouble()) { assert(other.isdouble()); return Box::mk_double(d + other.d); } else { assert(is_int()); assert(other.is_int()); return Box::mk_int(get_int() + other.get_int()); }}ostream &operator << (ostream &o, const Box &b) { if (b.isdouble()) { return o << "[" << b.get_double() << "]"; } else { return o << "[" << b.get_int() << "]"; }}int32_t randint() { return (rand() %2?1:-1) * (rand() % 100); }int32_t main() { // generate random integers, check that addition checks out srand(7); for(int32_t i = 0; i < 1000; ++i) { const int32_t i1 = randint(), i2 = randint(); const Box b1 = Box::mk_int(i1), b2 = Box::mk_int(i2); cout << "i1:" << i1 << " b1:" << b1 << " b1.double:" << b1.rawdouble() << " b1.get_int:" << b1.get_int() << "\n"; cout << "i2:" << i2 << " b2:" << b2 << " b2.double:" << b2.rawdouble() << " b2.get_int:" << b2.get_int() << "\n"; assert(b1.is_int()); assert(b2.is_int()); assert(b1.get_int() == i1); assert(b2.get_int() == i2); Box b3 = b1 + b2; assert(b3.is_int()); assert(b3.get_int() == i1 + i2); } for(int32_t i = 0; i < 1000; ++i) { const int32_t p1 = randint(), q1=randint(), p2 = randint(), q2=randint(); const double d1 = (double)p1/(double)q1; const double d2 = (double)p2/(double)q2; const Box b1 = Box::mk_double(d1); const Box b2 = Box::mk_double(d2); cout << "d1: " << d1 << " | b1: " << b1 << "\n"; cout << "d2 " << d2 << " | b2: " << b2 << "\n"; assert(b1.isdouble()); assert(b2.isdouble()); assert(b1.get_double() == d1); assert(b2.get_double() == d2);; Box b3 = b1 + b2; assert(b3.isdouble()); assert(b3.get_double() == d1 + d2); } return 0;}