code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(221 lines)
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:
3 //
4 // #define NUMX_C_H__BITS 64
5 // #include "numX.c.h"
6 //
7 #include <float.h>
8 #include <gc.h>
9 #include <math.h>
10 #include <stdbool.h>
11 #include <stdint.h>
12 #include <stdlib.h>
14 #include "text.h"
15 #include "types.h"
17 #ifndef NUMX_C_H__BITS
18 #define NUMX_C_H__BITS 64
19 #endif
21 #if NUMX_C_H__BITS == 64
22 #define NUM_T double
23 #define OPT_T double
24 #define NAMESPACED(x) Num$##x
25 #define TYPE_STR "Num"
26 #define SUFFIXED(x) x
27 #elif NUMX_C_H__BITS == 32
28 #define NUM_T float
29 #define OPT_T float
30 #define NAMESPACED(x) Num32$##x
31 #define TYPE_STR "Num32"
32 #define SUFFIXED(x) x##f
33 #else
34 #error "Unsupported bit width for Num"
35 #endif
37 #if NUMX_C_H__BITS == 64
38 #include "fpconv.h"
39 #include "string.h"
41 public
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);
47 public
48 PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo_t *info) {
49 (void)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;
55 public
56 PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) {
57 (void)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
68 public
69 PUREFUNC Text_t NAMESPACED(value_as_text)(NUM_T x) {
70 return Num$value_as_text((double)x);
72 public
73 PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo_t *info) {
74 (void)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;
80 public
81 PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) {
82 (void)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);
92 #endif
94 public
95 PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t *info) {
96 (void)info;
97 return *(NUM_T *)x == *(NUM_T *)y;
100 public
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);
116 public
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("%"));
123 public
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
129 NUM_T k = num * inv;
130 return SUFFIXED(round)(k) / inv;
131 } else {
132 NUM_T k = num / precision;
133 return SUFFIXED(round)(k) * precision;
137 public
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;
143 return r;
146 public
147 CONSTFUNC NUM_T NAMESPACED(mod1)(NUM_T num, NUM_T modulus) {
148 return SUFFIXED(1.0) + NAMESPACED(mod)(num - SUFFIXED(1.0), modulus);
151 public
152 CONSTFUNC NUM_T NAMESPACED(mix)(NUM_T amount, NUM_T x, NUM_T y) {
153 return (SUFFIXED(1.0) - amount) * x + amount * y;
156 public
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);
160 public
161 CONSTFUNC NUM_T NAMESPACED(clamped)(NUM_T x, NUM_T low, NUM_T high) {
162 return (x <= low) ? low : (x >= high ? high : x);
165 public
166 OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder) {
167 const char *str = Text$as_c_string(text);
168 char *end = NULL;
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);
173 #endif
174 if (end > str) {
175 if (remainder) *remainder = Text$from_str(end);
176 else if (*end != '\0') return SUFFIXED(nan)("none");
177 return n;
178 } else {
179 if (remainder) *remainder = text;
180 return SUFFIXED(nan)("none");
184 public
185 CONSTFUNC bool NAMESPACED(is_none)(const void *n, const TypeInfo_t *info) {
186 (void)info;
187 return isnan(*(NUM_T *)n);
190 public
191 CONSTFUNC bool NAMESPACED(isinf)(NUM_T n) {
192 return (fpclassify(n) == FP_INFINITE);
194 public
195 CONSTFUNC bool NAMESPACED(finite)(NUM_T n) {
196 return (fpclassify(n) != FP_INFINITE);
198 public
199 CONSTFUNC bool NAMESPACED(isnan)(NUM_T n) {
200 return (fpclassify(n) == FP_NAN);
203 public
204 const TypeInfo_t NAMESPACED(info) = {
205 .size = sizeof(NUM_T),
206 .align = __alignof__(NUM_T),
207 .metamethods =
209 .compare = NAMESPACED(compare),
210 .equal = NAMESPACED(equal),
211 .as_text = NAMESPACED(as_text),
212 .is_none = NAMESPACED(is_none),
216 #undef NUM_T
217 #undef OPT_T
218 #undef NAMESPACED
219 #undef TYPE_STR
220 #undef SUFFIXED
221 #undef NUMX_C_H__BITS