Skip to content

Instantly share code, notes, and snippets.

@m1lkweed
Last active July 31, 2025 21:30
Show Gist options
  • Select an option

  • Save m1lkweed/1e7664748eb3050595faa368773d37c1 to your computer and use it in GitHub Desktop.

Select an option

Save m1lkweed/1e7664748eb3050595faa368773d37c1 to your computer and use it in GitHub Desktop.
The most-efficient possible 8-bit rational in C.
// All rationals from -19/20 to 1 are represented, including all denominators from 1 to 20, in only 8 bits.
[[maybe_unused]] static const unsigned char n[128] = {0, 1, 1, 2, 1, 3, 1, 2, 3, 4, 1, 5, 1, 2, 3, 4, 5, 6, 1, 3, 5, 7, 1, 2, 4, 5, 7, 8, 1, 3, 7, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 5, 7, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 3, 5, 9, 11, 13, 1, 2, 4, 7, 8, 11, 13, 14, 1, 3, 5, 7, 9, 11, 13, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 5, 7, 11, 13, 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 1, 3, 7, 9, 11, 13, 17, 19};
[[maybe_unused]] static const unsigned char d[128] = {1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20};
[[maybe_unused]] static const unsigned char d_start[21] = {0, 0, 1, 2, 4, 6, 10, 12, 18, 22, 28, 32, 42, 46, 58, 64, 72, 80, 96, 102, 120};
[[maybe_unused]] static const unsigned char d_len[21] = {0, 1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8};
[[maybe_unused]] static const unsigned char grade_up[128] = {0, 120, 102, 96, 80, 72, 64, 58, 46, 42, 32, 28, 103, 22, 81, 18, 65, 12, 121, 47, 104, 10, 82, 33, 73, 6, 105, 59, 23, 48, 83, 4, 106, 66, 34, 97, 13, 84, 29, 49, 74, 107, 2, 122, 85, 60, 35, 108, 19, 50, 98, 7, 86, 43, 109, 14, 75, 24, 123, 36, 51, 67, 87, 110, 1, 111, 88, 68, 52, 37, 124, 25, 76, 15, 112, 44, 89, 8, 99, 53, 20, 113, 38, 61, 90, 125, 3, 114, 77, 54, 30, 91, 16, 100, 39, 69, 115, 5, 92, 55, 26, 62, 116, 9, 78, 40, 93, 11, 117, 56, 126, 17, 70, 21, 94, 27, 118, 31, 41, 45, 57, 63, 71, 79, 95, 101, 119, 127};
typedef struct{
unsigned _BitInt(7) index:7;
bool is_negative:1;
}rat8;
double rat8_to_double(rat8 in){
if(in.is_negative){
if(in.index == 0)
return 1.0;
else
return -n[128 - in.index] / (double)d[128 - in.index];
}else{
return n[in.index] / (double)d[in.index];
}
}
float rat8_to_float(rat8 in){
if(in.is_negative){
if(in.index == 0)
return 1.0f;
else
return -n[128 - in.index] / (float)d[128 - in.index];
}else{
return n[in.index] / (float)d[in.index];
}
}
rat8 int_pair_to_rat8(int numerator, int denominator){
rat8 ret = {};
if((numerator * denominator) == 1)
ret.is_negative = 1;
if(numerator < 0){
ret.is_negative = !ret.is_negative;
numerator *= -1;
}
if(denominator < 0){
ret.is_negative = !ret.is_negative;
denominator *= -1;
}
int an = numerator, b = denominator;
int k = __builtin_ctzg((unsigned)(an | b));
an >>= k;
b >>= k;
while(b != 0){
int quotient = an / b, tmp = b;
b = an - quotient * b;
an = tmp;
}
int divisor = an << k;
numerator /= divisor;
denominator /= divisor;
if(denominator > 20){
int floor_nearest_index = 0;
for(int i = 0; i < 128; ++i){
if(n[i]/(double)d[i] == numerator / (double)denominator)
break;
if((n[i]/(double)d[i] < numerator / (double)denominator) && (n[i] / (double)d[i] > n[floor_nearest_index] / (double)d[floor_nearest_index]))
floor_nearest_index = i;
}
ret.index = floor_nearest_index;
}else{
ret.index = d_start[denominator];
while(numerator != n[ret.index])
++ret.index;
}
return ret;
}
rat8 double_to_rat8(double in){
rat8 ret = {};
if(in == 1.0)
ret.is_negative = 1;
if(in < 0){
ret.is_negative = !ret.is_negative;
in *= -1;
}
int floor_nearest_index = 0;
for(int i = 0; i < 128; ++i){
if(n[i]/(double)d[i] == in)
break;
if((n[i]/(double)d[i] < in) && (n[i] / (double)d[i] > n[floor_nearest_index] / (double)d[floor_nearest_index]))
floor_nearest_index = i;
}
ret.index = floor_nearest_index;
return ret;
}
rat8 float_to_rat8(float in){
rat8 ret = {};
if(in == 1.0f)
ret.is_negative = 1;
if(in < 0){
ret.is_negative = !ret.is_negative;
in *= -1;
}
int floor_nearest_index = 0;
for(int i = 0; i < 128; ++i){
if(n[i]/(float)d[i] == in)
break;
if((n[i]/(float)d[i] < in) && (n[i] / (float)d[i] > n[floor_nearest_index] / (float)d[floor_nearest_index]))
floor_nearest_index = i;
}
ret.index = floor_nearest_index;
return ret;
}
rat8 rat8_add(rat8 a, rat8 b){
int a_numerator, a_denominator, b_numerator, b_denominator;
if(a.is_negative){
if(a.index == 0){
a_numerator = a_denominator = 1;
}else{
a_numerator = -n[128 - a.index];
a_denominator = d[128 - a.index];
}
}else{
a_numerator = n[a.index],
a_denominator = d[a.index];
}
if(b.is_negative){
if(b.index == 0){
b_numerator = b_denominator = 1;
}else{
b_numerator = -n[128 - b.index];
b_denominator = d[128 - b.index];
}
}else{
b_numerator = n[b.index],
b_denominator = d[b.index];
}
int denominator = a_denominator;
if(a_denominator != b_denominator){
int denominator = a_denominator * b_denominator;
a_numerator = denominator / a_numerator * a_denominator;
b_numerator = denominator / b_numerator * b_denominator;
}
a_numerator += b_numerator;
return int_pair_to_rat8(a_numerator, denominator);
}
rat8 rat8_sub(rat8 a, rat8 b){
int a_numerator, a_denominator, b_numerator, b_denominator;
if(a.is_negative){
if(a.index == 0){
a_numerator = a_denominator = 1;
}else{
a_numerator = -n[128 - a.index];
a_denominator = d[128 - a.index];
}
}else{
a_numerator = n[a.index],
a_denominator = d[a.index];
}
if(b.is_negative){
if(b.index == 0){
b_numerator = b_denominator = 1;
}else{
b_numerator = -n[128 - b.index];
b_denominator = d[128 - b.index];
}
}else{
b_numerator = n[b.index],
b_denominator = d[b.index];
}
int denominator = a_denominator;
if(a_denominator != b_denominator){
int denominator = a_denominator * b_denominator;
a_numerator = denominator / a_numerator * a_denominator;
b_numerator = denominator / b_numerator * b_denominator;
}
a_numerator -= b_numerator;
return int_pair_to_rat8(a_numerator, denominator);
}
rat8 rat8_mul(rat8 a, rat8 b){
int a_numerator, a_denominator, b_numerator, b_denominator;
if(a.is_negative){
if(a.index == 0){
a_numerator = a_denominator = 1;
}else{
a_numerator = -n[128 - a.index];
a_denominator = d[128 - a.index];
}
}else{
a_numerator = n[a.index],
a_denominator = d[a.index];
}
if(b.is_negative){
if(b.index == 0){
b_numerator = b_denominator = 1;
}else{
b_numerator = -n[128 - b.index];
b_denominator = d[128 - b.index];
}
}else{
b_numerator = n[b.index],
b_denominator = d[b.index];
}
return int_pair_to_rat8(a_numerator * b_numerator, a_denominator * b_denominator);
}
rat8 rat8_div(rat8 a, rat8 b){
int a_numerator, a_denominator, b_numerator, b_denominator;
if(a.is_negative){
if(a.index == 0){
a_numerator = a_denominator = 1;
}else{
a_numerator = -n[128 - a.index];
a_denominator = d[128 - a.index];
}
}else{
a_numerator = n[a.index],
a_denominator = d[a.index];
}
if(b.is_negative){
if(b.index == 0){
b_numerator = b_denominator = 1;
}else{
b_numerator = -n[128 - b.index];
b_denominator = d[128 - b.index];
}
}else{
b_numerator = n[b.index],
b_denominator = d[b.index];
}
return int_pair_to_rat8(a_numerator * b_denominator, a_denominator * b_numerator);
}
rat8 rat8_abs(rat8 x){
x.is_negative = 0;
return x;
}
#define rat8_compare(a, op, b) ((grade_up[(a).index] * ((a).is_negative?-1:1)) op (grade_up[(b).index] * ((b).is_negative?-1:1)))
// Example:
#define bit_cast(type, ...) ( \
union{typeof(__VA_ARGS__) in; typeof(type) out; \
int enforce_type:_Generic((int(*)(int(type)))0, \
int(*)(int):-1, default:1);} \
){__VA_ARGS__}.out
int main(){
for(int i = 0; i < 256; ++i)
printf("%d: %f\n", i, rat8_to_double(bit_cast(rat8, i)));
return rat8_compare(int_pair_to_rat8(1, 2), >, int_pair_to_rat8(1, 7));
}
@ldemailly
Copy link

ldemailly commented Jul 31, 2025

rat8 r1 = int_pair_to_rat8(5,17);
rat8 r2 = int_pair_to_rat8(7,19);
rat8 s = rat8_add(r1,r2);

hangs...

also... that's a lot of code and I'm not exactly seeing any concrete use case or improvement over double = (int8)/128.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment