[Proj] Re: Re: Proj4 Bug (rtodms)
Glynn Clements
glynn at gclements.plus.com
Fri Nov 10 11:05:13 PST 2006
Gerald I. Evenden wrote:
> > > The numbers have effectively right and exact representations in ten-bytes
> > > format, but the compiler instead of making r/60 makes r * 1/60 so it
> > > changes a little thing. I precise that this is done without any
> > > optimization (force no optimization) and with option "don't correct FDIV
> > > flaw" (for old processors) and "no quick floating points".
> >
> > In that case, the Borland compiler is entirely useless for any code
> > which makes non-trivial use of floating-point arithmetic.
> >
> > Even if this particular issue can be worked around, you are likely to
> > encounter similar issues time and time again.
> >
> > Any compiler written by someone who doesn't understand that x/y and
> > x*(1/y) aren't equivalent simply isn't worth the disk space it
> > occupies.
>
> That argument does not hold too well. I altered the origin problem as
> follows;
>
> #include <math.h>
> #include <stdio.h>
> static int res1,res2;
> int main()
> {
> double r=240.0;
> // double tmp = 1./60.;
> double tmp = 0.016666666666666666666666666666666666666;
> // res1 = r / 60.;
> res1 = r * tmp;
> res2 = floor(r / 60.);
> printf("%s\n",(res1==res2) ? "OK!" : "NOT OK!");
> printf("res1: %d res2: %d\n", res1, res2);
> // getch();
> }
>
> Both cases (1/60., 0.01666--) run as:
>
> ./a.out
> OK!
> res1: 4 res2: 4
>
> using a temporary value for "1/60." a la Borland and in both cases the
> procedure ran "OK."
FWIW, when I try this with:
gcc (GCC) 3.3.6 (Gentoo 3.3.6, ssp-3.3.6-1.0, pie-8.7.8)
on a Pentium 4, I get "OK" if I compile with optimisation (-O1 or
higher), and "NOT OK" if I compile either without optimisation, or
with both optimisation and -ffloat-store. I get the same result
regardless of whether tmp is initialised with 1./60. or 0.01666...
Also, if I use "long double" throughout (including adding a trailing L
to the FP literals), I get "OK" regardless of the optimisation
settings.
None of which is surprising when you consider the value of 1/60 at
various precisions:
precision bits value
single 32 0.0166666675359010696411133
double 64 0.0166666666666666664353702
extended 80 0.0166666666666666666674572
At double precision (64 bits), the rounded value is less than 1/60,
while at extended precision (80 bits) it's greater than 1/60. The
result of r*tmp would be similarly be less than or greater than 4.0.
As both the integer cast and floor round positive numbers downwards,
using double precision would result in a value slightly less than 4.0
being rounded down to 3, while extended precision would result in a
value slightly greater than 4.0 being rounded down to 4.
The x86 FPU has 80-bit registers, but "double" values will be reduced
to 64 bits when stored in memory. Optimisation makes it more likely
that intermediate results will be kept in registers (and thus not
truncated), while -ffloat-store causes all FP values to be stored into
memory then read from there, forcibly discarding any excess precision
which the FPU may provide:
`-ffloat-store'
Do not store floating point variables in registers, and inhibit
other options that might change whether a floating point value is
taken from a register or memory.
This option prevents undesirable excess precision on machines such
as the 68000 where the floating registers (of the 68881) keep more
precision than a `double' is supposed to have. Similarly for the
x86 architecture. For most programs, the excess precision does
only good, but a few programs rely on the precise definition of
IEEE floating point. Use `-ffloat-store' for such programs, after
modifying them to store all pertinent intermediate computations
into variables.
Of course, none of this is relevant to computing 240.0/60.0, which
isn't affected by optimisations[1] or rounding modes.
[1] Except for -funsafe-math-optimizations (also used by -ffast-math),
which specifically enables this kind of approximation. It is never
turned on by any -O setting:
`-funsafe-math-optimizations'
Allow optimizations for floating-point arithmetic that (a) assume
that arguments and results are valid and (b) may violate IEEE or
ANSI standards. When used at link-time, it may include libraries
or startup files that change the default FPU control word or other
similar optimizations.
This option should never be turned on by any `-O' option since it
can result in incorrect output for programs which depend on an
exact implementation of IEEE or ISO rules/specifications for math
functions.
The default is `-fno-unsafe-math-optimizations'.
`-ffast-math'
Sets `-fno-math-errno', `-funsafe-math-optimizations',
`-fno-trapping-math', `-ffinite-math-only' and
`-fno-signaling-nans'.
This option causes the preprocessor macro `__FAST_MATH__' to be
defined.
This option should never be turned on by any `-O' option since it
can result in incorrect output for programs which depend on an
exact implementation of IEEE or ISO rules/specifications for math
functions.
--
Glynn Clements <glynn at gclements.plus.com>
More information about the Proj
mailing list