## ยง NaN punning: Storing integers in doubles in JavaScript

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/NaN
union 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 bits
Box 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;
}