Summary: | Wrong numeric limits in system headers? | ||
---|---|---|---|
Product: | Base System | Reporter: | Václav Haisman <v.haisman> |
Component: | standards | Assignee: | freebsd-standards (Nobody) <standards> |
Status: | Open --- | ||
Severity: | Affects Only Me | CC: | lwhsu |
Priority: | Normal | ||
Version: | Unspecified | ||
Hardware: | Any | ||
OS: | Any |
Description
Václav Haisman
2008-12-30 22:40:01 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 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. 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
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. 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.
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 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 |