Skip to content

Instantly share code, notes, and snippets.

@m1lkweed
Created June 23, 2023 05:09
Show Gist options
  • Save m1lkweed/6bce23faa0a9a894fbc3c79099a511fb to your computer and use it in GitHub Desktop.
Save m1lkweed/6bce23faa0a9a894fbc3c79099a511fb to your computer and use it in GitHub Desktop.
A quick and dirty nan boxing implementation
// NaN boxing in C
// (c)m1lkweed 2022
// GPLv3+
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#define MASK_TYPE (0x0007000000000000UL)
#define MASK_PAYLOAD (0x0000ffffffffffffUL)
#define SET_SNAN (0xfff4000000000000UL)
enum value_types {
TYPE_FLOAT = 1,
TYPE_INT,
TYPE_VDPTR,
TYPE_STR,
};
typedef union _var_u{
double as_double;
uint64_t as_long;
struct{
uint64_t payload:48;
enum value_types tag:4;
uint32_t mantissa:11;
uint8_t sign:1;
};
}var;
var int_to_var(int a){
var ret = {.payload = a & MASK_PAYLOAD};
ret.as_long &= MASK_PAYLOAD;
ret.as_long |= SET_SNAN;
ret.tag = TYPE_INT;
return ret;
}
var ptr_to_var(void *a){
var ret = {.payload = ((uint64_t)a) & MASK_PAYLOAD};
ret.as_long &= MASK_PAYLOAD;
ret.as_long |= SET_SNAN;
ret.tag = TYPE_VDPTR;
return ret;
}
var float_to_var(float a){
union{
float as_float;
uint32_t as_int;
}b = {a};
var ret = {.payload = b.as_int & MASK_PAYLOAD};
ret.as_long &= MASK_PAYLOAD;
ret.as_long |= SET_SNAN;
ret.tag = TYPE_FLOAT;
return ret;
}
var str_to_var(const char *a){
var ret = {.as_long = SET_SNAN};
ret.tag = TYPE_STR;
uint64_t str = 0;
for(size_t i = 0; (i < 7) && (i < strlen(a)); ++i)
str |= (uint64_t)a[i] << (8 * i);
ret.as_long |= str & MASK_PAYLOAD;
return ret;
}
int var_to_int(var a){
return a.payload & MASK_PAYLOAD;
}
void *var_to_ptr(var a){
uint64_t ret = a.as_long;
ret &= MASK_PAYLOAD;
return (void*)ret;
}
float var_to_float(var a){
union{
uint32_t as_int;
float as_float;
}b = {
var_to_int(a)
};
return b.as_float;
}
void var_to_str(var a, char *out){
uint64_t str = a.payload;
for(int i = 0; i < 7; ++i)
out[i] = (str >> 8 * i)& 0xff;
}
[[gnu::always_inline]] extern inline bool is_int(var a){
return (a.as_long & MASK_TYPE) == ((uint64_t)TYPE_INT << 48);
}
[[gnu::always_inline]] extern inline bool is_ptr(var a){
return (a.as_long & MASK_TYPE) == ((uint64_t)TYPE_VDPTR << 48);
}
[[gnu::always_inline]] extern inline bool is_float(var a){
return (a.as_long & MASK_TYPE) == ((uint64_t)TYPE_FLOAT << 48);
}
[[gnu::always_inline]] extern inline bool is_str(var a){
return (a.as_long & MASK_TYPE) == ((uint64_t)TYPE_STR << 48);
}
var add_var(var a, var b){
switch((a.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_float(a) + var_to_float(b));
case TYPE_INT:
return float_to_var(var_to_float(a) + var_to_int(b));
}
case TYPE_INT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_int(a) + var_to_float(b));
case TYPE_INT:
return int_to_var(var_to_int(a) + var_to_int(b));
case TYPE_VDPTR:
return ptr_to_var((char*)var_to_ptr(b) + var_to_int(a));
}
case TYPE_VDPTR:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_INT:
return ptr_to_var((char*)var_to_ptr(a) + var_to_int(b));
case TYPE_VDPTR:
return ptr_to_var((char*)var_to_ptr(a) + (long)var_to_ptr(b));
}
default:
return ptr_to_var((void*)-1UL);
}
}
var sub_var(var a, var b){
switch((a.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_float(a) - var_to_float(b));
case TYPE_INT:
return float_to_var(var_to_float(a) - var_to_int(b));
}
case TYPE_INT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_int(a) - var_to_float(b));
case TYPE_INT:
return int_to_var(var_to_int(a) - var_to_int(b));
}
case TYPE_VDPTR:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return ptr_to_var((void*)-1UL);
case TYPE_INT:
return ptr_to_var((char*)var_to_ptr(a) - var_to_int(b));
case TYPE_VDPTR:
return ptr_to_var((void*)((char*)var_to_ptr(a) - (char*)var_to_ptr(b)));
}
default:
return ptr_to_var((void*)-1UL);
}
}
var mul_var(var a, var b){
switch((a.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_float(a) * var_to_float(b));
case TYPE_INT:
return float_to_var(var_to_float(a) * var_to_int(b));
}
case TYPE_INT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_int(a) * var_to_float(b));
case TYPE_INT:
return int_to_var(var_to_int(a) * var_to_int(b));
}
default:
return ptr_to_var((void*)-1UL);
}
}
var div_var(var a, var b){
switch((a.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_float(a) / var_to_float(b));
case TYPE_INT:
return float_to_var(var_to_float(a) / var_to_int(b));
}
case TYPE_INT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(var_to_int(a) / var_to_float(b));
case TYPE_INT:
return int_to_var(var_to_int(a) / var_to_int(b));
}
default:
return ptr_to_var((void*)-1UL);
}
}
var mod_var(var a, var b){
switch((a.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(fmod(var_to_float(a), var_to_float(b)));
case TYPE_INT:
return float_to_var(fmod(var_to_float(a), var_to_int(b)));
}
case TYPE_INT:
switch((b.as_long & MASK_TYPE) >> 48){
case TYPE_FLOAT:
return float_to_var(fmod(var_to_int(a), var_to_float(b)));
case TYPE_INT:
return int_to_var(var_to_int(a) % var_to_int(b));
}
default:
return ptr_to_var((void*)-1UL);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment