Hi Paul,

> While we're on the topic of C's arithmetic deficiencies, how about if
> we also add a macro for safe and portable comparison?

Yes, absolutely! It's a deficiency in the C standard libraries that there
are no library functions for doing that.

> Something like "ARITH_COMPARE (A, OP, B)" to compare A and B
> numerically rather than using C's rules.  So, for example,
> ARITH_COMPARE (-1, <, sizeof "x") would return true even though
> typically (-1 > sizeof "x") in C, and
> ARITH_COMPARE ((float) LONG_MAX, <, LONG_MAX) even though typically
> (float) LONG_MAX == LONG_MAX in C.

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).

Find attached some code for a beginning. Caveats:
  - For the integers it assumes a two's complement representation. 
  - For the floating-point type it assumes a representation with a fixed
    number of mantissa bits (*not* the 'long double' representation of IRIX
    and PowerPC that consists of two 'double's).

> This new macro could be in a new arith-compare module, or perhaps
> we could stretch things a bit and put it into intprops, as all of
> the deficiencies it's correcting are related to integers.

'arith-compare' sounds fine. Yes, I wouldn't put floating-point related
stuff into 'intprops', because then 'intprops' will soon need to depend on
the 'float' module.

Bruno
-- 
In memoriam Dang Xuan Hoan <http://www.vietmemorial.org/myweb/thelist.html>
/* Comparison of integer and floating-point numbers by value.
   Copyright (C) 2011 Free Software Foundation, Inc.
   Written by Bruno Haible <br...@clisp.org>, 2011.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */

#include <config.h>

/* In comparison expressions like (x < y), where x is an integer value and
   y is a floating-point value, the "usual arithmetic conversion are performed"
   (ISO C 99 section 6.5.8.(3)), that means, the integer value x is converted
   to the floating-point type of y (ISO C 99 section 6.3.1.8).
   This leads to results that don't match the mathematical values.  The
   following examples assume IEEE 754 'float' and 'double' types.
     (16777217 > 16777216.0f)                   => false; true by values.
     (16777217 == 16777216.0f)                  => true; false by values.
     (9007199254740993LL > 9007199254740992.0)  => false; true by values.
     (9007199254740993LL == 9007199254740992.0) => true; false by values.

   The function in this file compares numbers by value.  */

#include <limits.h>
#include <float.h>
#include <stdlib.h>

#include "intprops.h"

#define I_TYPE long long
#define I_BITS (sizeof (I_TYPE) * CHAR_BIT - TYPE_SIGNED (I_TYPE))
#define F_TYPE double
#define FLT_RADIX_BITS (FLT_RADIX == 2 ? 1 : FLT_RADIX == 4 ? 2 : 0)
#define F_MANTISSA_BITS (DBL_MANT_DIG * FLT_RADIX_BITS)
#define COMPARE compare_longlong_double

/* Compare x, of integral type I_TYPE, and y, of floating-point type F_TYPE
   and not a NaN value.
   Return +1 if x is greater than y, -1 if x is less than y, and 0 if x and y
   are equal.  */
int
COMPARE (I_TYPE x, F_TYPE y)
{
  if (I_BITS <= F_MANTISSA_BITS
      || (x <= ((I_TYPE) 1 << F_MANTISSA_BITS)
          && (!TYPE_SIGNED (I_TYPE) || x >= - ((I_TYPE) 1 << F_MANTISSA_BITS))))
    {
      /* x can be converted to F_TYPE without loss of precision.  */
      F_TYPE xf = (F_TYPE) x;
      /* Verify that there is no loss of precision.  */
      if ((I_TYPE) xf != x)
        abort ();
      return (xf > y ? 1 : xf < y ? -1 : 0);
    }
  else
    {
      /* Prepare to convert y to I_TYPE.  */
      if (TYPE_SIGNED (I_TYPE) && x < 0)
        {
          if (y >= - (F_TYPE) ((I_TYPE) 1 << F_MANTISSA_BITS))
            /* x < - 2^F_MANTISSA_BITS <= y.  */
            return -1;
          /* x, y both < - 2^F_MANTISSA_BITS.  */
          /* NB: TYPE_MINIMUM (I_TYPE) == - 2^I_BITS = 2 * - 2^(I_BITS-1) */
          if (y < (F_TYPE) 2 * - (F_TYPE) ((I_TYPE) 1 << (I_BITS - 1)))
            /* x >= TYPE_MINIMUM (I_TYPE) > y.  */
            return 1;
        }
      else
        {
          if (y <= (F_TYPE) ((I_TYPE) 1 << F_MANTISSA_BITS))
            /* x > 2^F_MANTISSA_BITS >= y.  */
            return 1;
          /* x, y both > 2^F_MANTISSA_BITS.  */
          /* NB: TYPE_MAXIMUM (I_TYPE) + 1 == 2^I_BITS = 2 * 2^(I_BITS-1) */
          if (y >= (F_TYPE) 2 * (F_TYPE) ((I_TYPE) 1 << (I_BITS - 1)))
            /* x <= TYPE_MAXIMUM (I_TYPE) < TYPE_MAXIMUM (I_TYPE) + 1 <= y.  */
            return -1;
        }
      /* y can be converted to I_TYPE without loss of precision.  */
      {
        I_TYPE yi = (I_TYPE) y;
        /* Verify that there is no loss of precision.  */
        if ((F_TYPE) yi != y)
          abort ();
        return (x > yi ? 1 : x < yi ? -1 : 0);
      }
    }
}

#ifdef TEST

#include <stdio.h>

int
main ()
{
  printf ("%d\n", compare_longlong_double (9007199254740993LL, 9007199254740992.0)); /* expect 1 */
  printf ("%d\n", compare_longlong_double (-9007199254740993LL, -9007199254740992.0)); /* expect -1 */
  printf ("%d\n", compare_longlong_double (9223372036854775807LL, 9223372036854775808.0)); /* expect -1 */
  printf ("%d\n", compare_longlong_double (-9223372036854775807LL-1, -9223372036854775808.0)); /* expect 0 */
  printf ("%d\n", compare_longlong_double (-9223372036854775807LL, -9223372036854775808.0)); /* expect 1 */
  return 0;
}

#endif

Reply via email to