Created
June 23, 2023 05:09
-
-
Save m1lkweed/6bce23faa0a9a894fbc3c79099a511fb to your computer and use it in GitHub Desktop.
A quick and dirty nan boxing implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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