43#if defined(__cplusplus)
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}
74static inline bool cmp_special_cases(
double a,
double b) {
75 return (isnan(a) && isnan(b)) || (isinf(a) && isinf(b) && signbit(a) == signbit(b));
78static inline bool cmp_is_zero(
double a) {
return fabs(a) <= DBL_EPSILON; }
80static inline int64_t cmp_double_to_int64(
double d) {
85 return (u.i < 0) ? INT64_MIN - u.i : u.i;
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;
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;
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;
104 int64_t a_int = cmp_double_to_int64(a);
105 int64_t b_int = cmp_double_to_int64(b);
107 if ((a_int < 0) != (b_int < 0)) {
108 return a_int == INT64_MIN && b_int == INT64_MIN;
110 return (uint64_t)llabs(a_int - b_int) <= max_ulps;
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;
117 double diff = fabs(a - b);
118 if (diff <= epsilon)
return true;
120 return diff <= fmax(fabs(a), fabs(b)) * epsilon;
124#define CMP(a, b, ...) \
125 _Generic((a), float: cmp_float, double: cmp_double, long double: cmp_long_double)(a, b, ##__VA_ARGS__)
128static inline bool cmp_float(
float a,
float b,
cmp_config_t config) {
129 switch (config.
mode) {
131 return cmp_absolute(a, b, config.
epsilon);
133 return cmp_relative(a, b, config.
epsilon);
135 return cmp_ulps(a, b, config.
ulps);
137 return cmp_combined(a, b, config.
epsilon);
139 return cmp_relative(a, b, config.
epsilon);
143static inline bool cmp_double(
double a,
double b,
cmp_config_t config) {
144 switch (config.
mode) {
146 return cmp_absolute(a, b, config.
epsilon);
148 return cmp_relative(a, b, config.
epsilon);
150 return cmp_ulps(a, b, config.
ulps);
152 return cmp_combined(a, b, config.
epsilon);
154 return cmp_relative(a, b, config.
epsilon);
158static inline bool cmp_long_double(
long double a,
long double b,
cmp_config_t config) {
159 switch (config.
mode) {
161 return cmp_absolute((
double)a, (
double)b, config.
epsilon);
163 return cmp_relative((
double)a, (
double)b, config.
epsilon);
165 return cmp_ulps((
double)a, (
double)b, config.
ulps);
167 return cmp_combined((
double)a, (
double)b, config.
epsilon);
169 return cmp_relative((
double)a, (
double)b, config.
epsilon);
173#define CMP_EPS(a, b, eps) \
175 (cmp_config_t){.mode = CMP_RELATIVE, \
177 .ulps = _Generic((a), float: 4, double: 4, long double: 4)})
179#define CMP_ABS(a, b, eps) \
181 (cmp_config_t){.mode = CMP_ABSOLUTE, \
183 .ulps = _Generic((a), float: 4, double: 4, long double: 4)})
185#define CMP_ULPS(a, b, ulps_val) \
187 (cmp_config_t){.mode = CMP_ULPS, \
188 .epsilon = _Generic((a), float: 1e-6f, double: 1e-12, long double: 1e-15), \
191#define CMP_COMB(a, b, eps) \
193 (cmp_config_t){.mode = CMP_COMBINED, \
195 .ulps = _Generic((a), float: 4, double: 4, long double: 4)})
197#if defined(__cplusplus)
cmp_mode_t
Comparison modes for floating-point numbers.
Default configuration for each floating-point type.