bug-autoconf
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

autoconf-2.59 and AC_C_LONG_DOUBLE: comments and a proposal


From: Nelson H. F. Beebe
Subject: autoconf-2.59 and AC_C_LONG_DOUBLE: comments and a proposal
Date: Wed, 2 Jun 2004 10:43:01 -0600 (MDT)

In autoconf-2.59, and likely earlier version, the test macro
AC_C_LONG_DOUBLE expands to the following code to test the Standard C
limits (for readability, I've run the code through the "indent"
prettyprinter):

#include <float.h>
long double foo = 0.0;
int
main()
{
    static int test_array[1 - 2 * !(    /* Using '|' rather than '||' catches a 
GCC 2.95.2 x86 bug.
                                         */
                                       (DBL_MAX < LDBL_MAX) |
                                       (LDBL_EPSILON < DBL_EPSILON) |
                                       (DBL_MAX_EXP < LDBL_MAX_EXP) |
                                       (DBL_MANT_DIG < LDBL_MANT_DIG))];
    test_array[0] = 0;
    return 0;
}

On many systems, compilation of this test fails.  For example, on Sun
Solaris, with native compilers, the code expands to

long double foo = 0.0;
int
main()
{
    static int test_array[1 - 2 * !((1.7976931348623157E+308 < 
1.189731495357231765085759326628007016E+4932L) |
                                    
(1.925929944387235853055977942584927319E-34L < 2.2204460492503131E-16) |
                                    ((+1024) < (+16384)) |
                                    (53 < 113))];
    test_array[0] = 0;
    return 0;
}

and compilation reports

        configure:3109: CC -c   conftest.c >&5
        "conftest.c", line 32: Error: An integer constant expression is 
required within the array subscript operator.
        1 Error(s) detected.
        configure:3115: $? = 1

Examination of the 1989 ANSI C Standard, section 3.8.1. `Conditional
Inclusion'' provides the requirement from its first line:

>> The expresssion that controls conditional inclusion shall be an
>> integral constant expresion ...
   ^^^^^^^^ NB

In the 1999 ISO C Standard, section 6.10.1 ``Conditional inclusion'',
I find similar language:

>> The expression that controls conditional inclusion shall be an
>> integer constant expression
   ^^^^^^

Thus the AC_C_LONG_DOUBLE test violates both 1989 and 1999 ANSI/ISO C
Standards, because it requires floating-point arithmetic in the
preprocessor constant expression.

AC_C_LONG_DOUBLE therefore needs to be rewritten with a test program
that is linked and run to report the result of the expression.

As a first step, I propose an alternative test program (to be improved
below):

#include <float.h>

int
main()
{
    return (!((DBL_MAX < LDBL_MAX) &&
              (LDBL_EPSILON < DBL_EPSILON) &&
              (DBL_MAX_EXP < LDBL_MAX_EXP) &&
              (DBL_MANT_DIG < LDBL_MANT_DIG)));
}

Notice that this exits with a failure status condition unless ALL FOUR
conditions are satisfied.

In the current AC_C_LONG_DOUBLE implementation, if JUST ONE of the
tests is true, the boolean expression inside parentheses evaluates to
1, and the code expands to

        static int test_array[1];

and the test program will then successfully compile.  The documented
behavior of AC_C_LONG_DOUBLE in the autoconf manual is:

 - Macro: AC_C_LONG_DOUBLE
     If the C compiler supports a working `long double' type with more
     range or precision than the `double' type, define
     `HAVE_LONG_DOUBLE'.

