1 // Type infos and methods for Nums (floating point)
2 // This file is a template that expects `NUMSX_C_H__BITS` to be defined before including:
4 // #define NUMX_C_H__BITS 64
17 #ifndef NUMX_C_H__BITS
18 #define NUMX_C_H__BITS 64
21 #if NUMX_C_H__BITS == 64
24 #define NAMESPACED(x) Num$##x
25 #define TYPE_STR "Num"
27 #elif NUMX_C_H__BITS == 32
30 #define NAMESPACED(x) Num32$##x
31 #define TYPE_STR "Num32"
32 #define SUFFIXED(x) x##f
34 #error "Unsupported bit width for Num"
37 #if NUMX_C_H__BITS == 64
42 PUREFUNC Text_t NAMESPACED(value_as_text)(NUM_T x) {
43 char *str = GC_MALLOC_ATOMIC(24);
44 int len = fpconv_dtoa(x, str);
45 return Text$from_strn(str, (size_t)len);
48 PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo_t *info) {
50 if (!x) return Text(TYPE_STR);
51 static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m");
52 Text_t text = NAMESPACED(value_as_text)(*(NUM_T *)x);
53 return colorize ? Text$concat(color_prefix, text, color_suffix) : text;
56 PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) {
58 int64_t rx = *(int64_t *)x, ry = *(int64_t *)y;
60 if (rx == ry) return 0;
62 if (rx < 0) rx ^= INT64_MAX;
63 if (ry < 0) ry ^= INT64_MAX;
65 return (rx > ry) - (rx < ry);
67 #elif NUMX_C_H__BITS == 32
69 PUREFUNC Text_t NAMESPACED(value_as_text)(NUM_T x) {
70 return Num$value_as_text((double)x);
73 PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo_t *info) {
75 if (!x) return Text(TYPE_STR);
76 static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m");
77 Text_t text = Num$value_as_text((double)*(NUM_T *)x);
78 return colorize ? Text$concat(color_prefix, text, color_suffix) : text;
81 PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) {
83 int32_t rx = *(int32_t *)x, ry = *(int32_t *)y;
85 if (rx == ry) return 0;
87 if (rx < 0) rx ^= INT32_MAX;
88 if (ry < 0) ry ^= INT32_MAX;
90 return (rx > ry) - (rx < ry);
95 PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t *info) {
97 return *(NUM_T *)x == *(NUM_T *)y;
101 CONSTFUNC bool NAMESPACED(near)(NUM_T a, NUM_T b, NUM_T ratio, NUM_T absolute) {
102 if (ratio < 0) ratio = 0;
103 else if (ratio > 1) ratio = 1;
105 if (a == b) return true;
107 NUM_T diff = SUFFIXED(fabs)(a - b);
108 if (diff < absolute) return true;
109 else if (isnan(diff)) return false;
111 NUM_T epsilon = SUFFIXED(fabs)(a * ratio) + SUFFIXED(fabs)(b * ratio);
112 if (isinf(epsilon)) epsilon = DBL_MAX;
113 return (diff < epsilon);
117 Text_t NAMESPACED(percent)(NUM_T x, NUM_T precision) {
118 NUM_T d = SUFFIXED(100.) * x;
119 d = NAMESPACED(with_precision)(d, precision);
120 return Text$concat(NAMESPACED(value_as_text)(d), Text("%"));
124 CONSTFUNC NUM_T NAMESPACED(with_precision)(NUM_T num, NUM_T precision) {
125 if (precision == SUFFIXED(0.0)) return num;
126 // Precision will be, e.g. 0.01 or 100.
127 if (precision < SUFFIXED(1.)) {
128 NUM_T inv = SUFFIXED(round)(SUFFIXED(1.) / precision); // Necessary to make the math work
130 return SUFFIXED(round)(k) / inv;
132 NUM_T k = num / precision;
133 return SUFFIXED(round)(k) * precision;
138 CONSTFUNC NUM_T NAMESPACED(mod)(NUM_T num, NUM_T modulus) {
139 // Euclidean division, see:
140 // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
141 NUM_T r = (NUM_T)remainder((double)num, (double)modulus);
142 r -= (r < SUFFIXED(0.)) * (SUFFIXED(2.) * (modulus < SUFFIXED(0.)) - SUFFIXED(1.)) * modulus;
147 CONSTFUNC NUM_T NAMESPACED(mod1)(NUM_T num, NUM_T modulus) {
148 return SUFFIXED(1.0) + NAMESPACED(mod)(num - SUFFIXED(1.0), modulus);
152 CONSTFUNC NUM_T NAMESPACED(mix)(NUM_T amount, NUM_T x, NUM_T y) {
153 return (SUFFIXED(1.0) - amount) * x + amount * y;
157 CONSTFUNC bool NAMESPACED(is_between)(const NUM_T x, const NUM_T low, const NUM_T high) {
158 return (low <= x && x <= high) || (high <= x && x <= low);
161 CONSTFUNC NUM_T NAMESPACED(clamped)(NUM_T x, NUM_T low, NUM_T high) {
162 return (x <= low) ? low : (x >= high ? high : x);
166 OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder) {
167 const char *str = Text$as_c_string(text);
169 #if NUMX_C_H__BITS == 64
170 NUM_T n = strtod(str, &end);
171 #elif NUMX_C_H__BITS == 32
172 NUM_T n = strtof(str, &end);
175 if (remainder) *remainder = Text$from_str(end);
176 else if (*end != '\0') return SUFFIXED(nan)("none");
179 if (remainder) *remainder = text;
180 return SUFFIXED(nan)("none");
185 CONSTFUNC bool NAMESPACED(is_none)(const void *n, const TypeInfo_t *info) {
187 return isnan(*(NUM_T *)n);
191 CONSTFUNC bool NAMESPACED(isinf)(NUM_T n) {
192 return (fpclassify(n) == FP_INFINITE);
195 CONSTFUNC bool NAMESPACED(finite)(NUM_T n) {
196 return (fpclassify(n) != FP_INFINITE);
199 CONSTFUNC bool NAMESPACED(isnan)(NUM_T n) {
200 return (fpclassify(n) == FP_NAN);
204 const TypeInfo_t NAMESPACED(info) = {
205 .size = sizeof(NUM_T),
206 .align = __alignof__(NUM_T),
209 .compare = NAMESPACED(compare),
210 .equal = NAMESPACED(equal),
211 .as_text = NAMESPACED(as_text),
212 .is_none = NAMESPACED(is_none),
221 #undef NUMX_C_H__BITS