solidc
Robust collection of general-purpose cross-platform C libraries and data structures designed for rapid and safe development in C
Loading...
Searching...
No Matches
cmp.h
Go to the documentation of this file.
1
34#ifndef SOLIDC_CMP_H
35#define SOLIDC_CMP_H
36
37#include <float.h>
38#include <math.h>
39#include <stdbool.h>
40#include <stdint.h>
41#include <stdlib.h>
42
43#if defined(__cplusplus)
44extern "C" {
45#endif
46
50typedef enum {
54 CMP_ULPS,
58
62typedef struct {
64 double epsilon;
65 uint64_t ulps;
67
68// Default configurations
69#define CMP_DEFAULT_FLOAT {CMP_RELATIVE, 1e-6f, 4}
70#define CMP_DEFAULT_DOUBLE {CMP_RELATIVE, 1e-12, 4}
71#define CMP_DEFAULT_LONG {CMP_RELATIVE, 1e-15, 4}
72
73// Helper functions
74static inline bool cmp_special_cases(double a, double b) {
75 return (isnan(a) && isnan(b)) || (isinf(a) && isinf(b) && signbit(a) == signbit(b));
76}
77
78static inline bool cmp_is_zero(double a) { return fabs(a) <= DBL_EPSILON; }
79
80static inline int64_t cmp_double_to_int64(double d) {
81 union {
82 double d;
83 int64_t i;
84 } u = {d};
85 return (u.i < 0) ? INT64_MIN - u.i : u.i;
86}
87
88// Core comparison functions
89static inline bool cmp_absolute(double a, double b, double epsilon) {
90 if (cmp_special_cases(a, b)) return true;
91 return fabs(a - b) <= epsilon;
92}
93
94static inline bool cmp_relative(double a, double b, double epsilon) {
95 if (cmp_special_cases(a, b)) return true;
96 if (cmp_is_zero(a) && cmp_is_zero(b)) return true;
97 return fabs(a - b) <= fmax(fabs(a), fabs(b)) * epsilon;
98}
99
100static inline bool cmp_ulps(double a, double b, uint64_t max_ulps) {
101 if (cmp_special_cases(a, b)) return true;
102 if (cmp_is_zero(a) && cmp_is_zero(b)) return true;
103
104 int64_t a_int = cmp_double_to_int64(a);
105 int64_t b_int = cmp_double_to_int64(b);
106
107 if ((a_int < 0) != (b_int < 0)) {
108 return a_int == INT64_MIN && b_int == INT64_MIN;
109 }
110 return (uint64_t)llabs(a_int - b_int) <= max_ulps;
111}
112
113static inline bool cmp_combined(double a, double b, double epsilon) {
114 if (cmp_special_cases(a, b)) return true;
115 if (cmp_is_zero(a) && cmp_is_zero(b)) return true;
116
117 double diff = fabs(a - b);
118 if (diff <= epsilon) return true;
119
120 return diff <= fmax(fabs(a), fabs(b)) * epsilon;
121}
122
123// Main generic comparison function
124#define CMP(a, b, ...) \
125 _Generic((a), float: cmp_float, double: cmp_double, long double: cmp_long_double)(a, b, ##__VA_ARGS__)
126
127// Type-specific comparison functions
128static inline bool cmp_float(float a, float b, cmp_config_t config) {
129 switch (config.mode) {
130 case CMP_ABSOLUTE:
131 return cmp_absolute(a, b, config.epsilon);
132 case CMP_RELATIVE:
133 return cmp_relative(a, b, config.epsilon);
134 case CMP_ULPS:
135 return cmp_ulps(a, b, config.ulps);
136 case CMP_COMBINED:
137 return cmp_combined(a, b, config.epsilon);
138 default:
139 return cmp_relative(a, b, config.epsilon);
140 }
141}
142
143static inline bool cmp_double(double a, double b, cmp_config_t config) {
144 switch (config.mode) {
145 case CMP_ABSOLUTE:
146 return cmp_absolute(a, b, config.epsilon);
147 case CMP_RELATIVE:
148 return cmp_relative(a, b, config.epsilon);
149 case CMP_ULPS:
150 return cmp_ulps(a, b, config.ulps);
151 case CMP_COMBINED:
152 return cmp_combined(a, b, config.epsilon);
153 default:
154 return cmp_relative(a, b, config.epsilon);
155 }
156}
157
158static inline bool cmp_long_double(long double a, long double b, cmp_config_t config) {
159 switch (config.mode) {
160 case CMP_ABSOLUTE:
161 return cmp_absolute((double)a, (double)b, config.epsilon);
162 case CMP_RELATIVE:
163 return cmp_relative((double)a, (double)b, config.epsilon);
164 case CMP_ULPS:
165 return cmp_ulps((double)a, (double)b, config.ulps);
166 case CMP_COMBINED:
167 return cmp_combined((double)a, (double)b, config.epsilon);
168 default:
169 return cmp_relative((double)a, (double)b, config.epsilon);
170 }
171}
172
173#define CMP_EPS(a, b, eps) \
174 cmp(a, b, \
175 (cmp_config_t){.mode = CMP_RELATIVE, \
176 .epsilon = (eps), \
177 .ulps = _Generic((a), float: 4, double: 4, long double: 4)})
178
179#define CMP_ABS(a, b, eps) \
180 cmp(a, b, \
181 (cmp_config_t){.mode = CMP_ABSOLUTE, \
182 .epsilon = (eps), \
183 .ulps = _Generic((a), float: 4, double: 4, long double: 4)})
184
185#define CMP_ULPS(a, b, ulps_val) \
186 cmp(a, b, \
187 (cmp_config_t){.mode = CMP_ULPS, \
188 .epsilon = _Generic((a), float: 1e-6f, double: 1e-12, long double: 1e-15), \
189 .ulps = (ulps_val)})
190
191#define CMP_COMB(a, b, eps) \
192 cmp(a, b, \
193 (cmp_config_t){.mode = CMP_COMBINED, \
194 .epsilon = (eps), \
195 .ulps = _Generic((a), float: 4, double: 4, long double: 4)})
196
197#if defined(__cplusplus)
198}
199#endif
200
201#endif /* SOLIDC_CMP_H */
202
cmp_mode_t
Comparison modes for floating-point numbers.
Definition cmp.h:50
@ CMP_RELATIVE
Definition cmp.h:52
@ CMP_COMBINED
Definition cmp.h:56
@ CMP_ABSOLUTE
Definition cmp.h:51
Default configuration for each floating-point type.
Definition cmp.h:62
cmp_mode_t mode
Definition cmp.h:63
double epsilon
Definition cmp.h:64
uint64_t ulps
Definition cmp.h:65