I believe this is inadvisable: it should instead specify

 - Macro: AC_C_LONG_DOUBLE
     If the C compiler supports a working `long double' type with more
     range and precision than the `double' type, define
           ^^^ critical change here
     `HAVE_LONG_DOUBLE'.

My proposed test will match this documentation change.

In numerical code, when a "long double" type is being exploited to
achieve higher accuracy (I'm working on just such code right now), one
needs BOTH increased range and increased precision.  It would be of
little interest to have one or the other, but not both, of these.

It must be noted that my initial proposed test is not flawless.
Specifically, it does not catch a case like the Portland Group
compiler, pgcc, on Intel IA-32 systems.  That compiler piggybacks on
top of the native C header files and library, which define support for
an 80-bit long double.  However, the compiler does not implement "long
double"; it accepts code with that type, but compiles it as if it were
"double".  This then results in run-time failures if [s]printf
floating-point format items with the L type modifier, or long double
library routines, like powl(), are used.

After some further experimentation with that compiler, I found a
straightforward fix:

    return (!((DBL_MAX < LDBL_MAX) &&
              (DBL_MAX > 0.5 * DBL_MAX) &&
              (LDBL_MAX > 0.5L * LDBL_MAX) &&
              (LDBL_EPSILON < DBL_EPSILON) &&
              (LDBL_MIN < DBL_MIN) &&
              (DBL_MIN > 0.0) &&
              (LDBL_MIN > 0.0L) &&
              (DBL_MAX_EXP < LDBL_MAX_EXP) &&
              (DBL_MANT_DIG < LDBL_MANT_DIG)));

The additional tests ensure that neither DBL_MAX nor LDBL_MAX has
overflowed to infinity, and that neither DBL_MIN nor LDBL_MIN has
underflowed to zero.  With that change, the test now correctly detects
that pgcc and pgCC fail to properly support long double.

I tested this second solution on a wide range of systems

        AMD Athlon              GNU/Linux version 2.4.2-2smp (Red Hat 7.1 
(Seawolf))
        AMD Athlon(TM) MP 1800+ GNU/Lunix 2.6.5-1.358smp (Fedora Core release 2 
(Tettnang))
        Apple PowerPC G3 267MHz GNU/Linux 2.4.19-4a (Yellow Dog Linux release 
2.3 (Dayton))
        Apple PowerPC G4 1.4GHz Darwin 7.3.0 (MacOS 10.3.2 (7D26))
        Compaq Alpha Sierra     OSF/1 5.1
        Compaq/DEC Alpha        OSF/1 4.0F
        DEC Alpha               GNU/Linux 2.2.19-6.2.1 (Red Hat 6.2)
        HP 9000/712             HP-UX 10.20
        HP/Intel IA-64          GNU/Linux 2.2.17-14smp (Red Hat 6.2) [via HP 
NUE emulator on IA-32]
        IBM PowerPC             AIX 4.2
        Intel Itanium-2         GNU/Linux Red Hat Linux Advanced Server release 
2.1AS (Derry)
        Intel Pentium II        FreeBSD 4.4-RELEASE #0
        Intel Pentium III       FreeBSD 5.0
        Intel Pentium III       GNU/Linux 2.4.18-26.8.0smp (Red Hat 8.0 
(Psyche))
        Intel Pentium III       NetBSD 1.6
        Intel Pentium III       OpenBSD 3.2
        Intel Pentium III       Solaris 9 x86
        SGI Origin 200          IRIX 6.5
        Sun SPARC               GNU/Linux 2.2.19-6.2.1 (Red Hat 6.2)
        Sun SPARC               Solaris 2.7
        Sun SPARC               Solaris 2.8
        Sun SPARC               Solaris 2.9

using gcc, g++, native cc and CC (or cxx or xlC), pgcc, pgCC, icc, and
lcc, where available.  The test correctly identified the systems where
long double is not supported, or unusable.

It also detected IBM AIX systems, which use a brain-dead
doubled-double representation without requiring that the exponents of
each half to have a constant separation.  The result is that long
double numbers are uselessly represented as

        [sign]<highpart><arbitrarygarbage><lowpart><exponent>

making long double worthless in numerical code.

An instrumented version of the test on an IBM AIX system reported

        (DBL_MAX < LDBL_MAX)           = 1
        (DBL_MAX > 0.5 * DBL_MAX)      = 1
        (LDBL_MAX > 0.5L * LDBL_MAX)   = 1
        (LDBL_EPSILON < DBL_EPSILON)   = 1
        (LDBL_MIN < DBL_MIN)           = 0
        (DBL_MIN > 0.0)                = 1
        (LDBL_MIN > 0.0L)              = 1
        (DBL_MAX_EXP < LDBL_MAX_EXP)   = 0
        (DBL_MANT_DIG < LDBL_MANT_DIG) = 1

Similarly, SGI IRIX 6.5 MIPS R10000 fails the test, because it too has
a paired double representation that does not increase the exponent
range; the instrumented version output is the same as for IBM AIX.

The one problematic system that my test failed to detect is NetBSD 1.6
on Intel IA-32.  It produces

        (DBL_MAX < LDBL_MAX)           = 1
        (DBL_MAX > 0.5 * DBL_MAX)      = 1
        (LDBL_MAX > 0.5L * LDBL_MAX)   = 1
        (LDBL_EPSILON < DBL_EPSILON)   = 1
        (LDBL_MIN < DBL_MIN)           = 1
        (DBL_MIN > 0.0)                = 1
        (LDBL_MIN > 0.0L)              = 1
        (DBL_MAX_EXP < LDBL_MAX_EXP)   = 1
        (DBL_MANT_DIG < LDBL_MANT_DIG) = 1

and yet the math library, -lm, is lacking all long double routines,
and printf() produces incorrect output for %.Le format items.

By contrast, FreeBSD 4.4 and 5.0, and OpenBSD 3.2, are all correctly
detected as lacking long double support.

I'm therefore inclined to recommend that the test be augmented with a
minimal check for library support of long double, such as illustrated
by this test program (augmented with print statements):

% cat zzlongdouble.c
#include <stdio.h>
#include <float.h>
#include <math.h>

int
main()
{
    double x;
    long double y;

    (void)printf("(DBL_MAX < LDBL_MAX)           = %d\n", (DBL_MAX < LDBL_MAX));
    (void)printf("(DBL_MAX > 0.5 * DBL_MAX)      = %d\n", (DBL_MAX > 0.5 * 
DBL_MAX));
    (void)printf("(LDBL_MAX > 0.5L * LDBL_MAX)   = %d\n", (LDBL_MAX > 0.5L * 
LDBL_MAX));
    (void)printf("(LDBL_EPSILON < DBL_EPSILON)   = %d\n", (LDBL_EPSILON < 
DBL_EPSILON));
    (void)printf("(LDBL_MIN < DBL_MIN)           = %d\n", (LDBL_MIN < DBL_MIN));
    (void)printf("(DBL_MIN > 0.0)                = %d\n", (DBL_MIN > 0.0));
    (void)printf("(LDBL_MIN > 0.0L)              = %d\n", (LDBL_MIN > 0.0L));
    (void)printf("(DBL_MAX_EXP < LDBL_MAX_EXP)   = %d\n", (DBL_MAX_EXP < 
LDBL_MAX_EXP));
    (void)printf("(DBL_MANT_DIG < LDBL_MANT_DIG) = %d\n", (DBL_MANT_DIG < 
LDBL_MANT_DIG));
    (void)printf("(sqrt(DBL_MIN)*sqrt(DBL_MIN) != DBL_MIN) = %d\n",
                 (sqrt(DBL_MIN)*sqrt(DBL_MIN) != DBL_MIN));
    (void)printf("(sqrtl(LDBL_MIN)*sqrtl(LDBL_MIN) != LDBL_MIN) = %d\n",
                 (sqrtl(LDBL_MIN)*sqrtl(LDBL_MIN) != LDBL_MIN));

    x = DBL_MAX;
    y = LDBL_MAX;
    if (!(x < y)) return (1);

    x = DBL_MAX;
    if (!(x > 0.5 * x)) return (1);

    y = LDBL_MAX;
    if (!(y > 0.5L * y)) return (1);

    x = DBL_EPSILON;
    y = LDBL_EPSILON;
    if (!(y < x)) return (1);

    x = DBL_MIN;
    y = LDBL_MIN;
    if (!(y < x)) return (1);

    x = DBL_MIN;
    if (!(x > 0.0)) return (1);

    y = LDBL_MIN;
    if (!(y > 0.0L)) return (1);

    x = DBL_MAX_EXP;
    y = LDBL_MAX_EXP;
    if (!(x < y)) return (1);

    x = DBL_MANT_DIG;
    y = LDBL_MANT_DIG;
    if (!(x < y)) return (1);

    x = sqrt(DBL_MIN);
    if (x*x != DBL_MIN) return (1);

    y = sqrtl(LDBL_MIN);
    if (y*y != LDBL_MIN) return (1);

    return (0);
}

This now correctly fails to link with -lm on NetBSD.

However, on Sun Solaris, use of long double library routines it needs
an extra nonstandard command-line option, -lsunmath.  Although native
cc and CC compilers know where to find it, gcc and lcc do not; they
then need something like

        gcc zzlongdouble.c -R/opt/SUNWspro/lib -L/opt/SUNWspro/lib -lsunmath -lm

to correctly link and subsequently run.

If the gcc folks had put support for Sun's additional libraries into
gcc's built-in library path, this ugliness would not be necessary, and
I'm not happy that it be embedded in configure scripts either, unless
autoconf developers feel that we can live with that necessity.

I'm not entirely happy with the tests

        sqrt(DBL_MIN)*sqrt(DBL_MIN) != DBL_MIN
        sqrtl(LDBL_MIN)*sqrtl(LDBL_MIN) != LDBL_MIN

either, because they rely both on accurate square-root routines, and
on DBL_MIN and LDBL_MIN being negative even powers of two.  The latter
is the case for IEEE 754 arithmetic, which is almost universal today,
but it could fail on other floating-point architectures.

Perhaps it would be sufficient to test instead:

        sqrt(DBL_MIN)   > DBL_MIN
        sqrtl(LDBL_MIN) > LDBL_MIN

Comments?

-------------------------------------------------------------------------------
- Nelson H. F. Beebe                    Tel: +1 801 581 5254                  -
- University of Utah                    FAX: +1 801 581 4148                  -
- Department of Mathematics, 110 LCB    Internet e-mail: address@hidden  -
- 155 S 1400 E RM 233                       address@hidden  address@hidden -
- Salt Lake City, UT 84112-0090, USA    URL: http://www.math.utah.edu/~beebe  -
-------------------------------------------------------------------------------




reply via email to

[Prev in Thread] Current Thread [Next in Thread]