Last active
July 31, 2025 21:30
-
-
Save m1lkweed/1e7664748eb3050595faa368773d37c1 to your computer and use it in GitHub Desktop.
The most-efficient possible 8-bit rational in C.
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
| // 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)); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hangs...
also... that's a lot of code and I'm not exactly seeing any concrete use case or improvement over
double = (int8)/128.