Bug 130067 - Wrong numeric limits in system headers?
Summary: Wrong numeric limits in system headers?
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: standards (show other bugs)
Version: Unspecified
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-standards (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-12-30 22:40 UTC by Václav Haisman
Modified: 2019-02-02 03:35 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Václav Haisman 2008-12-30 22:40:01 UTC
There seems to be a problem with definition of long double limits on FreeBSD i386/6.x.

shell::wilx:~/packed_vector> echo | g++ -dD -E -  | sort | grep LDBL_MAX
#define __LDBL_MAX_10_EXP__ 4932
#define __LDBL_MAX_EXP__ 16384
#define __LDBL_MAX__ 1.1897314953572316e+4932L

shell::wilx:~/packed_vector> fgrep -rn LDBL_MAX /usr/include
[...]
/usr/include/machine/float.h:75:#define LDBL_MAX_EXP    16384
/usr/include/machine/float.h:76:#define LDBL_MAX        1.1897314953572317650E+4932L
/usr/include/machine/float.h:77:#define LDBL_MAX_10_EXP 4932
/usr/include/float.h:75:#define LDBL_MAX_EXP    16384
/usr/include/float.h:76:#define LDBL_MAX        1.1897314953572317650E+4932L
/usr/include/float.h:77:#define LDBL_MAX_10_EXP 4932

Notice the difference in definition of LDBL_MAX, the values in system
headers are tiny bit larger than that defined by GCC itself.

machine/float.h:76:#define LDBL_MAX        1.1897314953572317650E+4932L
float.h:76:#define LDBL_MAX                1.1897314953572317650E+4932L
GCC: __LDBL_MAX__                          1.1897314953572316e+4932L

A simple test shows the following:

shell::wilx:~/tmp> cat >longdouble.cxx
#include <iostream>
#include <limits>
#include <cfloat>

int
main ()
{
  typedef std::numeric_limits<long double> limits;
  std::cout << "max: " << limits::max () << "\n";
  std::cout << "__LDBL_MAX__: " << __LDBL_MAX__ << "\n";
  std::cout << "LDBL_MAX: " << LDBL_MAX << "\n";
}

shell::wilx:~/tmp> g++ -o longdouble longdouble.cxx

shell::wilx:~/tmp> ./longdouble
max: 1.18973e+4932
__LDBL_MAX__: 1.18973e+4932
LDBL_MAX: inf

This is on 6.3/i386. 7.1/AMD64 does not print inf for LDBL_MAX. I
think this is a bug in 6.x headers or in GCC 3.4.x that it
uses. LDBL_MAX should never result in "inf".

How-To-Repeat: #include <iostream>
#include <limits>
#include <cfloat>

int
main ()
{
  typedef std::numeric_limits<long double> limits;
  std::cout << "max: " << limits::max () << "\n";
  std::cout << "__LDBL_MAX__: " << __LDBL_MAX__ << "\n";
  std::cout << "LDBL_MAX: " << LDBL_MAX << "\n";
}
Comment 1 Bruce Evans freebsd_committer 2008-12-31 11:26:22 UTC
On Tue, 30 Dec 2008, Vaclav Haisman wrote:

> There seems to be a problem with definition of long double limits on FreeBSD i386/6.x.
>
> shell::wilx:~/packed_vector> echo | g++ -dD -E -  | sort | grep LDBL_MAX
> #define __LDBL_MAX_10_EXP__ 4932
> #define __LDBL_MAX_EXP__ 16384
> #define __LDBL_MAX__ 1.1897314953572316e+4932L
>
> shell::wilx:~/packed_vector> fgrep -rn LDBL_MAX /usr/include
> [...]
> /usr/include/machine/float.h:75:#define LDBL_MAX_EXP    16384
> /usr/include/machine/float.h:76:#define LDBL_MAX        1.1897314953572317650E+4932L
> /usr/include/machine/float.h:77:#define LDBL_MAX_10_EXP 4932
> /usr/include/float.h:75:#define LDBL_MAX_EXP    16384
> /usr/include/float.h:76:#define LDBL_MAX        1.1897314953572317650E+4932L
> /usr/include/float.h:77:#define LDBL_MAX_10_EXP 4932
>
> Notice the difference in definition of LDBL_MAX, the values in system
> headers are tiny bit larger than that defined by GCC itself.
> ...
> machine/float.h:76:#define LDBL_MAX        1.1897314953572317650E+4932L
> float.h:76:#define LDBL_MAX                1.1897314953572317650E+4932L
> GCC: __LDBL_MAX__                          1.1897314953572316e+4932L

This has never worked.  However, since gcc became aware of the limited
precision of long doubles under FreeBSD a few years ago, it shouldn't
be as broken as it is.  Gcc defines __LDBL_MAX__ to have the correct
value (1-2^-53)*2^1024 (rounded to 17 digits, which is enough for the
actual precision of 53 bits), while FreeBSD defines LDBL_MAX as
(1-2^-64)*2^1024 (rounded to 20 digits, which is perhaps not quite
enough for the non-actual precision of 64 bits (either this 20 or
DECIMAL_DIG's value of 21 is dubious).  The wrong value in FreeBSD
may have worked accidentally a few years ago, but now hit has no
chance of working:
- a few years ago: gcc didn't know about FreeBSD's limited precision,
   so it evaluated the constant in 64-bit precision and got precisely
   (1-2^-64)*2^1024 (only the decimal constant is imprecise).  Static
   initialization to this value preserved the value.  Actual use of
   the value caused it to be rounded to 53 bits.  I think that still
   made it Infinity in most cases.
- now: gcc evaluates it in 53-bit precision and gets Infinity for it
   consistently.

Many other long double constants in FreeBSD's <float.h> have never
worked, and are much further from neing correct, but are fixed in
gcc:

From gcc4.2 -E -dM:
% #define __LDBL_MAX__ 1.1897314953572316e+4932L
% #define __LDBL_MAX_EXP__ 16384
% #define __LDBL_HAS_INFINITY__ 1
% #define __LDBL_MIN__ 3.3621031431120935e-4932L
% #define __LDBL_HAS_QUIET_NAN__ 1
% #define __LDBL_HAS_DENORM__ 1
% #define __LDBL_EPSILON__ 2.2204460492503131e-16L
% #define __LDBL_DIG__ 15
% #define __LDBL_MANT_DIG__ 53
% #define __LDBL_MIN_EXP__ (-16381)
% #define __LDBL_MAX_10_EXP__ 4932
% #define __LDBL_DENORM_MIN__ 7.4653686412953080e-4948L
% #define __LDBL_MIN_10_EXP__ (-4931)

From float.h:
% #define LDBL_MANT_DIG	64

Wrong; should be 53.  All the other errors are derived from this
(see the definitions of FLT_* for the derivations -- p must be 53
but is 64).

% #define LDBL_EPSILON	1.0842021724855044340E-19L

Wrong: too small by a factor of 2^13.

% #define LDBL_DIG	18

Wrong: unnecessarily large (fairly harmless).

% #define LDBL_MIN_EXP	(-16381)

Correct.

% #define LDBL_MIN	3.3621031431120935063E-4932L

Correct (just has more precision than needed for rounding to 53 bits).

% #define LDBL_MIN_10_EXP	(-4931)
% #define LDBL_MAX_EXP	16384

Correct.

% #define LDBL_MAX	1.1897314953572317650E+4932L

Wrong: see above.

% #define LDBL_MAX_10_EXP	4932

Correct.

Bruce
Comment 2 David Schultz freebsd_committer 2009-01-06 19:03:13 UTC
On FreeBSD/i386, long doubles are represented with 64 bits of
precision, but computations are performed with 53 bits of
precision. In a sane world, this discrepancy wouldn't exist, but
for reasons I won't get into, they do, and probably always will.

C99 defines the LDBL constants based on what can be represented,
not what can be computed as the result of arithmetic operations,
so my interpretation is that the values in float.h are correct,
though confusing.
Comment 3 Václav Haisman 2009-01-08 21:56:59 UTC
David Schultz wrote, On 6.1.2009 20:03:
> On FreeBSD/i386, long doubles are represented with 64 bits of
> precision, but computations are performed with 53 bits of
> precision. In a sane world, this discrepancy wouldn't exist, but
> for reasons I won't get into, they do, and probably always will.
> 
> C99 defines the LDBL constants based on what can be represented,
> not what can be computed as the result of arithmetic operations,
> so my interpretation is that the values in float.h are correct,
> though confusing.
I am not language lawyer but even if it were true that the constants are
right, there is still the problem that they (especially the LDBL_MAX value)
are useless with the provided GCC. Either GCC or the headers should be
changed. Otherwise the constants are rather useless and unusable.

--
VH
Comment 4 John E. Hein 2009-01-09 15:57:26 UTC
V=E1clav Haisman wrote at 22:56 +0100 on Jan  8, 2009:
 > David Schultz wrote, On 6.1.2009 20:03:
 > > On FreeBSD/i386, long doubles are represented with 64 bits of
 > > precision, but computations are performed with 53 bits of
 > > precision. In a sane world, this discrepancy wouldn't exist, but
 > > for reasons I won't get into, they do, and probably always will.
 > > =

 > > C99 defines the LDBL constants based on what can be represented,
 > > not what can be computed as the result of arithmetic operations,
 > > so my interpretation is that the values in float.h are correct,
 > > though confusing.
 > I am not language lawyer but even if it were true that the constants a=
re
 > right, there is still the problem that they (especially the LDBL_MAX v=
alue)
 > are useless with the provided GCC. Either GCC or the headers should be=

 > changed. Otherwise the constants are rather useless and unusable.

FWIW, when you compile the OP's sample code on i386 with -pedantic
(with 6.x's base gcc 3.4.6 or 7.x's base gcc 4.2.1), you get:

x.cc:11: error: floating constant exceeds range of 'long double'

(the LDBL_MAX line)

That seems to tip the scale more to the 'float.h is wrong' side.
Comment 5 David Schultz freebsd_committer 2009-01-09 18:41:36 UTC
On Fri, Jan 09, 2009, John Hein wrote:
> FWIW, when you compile the OP's sample code on i386 with -pedantic
> (with 6.x's base gcc 3.4.6 or 7.x's base gcc 4.2.1), you get:
> 
> x.cc:11: error: floating constant exceeds range of 'long double'
> 
> (the LDBL_MAX line)
> 
> That seems to tip the scale more to the 'float.h is wrong' side.

gcc doesn't do quite the right thing with long double constants in
FreeBSD, and it causes no end of trouble when writing long double
routines that are intended to work when the FPU is switched to
extended precision mode.

Older versions of gcc would evaluate long double constant
expressions at compile time using extended precision, which was
wrong because it didn't reflect what the FPU would have done at
runtime. More recent versions of gcc were "fixed" to evaluate all
long double constants using double precision, which matches what
the FPU does by default. However, now it's not even possible to
write a program that uses long double constants, even if the
program changes the FPU precision at runtime, because gcc
truncates all the constants at compile time (and generates
spurious complaints such as the one you mention). C99 defines some
pragmas that would improve the situation, but gcc doesn't
implement them.
Comment 6 Bruce Evans freebsd_committer 2009-01-10 11:35:59 UTC
On Fri, 9 Jan 2009, David Schultz wrote:

> On Fri, Jan 09, 2009, John Hein wrote:
>> FWIW, when you compile the OP's sample code on i386 with -pedantic
>> (with 6.x's base gcc 3.4.6 or 7.x's base gcc 4.2.1), you get:
>>
>> x.cc:11: error: floating constant exceeds range of 'long double'
>>
>> (the LDBL_MAX line)
>>
>> That seems to tip the scale more to the 'float.h is wrong' side.

No, it is strictly a bug in gcc (gcc is supposed to support FreeBSD's
idea of a long double, since the special support is only used by
FreeBSD, but it doesn't).  The FreeBSD value of LDBL_MAX doesn't exceed
the range of a long double with FreeBSD's idea of a long double.
However, almost any operation on FreeBSD's LDBL_MAX would overflow.
E.g., (LDBL_MAX + 0) and (LDBL_MAX * 1) both overflow to give a result
of Inf.  There may be optimization bugs related to this -- IIRC,
evaluating x+0 as x at compile time is an invalid optimization, because
x+0 should differ from x iff x is -0, but evaluating x*1 as x at compile
time is a valid optimization because there should be no special cases;
however LDBL_MAX*1 gives a special case.

> gcc doesn't do quite the right thing with long double constants in
> FreeBSD, and it causes no end of trouble when writing long double
> routines that are intended to work when the FPU is switched to
> extended precision mode.

I think it does do the right thing from its viewpoint.  In its viewpoint,
long doubles have only 53 bits of precision.  Since it refuses to
construct long doubles with the full 64 bits of precision that are
possible and expected by FreeBSD, this is a valid viewpoint -- there
is no way to construct a long double with more than 53 bits of precision
in C without invoking undefined behaviour, so the extra bits might as
well not be there.  This viewpoint also avoids surprises like
LDBL_MAX*1 = Inf.

> Older versions of gcc would evaluate long double constant
> expressions at compile time using extended precision, which was
> wrong because it didn't reflect what the FPU would have done at
> runtime. More recent versions of gcc were "fixed" to evaluate all
> long double constants using double precision, which matches what
> the FPU does by default. However, now it's not even possible to
> write a program that uses long double constants, even if the
> program changes the FPU precision at runtime, because gcc
> truncates all the constants at compile time (and generates
> spurious complaints such as the one you mention). C99 defines some
> pragmas that would improve the situation, but gcc doesn't
> implement them.

This is true.

Bruce
Comment 7 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 08:00:52 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped