On 05/15/11 17:32, Bruno Haible wrote: > Can you do this purely with macros? I think we should start from a > function, or a set of functions (depending on the input types).
I was hoping that we could do it with macros, just as the integer overflow tests are done with macros. The advantage of macros is that, if done right, they're easier to use, at least in C: a single comparison macro should work for any pair of arithmetic arguments, so that the caller need not worry about which comparison function to use. Also, the macros are valid as constant expressions, if their arguments are constants. A disadvantage is that they work only if arguments are side effect free, but that's a price I'm willing to pay. (It may be that we'll want both macros and functions.) Certainly for integers macros should be doable. But for floating-point things get tricky. I had forgotten about the double-double of the PowerPC. Here's my first cut at doing it with macros. I haven't proofread it as much as I'd like, and I haven't written down all the portability assumptions or all the ideas that have gone into this code, but I thought I'd show a brief idea of what I was thinking of. As usual, the idea is to work on all practical platforms, perhaps excluding some weird theoretical platforms that conform to the C standard. I'm unhappy with ARITH_TYPE_COMPARE (ta, a, op, tb, b); I want a macro ARITH_COMPARE (a, op, b) so that the user need not write down the types of the operands. But one step at a time... #include <intprops.h> /* Return 1 if A OP B, where A and B are integer expressions and OP is a comparison operator such as == or <. Use mathematical comparison, not C comparison; for example, INT_COMPARE (-1, <, UINT_MAX) yields 1 even though (-1 < UINT_MAX) yields 0. A and B must not have side effects. */ #define INT_COMPARE(a, op, b) \ (! _GL_INT_MATH_COMPARISON (a, b) && (a) < 0 ? -1 op 0 \ : ! _GL_INT_MATH_COMPARISON (a, b) && (b) < 0 ? 0 op -1 \ : (a) op (b)) /* Return 1 if comparing A to B is guaranteed to agree with the mathematical result. A and B are integer expressions. */ #define _GL_INT_MATH_COMPARISON(a, b) \ (! (_GL_INT_SIGNED (a) || _GL_INT_SIGNED (b)) \ || _GL_INT_SIGNED (0 * (a) * (b))) /* Return 1 if A OP B, where A is an arithmetic expression of type TA and B an arithmetic expression of type TB. OP is one of the standard C comparison operators. Use mathematical comparison, not C comparison; for example, ARITH_TYPE_COMPARE (long long int, 9223372036854775807, <, double, 9223372036854775808.0) returns 1 even though 9223372036854775807 == 9223372036854775808.0 on most hosts due to rounding error. */ #define ARITH_TYPE_COMPARE(ta, a, op, tb, b) \ (TYPE_IS_INTEGER (ta) && TYPE_IS_INTEGER (tb) \ ? INT_COMPARE (a, op, b) \ : ((TYPE_IS_INTEGER (ta) ? sizeof (ta) < sizeof (tb) \ : ! TYPE_IS_INTEGER (tb) || sizeof (tb) < sizeof (ta)) \ || (a) != (b)) \ ? (a) op (b) \ : TYPE_IS_INTEGER (ta) \ ? ((a) / 2 != (ta) ((b) / 2) \ ? ((a) / 2) op (ta) ((b) / 2) \ : (((a) - (a) / 2 * 2) op ((b) - (a) / 2 * 2))) \ : ((tb) ((a) / 2) != (b) / 2 \ ? (tb) ((a) / 2) op ((b) / 2) \ : ((a) - (b) / 2 * 2) op (((b) - (b) / 2 * 2)))) #include <verify.h> verify (ARITH_TYPE_COMPARE (long long int, 9007199254740993LL, >, double, 9007199254740992.0)); verify (ARITH_TYPE_COMPARE (long long int, -9007199254740993LL, <, double, -9007199254740992.0)); verify (ARITH_TYPE_COMPARE (long long int, 9223372036854775807, <, double, 9223372036854775808.0)); verify (ARITH_TYPE_COMPARE (long long int, -9223372036854775807LL-1, ==, double, -9223372036854775808.0)); verify (ARITH_TYPE_COMPARE (long long int, -9223372036854775807LL, >, double, -9223372036854775808.0));