[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: proposed ARITH_COMPARE macro
From: |
Paul Eggert |
Subject: |
Re: proposed ARITH_COMPARE macro |
Date: |
Sun, 15 May 2011 21:43:38 -0700 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110424 Thunderbird/3.1.10 |
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));