Skip to content

Instantly share code, notes, and snippets.

@bollu
Created August 22, 2020 13:11
Show Gist options
  • Save bollu/c87c68da2c57bb725ae74ce172f73799 to your computer and use it in GitHub Desktop.
Save bollu/c87c68da2c57bb725ae74ce172f73799 to your computer and use it in GitHub Desktop.
Use NaN punning to box an int32_t and a double together
#include <assert.h>
#include <iostream>
#include <limits>
using namespace std;
#define INT32_TAG 0xfffffffe
// https://github.com/WebKit/webkit/blob/950143da027e80924b4bb86defa8a3f21fd3fb1e/Source/JavaScriptCore/runtime/JSCJSValueInlines.h
// https://github.com/WebKit/webkit/blob/950143da027e80924b4bb86defa8a3f21fd3fb1e/Source/JavaScriptCore/runtime/JSCJSValue.h
// 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment