Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 27 Jun 2008 12:51:47 +1000 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Satoshi Kimura <satosi.kimura@nifty.FreeBSD.ORG>, com@FreeBSD.org
Cc:        freebsd-gnats-submit@FreeBSD.org, freebsd-i386@FreeBSD.org
Subject:   Re: i386/125011: precision of constants for long double
Message-ID:  <20080627111944.V85486@delplex.bde.org>
In-Reply-To: <200806261549.m5QFnIcM058517@www.freebsd.org>
References:  <200806261549.m5QFnIcM058517@www.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, 26 Jun 2008, Satoshi Kimura wrote:

>> Description:
> 1. problem
>  On rather new environment of FreeBSD (ex. 6.2, 7.0),
> standard gcc (i.e /usr/bin/gcc) makes strange code
> concerning "long double".  The precision of all constants
> of "long double" are rounded to that of "double". In addition,
> if we add -m128bit-long-double option to gcc, this behavior
> did not appear.

I think not rounding for -m128bit-long-double is a bug, but it should
be considered a feature, since there seems to be no other way to
generate full precision for long doubles.  It seems to work for compile
time evaluation too:

%%%
   long double lc;

   lc = 1.0L + LDBL_EPSILON;
%%%

Note that <float.h> is broken by default -- the LDBL_EPSILON in it
doesn't actually work, since compile time expressions are evaluated
in the default runtime precision which is plain double and the
LDBL_EPSILON in <float.h> is only correct for the non-default precision
of extended; thus 1.0L + LDBL_EPSILON always gives 1.0L at compile
time, while at runtime it does the same by default, but if the runtime
precision is changed to extended, then it actually gives the correct
result of 1.0L plus a little.

The above paragraph is with the default compiler options.
-m128bit-long-double apparently causes the evaluation to be done in
extended precision, so LDBL_EPSILON works in expressions at compile
time, but gives results inconsistent with the default at runtime.

>  Historically, precision of constants of "long double" were
> as follows:
>
> FreeBSD  gcc       96bit   128bit
> 2.2.6    2.2.2.1    NR      -
> 3.4      2.7.2.3    NR      -
> 4.9      2.95.4     NR      -
>         3.0.4      NR      NR
> 5.3      3.2.3      NR      NR
>         3.3.5      R       R      <- (1)

gcc-3.3.3 in 5.2-CURRENT also generates garbage in the unused top 32
bits of a 128-bit long double.  This is fixed in at least gcc-4.2.1
in 8.0-CURRENT.

>         3.4.2      NR      NR     <- (2)
> 6.2      3.4.6      R       NR     <- (3)
> 6.3      3.4.6      R       NR
>         4.2.4      R       NR
> 7.0      4.2.1      R       NR
>
> Meanings of this table are as follows:
> FreeBSD : version of FreeBSD (RELEASE)
> gcc     : version of gcc
> 96bit   : 96bit long double
> 128bit  : 128bit long double (compiled with -m128bit-long-double option)
> NR      : not rounded, i.e keep precision of long double.
> R       : rounded to precision of double.
>
> 2. question
>  On above table, modification from (2) to (3) was made by
> discussions at
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=17778
>
>  The reason of this midification was as follows:
>> On FreeBSD/i386, FPU precision is set to 53 bits to avoid problems with extra
>> precision and double rounding for doubles.  gcc understands this and evaluates
>> FP constants in 53-bit precision, even for long doubles.
>
>  He compared (1) and (2) on above table, and concluded to
> resume the condition of (1). But, I cannot find the reason
> that 128bit "long double" did not modified. ((3) on above table.)
>
> The most impotant question is that his understanding above is
> correct or not ? If false, this modification is to be avoided.
> Or, if true, 128bit "long double" is also to be modified.

I think it is just a bug that -m128-bit-long double gives a different
handling of precision.  According to gcc.info, this and the
-m96bit-long-double options are is only intended to control the size of
the long double type.  It is explicitly stated that "neither of these
options enable any extra precision over the x87 standard of 80 bits for
a long double".  But FreeBSD doesn't use the standard precision and
gcc.info doesn't say anything about this.

There should be a way to generate long double constants with full
precision intentionally, without breaking the ABI by using 128-bit
ones.  Similarly for the compile time precision.  Similarly for
rounding modes.

FreeBSD now uses horrible complications in tables of long double
constants in files like k_tanl.c in order to avoid the problem of the
compiler "always" giving plain double precision.  I once tried
-m96-bit-long-double to get full precision, but it didn't work.  I
didn't know about -m128-bit-long-double, and it didn't give extra
precision at the time (with gcc-3.3.3).  The tables now obtain full
precision by representing long doubles as the some of 2 doubles.  This
is actually the fastest way to handle long doubles on some i386's, but
doing it as an optimization belongs in the compiler.  gcc already does
the related optimization of representing double constants (including
non-literal ones, even in arrays in some cases) as floats if float
precision is enough.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20080627111944.V85486>