I want to make strcmp
-like interface for comparing numbers, e.g., ncmp(x, y)
that returns an int > 0
if x > y
, 0
if x = y
, < 0
if x < y
in C (not C++).
Although I necessarily do not want to constrain the types, my main interest is to compare signed long int
s and double
s. The 'interface' can be a macro as in tgmath.h
, or it can be a (set of) functions. I want all pairs of signed long int
and double
to work; (signed long int, double)
and (double, double)
should work for instance.
What I'm currently using is the following macro:
#define ncmp(x, y) ((x) > (y)) - ((x) < (y))
Does this naive macro have any pitfalls? Is there a better, robust solution to compare numbers?
Any help would be greatly appreciated!
I want all pairs of
signed long int
anddouble
to work;
Since C11, code can use _Generic
to steer function selection based on type.
int cmp_long(long x, long y) {
return (x > y) - (x < y);
}
int cmp_double(double x, double y) {
return (x > y) - (x < y);
}
#define cmp(X, Y) _Generic((X) - (Y), \
long: cmp_long((X),(Y)), \
double: cmp_double((X),(Y)) \
)
This approach does not well detect cases where X, Y
are of different types as the (X) - (Y)
uses the common type between them @Ian Abbott. Yet it is a start.
int main(void) {
printf("%d\n", cmp(1L, 2L));
printf("%d\n", cmp(3.0, 4.0));
}
A more complex 2-stage _Generic
could be made to distinguish long, double
and double, long
. I'll leave that part to OP.
The compare function would be like the one below. The tricky part is to not lose precision of long
(it might be 64-bit) when comparing to double
.
// TBD: handling of NANs
#define DBL_LONG_MAX_P1 ((LONG_MAX/2 + 1)*2.0)
int cmp_long_double(long x, double y) {
// These 2 compares are expected to be exact - no rounding
if (y >= DBL_LONG_MAX_P1) return -1;
if (y < (double)LONG_MIN) return 1;
// (long) y is now in range of `long`. (Aside from NANs)
long y_long = (long) y; // Lose the fraction
if (y_long > x) return -1;
if (y_long < x) return 1;
// Still equal, so look at fraction
double whole;
double fraction = modf(y, &whole);
if (fraction > 0.0) return -1;
if (fraction < 0.0) return 1;
return 0;
}
Simplifications may exist.
When double
encodes all long
exactly or when long double
exists and encodes all long
exactly, it's easiest to convert both the long
and double
to the common type and compare.