real x
parameter ( x = 3.141592653589793 )
double precision y
intrinsic dble
c
y = dble( x )
print "('x = ', z8)", x
print "('y = ', z16)", y
end
produces the output :
x = 40490fdb
y = 400921fb54442d18 <---- Y not = X padded with zeros
However, if the parameter statement is changed to x = 3.141592653589793 / 1.0
the output is :
x = 40490fdb
y = 400921fb60000000 <---- Y = X padded with zeros
So, in one case the expression in the parameter statement is evaluated
in the precision of X and in the other case it isn't. Is this standard
conforming?
Thanks,
Tom Fairgrieve
Yes. The standard makes no requirements on conversion precision or
the precision of operators (like divide). It only requires that the
storage for a double precision variable be twice that required for a
single (and Fortran 90 now requires the internal format of floats to
conform to a certain model used by the new numeric inquiry functions). Parameters are not allowed to be storage associated, so they
can (by very murky logic) be any precision the implementor likes.
I recommended some specific language in the standard specifying conversion
precision and operation precision minima for operations like this (in terms
of the values given by the numeric inquiry functions, for example). The
committee's response was that precision issues were beyond the scope of
the language standard.
I would prefer that the rules be: all numeric literals have a universal
type and that they be converted according to the context of their first
use; since all variables and/or parameters have specific types associated
with them, the first use of a literal assigned or operated on with such
a variable or parameter is clear; a numeric literal passed as an argument
to a procedure is passed as a single precision value unless an interface
specification for the procedure is present to denote the correct use
explicitly.
In the above example, these rules would call for the literals to be
converted as singles since that is the type of the parameter they are
being assigned to. These rules can be explicitly overriden by passing
the literal to a type coercion intrinsic as the first use. That is:
real x
parameter (x=dble(3.141592653589793 / 3.0))
This would convert the literals and compute PI/3 in double before rounding
to single to be assigned to X. As I said, this is my preferred set of
rules. In the past, there has been considerable controversy on the net
over this very issue.
--
J. Giles
This is a matter between you and your vendor.
--
Walt Brainerd wa...@netcom.com
Unicomp, Inc. +1-415-949-4052
235 Mt. Hamilton Ave. +1-415-949-4058 (fax)
Los Altos, CA 94022 USA "F90--Fortran for the '90s"
produces the output :
The behavior is standard-conforming, as others have pointed out. Indeed,
the problem is that the standard is not specific about how precision is
handled in such cases.
The standard permits a variety of approaches to be taken by compilers.
The most easily identified and agreed-upon approach is what I call
the "minimalist" approach. Even those who don't like it can easily agree
on what it _is_.
The minimalist approach says that the precision for a numeric constant like
3.141592653589793 is determined entirely by that constant independent of
context (ignoring a few places in Fortran where things that look like
numeric constants really aren't, like FORMAT statements). Given that
determination, the effective value of that numeric constant is based on
that precision.
Because 3.141592653589793 is a single-precision REAL constant (no trailing
"D0" to make it double-precision), it's effective value would be, for
example (on a typical machine), 3.141593. That is, it would be truncated
or rounded (or whatever) to the precision supported by REAL (and, presumably,
converted to binary at the same time, so we're talking _approximately_
3.141593).
This might not seem to bad to you based on your example, but consider this:
DOUBLE PRECISION X
PARAMETER (X = 3.141592653589793)
Here, most Fortran people would not notice the missing "D0" at the end of
the constant and thus assume X had a value approximating the specified
constant. Instead, the minimalist approach (the one minimally conforming
to the standard, and probably intended by the standard-writers) would
mandate that, on the same sample machine, X end up with a value approximately
equal to 3.141593D0. (That is, when printed, the value might be displayed
as 3.141593000000, 3.14159264838358, 3.141593454382, or whatever, but probably
_not_ 3.141592653589793 as most people would expect.)
(Strictly speaking, some people claim the standard would permit the value
to be displayed as "4.000000000", but that's hardly helpful in understanding
what reasonable compilers do. :-)
So, to solve this apparent difficult using a means other that continually
responding to bug reports by saying "Hey, silly user, append 'D0' to your
constants when you really want them to be double-precision", many compilers
are written to leave unspecified the precision of numeric constants.
The idea is that the precision of S is unspecified while used in context C.
S is the set of entities in a Fortran program unit that would, in a
minimalist implementation, normally be assigned a particular precision.
C is the set of contexts in a Fortran program unit in which entities in
set S might be valid.
Many compilers put not only numeric constants but named constants as well into
set S, which results in the behavior you see with your compiler. That is,
the precision for 3.141592653589793 is not determined up front; nor is the
precision for named constants, such as X in your program, despite the fact
that you indicated it should be single-precision via "REAL X".
Many compilers also put "assignment within a PARAMETER statement", "intrinsic
conversion functions", and "implicit conversion" into set C. Again, this is
why you see the DBLE() function effectively perform no actual conversion --
its input is not REAL X as you might expect, but instead the unspecified-
precision named constant X with the unspecified-precision numeric constant
value 3.141592653589793. Only when the compiler sees that the context of
DBLE() itself is not in C does it realize it must decide on a precision --
at which point it knows that DOUBLE precision is desired (returned by DBLE())
and thus converts 3.141592653589793 to DOUBLE precision.
Now, your compiler does not put "floating-point division" into set C, which
is why you get a different result when you do
PARAMETER (X = 3.141592653589793 / 1.0)
So, the compiler decides on precisions for the / operator first. Because
it's operands both have unspecified precision, it chooses single precision.
(I suppose it could choose double or something else, but I'm not sure if
that'd be standard-conforming). It then converts both operands (numeric
constants) to single precision, does the division, and _then_ makes the
assignment to the named constant X (which it might still treat as having
unspecified precision, but the value has no more precision than single
precision).
When your compiler sees DBLE(X) in this latter case, it already has chopped
X into single-precision, so to DBLE() it it must extend it with zeros. In
your compiler's case, it extends with binary zeros; other compilers could
keep X in decimal and extend it with decimal zeros (which would probably look
like binary garbage in your example) and still be standard-conforming.
The upshot of all this is that, although the minimalist approach has _obvious_
problems that anyone can point out (as I did above) and run into, it has
the advantage that it's problem are indeed _more_ obvious than the extended
approaches.
Why?
Because with the minimalist approach, everyone can at least agree that
sets S and C are empty. That is, there are no unspecified-precision entities
and, correspondingly, no contexts in which unspecified-precision entities
have their unspecifiedness preserved.
With the extended approaches used by various compilers, it gets a _lot_ easier
to do certain popular things in Fortran, like make double-precision versions
of procedures written and tested in single-precision, and so on. (This
would have been easier if F77 and/or F90 had provided for IMPLICIT types and
prceisions for numeric constants like it does for named constants and
variables; but only _somewhat_ easier.)
However, it also gets a _lot_ easier to _think_ you know the value of a
given constant, named constant, or variable, and then find that the value
changes substantially when you make what appears to be a minor change to
the program.
Moreover, because compilers disagree over what is in the sets S and C,
any code you write depending on nonempty sets S and C is not only going
to be unportable to systems with minimalist compilers (remember, minimalist
here does _not_ mean nonoptimizing!), but might be unportable to systems
with _differing_ ideas of what is in sets S and C.
For example, another compiler might put floating-point operators + - * / in
set C, thus doing your
PARAMETER (X = 3.141592653589793 / 1.0)
as a _decimal_ floating divide, meaning X would then be an unspecified-
precision named cosntant with the unspecified-precision numeric-constant
value 3.141592653589793!
Yet another compiler might not put DBLE() into set C, meaning your _first_
example would probably work differently.
The problem here is that you might not notice there are problems, or be able
to find where the problems occur -- subtle differences in numeical
results might be all you ever see, if you're lucky enough to see it.
Because of this, as the author of GNU Fortran, I've decided to punt the
issue and have GNU Fortran support _only_ the minimalist approach initially.
This'll make it slightly incompatible with many compilers (including
f2c), but only until we decide what to do. Of course, most other compilers
aren't distributed in source form and probably don't offer their users the
option of choosing _which_ approach to use; GNU Fortran might someday, though
personally I've got better things to spend my time on (and hope others will
make the necessary changes). It might turn out to be the case that the
best thing GNU Fortran can do for the Fortran community is stick to the
minimalist approach but, when asked to do so, _warn_ users when their
programs contain constructs that might result in differing precisions in
appropriate situations. Whatever is decided, it'll be fairly easy to add
to the compiler. (And, in any case, the "slight incompatibilities" won't
be much worse than the other incompatibilities between the various compilers,
just perhaps more obviously incompatible.)
Ideally, though, we should have a language where precision (and even type)
issues are resolved only at the last possible moment, and backtraced (by the
compiler) to determine the precisions for source operands (like constants,
named constants, and named variables). Fortran isn't that language, nor is
C or many others, however.
In summary, the basic problem with the minimalist approach is that it's
obviously intuitively wrong in many simple cases; the problem with any
other approach is that it _is_ wrong, just in less obvious ways, and _that_
might make it more dangerous for users who aren't compiler experts. (The
idea that a constant like 3.141592653589793 might have two or three different
values depending on context might or might not be totally off the wall to
a given Fortran user; the minimalist approach provides only one value for
such a constant, any other approach mandates multiple values, but sometimes
that seems to make more sense.)
This is probably more than anyone wants to know about the issue. I hope the
info is accurate as well.
--
James Craig Burley, Software Craftsperson bur...@gnu.ai.mit.edu
Member of the League for Programming Freedom (LPF)