!standard 4.09 (38) 01-05-14 AI95-00268/01
!class binding interpretation 01-05-14
!status work item 01-05-14
!status received 01-05-14
!qualifier Error
!priority Medium
!difficulty Medium
!subject Rounding of real static expressions
!summary
The rounding static floating point expressions is implementation-defined.
It is recommended that the rounding is the same as the default rounding on the
target system.
!question
4.9(38) requires biased rounding of static floating point expressions. This
often is different than the runtime rounding of floating point expressions
(which is usually unbiased rounding for IEEE machines). That means that
there are anomalies; the value of a static expression can be required to be
different than the same expression evaluated at runtime. Is this intended? (No.)
!recommendation
(See summary.)
!wording
(See corrigendum.)
!discussion
!corrigendum 4.9(38)
@drepl
For a real static expression that is not part of a larger static
expression, and whose expected type is not a descendant of a formal scalar
type, the implementation shall round or truncate the value (according to the
Machine_Rounds attribute of the expected type) to the nearest machine number
of the expected type; if the value is exactly half-way between two machine
numbers, any rounding shall be performed away from zero. If the expected type
is a descendant of a formal scalar type, no special rounding or truncating is
required - normal accuracy rules apply (see Annex G).
@dby
For a real static expression that is not part of a larger static
expression, and whose expected type is not a descendant of a formal scalar
type, the implementation shall round or truncate the value (according to the
Machine_Rounds attribute of the expected type) to the nearest machine number
of the expected type; if the value is exactly half-way between two machine
numbers, the rounding performed is implementation-defined. If the expected type
is a descendant of a formal scalar type, no special rounding or truncating is
required - normal accuracy rules apply (see Annex G).
@i<@s8>
For a real static expression that is not part of a larger static
expression, and whose expected type is not a descendant of a formal scalar
type, the rounding should be the same as the default rounding for the target
system.
!ACATS test
This is implementation advice, and thus is not testable. Existing tests
C490001 and C490002 test the modified rule, and have been changed to eliminate
these tests.
!appendix
From: Robert Dewar
Sent: Wednesday, March 28, 2001 10:46 PM
One possibility would be to eliminate the obnoxious rounding rule. I must
say I never noticed this during review, and it seems quite horrible to me3
to require that static expressions get the wrong result.
As for infinite precision, well you are always free to use infinite
precision in evaluating expressions anway, because of 11.6 stuff (I
must say I can't understand the new 11.6, but it must allow out of
range intermediate values :-)
Of course to *require* the infinite precision in the body would be
disastrously wrong.
****************************************************************
From: Pascal Leroy
Sent: Thursday, March 29, 2001 2:21 AM
> One possibility would be to eliminate the obnoxious rounding rule. I must
> say I never noticed this during review, and it seems quite horrible to me
> to require that static expressions get the wrong result.
There are two issues here: do we want rounding? and if we do, do we want biased
or unbiased rounding?
I assume we have a consensus that biased rounding was the wrong choice, the RM
should either require unbiased rounding or left mid-point rounding
implementation-defined.
But I would even argue that rounding is obnoxious regardless of the mid-point
issue. A few years ago we had a complaint from a customer who had code like:
X := Y / 3#0.1#;
Well, the divisor was not the literal 3#0.1#, it was some complicated universal
expression which turned out to be exactly 1/3.
In Ada 83 we transformed this into 3.0 * Y, which was both fast and accurate.
In Ada 95 we have to round 3#0.1# to some obscure machine number, and then
generate a division. This is both way slower and significantly less accurate...
****************************************************************
From: Robert Dewar
Sent: Thursday, March 29, 2001 5:06 AM
<>
This is a very annoying and incorrect transformation for a compiler to make.
You *definitely* do not want a compiler fiddling with floating-point
expressions. The expressions
3.0 * Y
Y / 3#0.1#
are simply different, and have different semantics. It is a huge advantage
of Ada that it stops compilers from fiddling around like this. I consider
that this was quite inappropriate in Ada 83 as well (it is arguable whether
or not it was allowed, I would say no, but in any case it is a good thing
that in Ada 95 it is made clear that this kind of transformation is not
allowed).
We definitely DO want rounding, and it is important that it be properly
defined how the rounding should work.
I think it is reasonable to argue that the rounding should be done in a
manner consistent with the run-time rounding mode. Either this should be
required, or it should be at least permitted. The current rules which require
it to be done wrong are very irritating (irritating in exactly the same
kind of manner that the transformation that Pascal mentions is irritating).
Yes, of course we know that a floating point multiplication is faster than
a division. So does any competent programmer. Indeed if a programmer writes
the unusual expression
Y / 3#0.1#
the *only* legitimate explanation is that it is VERY deliberately different
from a multiplication by 3.0. Compilers should NOT assume that the programmer
intended something different than what they wrote.
Floating-point is NOT an easy domain, it is after all not even valid to
replace A+B by B+A in IEEE arithmetic.
Incidentally I would consider it quite acceptable to distinguish strict
mode from relaxed mode here, and allow this kind of transformation in
relaxed mode. So I would not oppose some language that says that the
rounding rule applies only in strict mode.
****************************************************************
From: Pascal Leroy
Sent: Thursday, March 29, 2001 6:23 AM
> Floating-point is NOT an easy domain, it is after all not even valid to
> replace A+B by B+A in IEEE arithmetic.
That statement surprises me (but then I have been surprised before). Can you
give an example, just to enlighten me?
> Can you point to this AI, it would help inform the current discussion, and
> in some sense would be decisive on the issue, since we intend to be back
> compatible with both the Ada 83 RM and relevant AI's.
AI83-00001 makes a subtle-but-significant distinction between "denotes" and
"declares". RM83 12.3(6-14) then uses "denotes" all over the place. The
conclusion is that, outside the instance, an expression is static if all the
names in this expression "denote" static things. (I must admit that 15 years
later the logic seems a bit tenuous, but it was quite convincing at the time.)
This was deemed important for the usability of generics.
****************************************************************
From: Robert Dewar
Sent: Thursday, March 29, 2001 6:39 AM
<>
A = +0.0
B = -0.0
A+B = +0.0
B+A = -0.0
gcc is VERY careful to preserve proper IEEE semantics for negative zeroes
You should not do commutative optimizations on floating-point addition
unless you can prove that the result is not subject to this special
negative zero case (which is in general very hard to do).
I am talking here about an implementation in which Signed_Zeroes is true,
so that it conforms to IEC 559. Note that the Ada RM is quite explicit
in this regard, see for example, the subtle note in (RM G.1.1(57)) which
covers a case that is very easy to get wrong, it is very easy to assume
that real+complex should be done by first supplying a zero imaginary
component to the real and then doing a complex addition, but as this
paragraph points out, this gives the wrong result for the negative zero
case.
This stuff has been very carefully considered in Ada, especially in
Ada 95 (it was one of my special goals for Ada 95, following from
the PhD thesis work of my student Sam Figueroa on floating-point
semantics in high level languages, that Ada 95 be properly consistent
with IEEE floating-point, and this goal was indeed achieved :-)
Robert
Going back to the original topic of the forced biased rounding, we put
an unconditional warning in version 3.14a of GNAT to note that this was
happening, and our experience is that a lot of customers ran into this
warning and were puzzled, we tried to make it clear, but nothing in
floating-point is clear to most programmers :-) The warning says:
x.adb:245:27: warning: static expression does not round to even (RM 4.9(38))
after explaining this in detail to several customers, we decided to have
this particular warning separately controlled and off by default, but the
fact that the warning appears quite often means that we are not talking
about some theoretical case that never happens. On the contrary this
annoying rule requires a significant number of literals to be rounded
incorrectly, and we find this quite worrisome.
****************************************************************
From: Tucker Taft
Sent: Thursday, March 29, 2001 11:18 AM
Pascal Leroy wrote:
> > One possibility would be to eliminate the obnoxious rounding rule. I must
> > say I never noticed this during review, and it seems quite horrible to me
> > to require that static expressions get the wrong result.
>
> There are two issues here: do we want rounding? and if we do, do we want
> biased or unbiased rounding?
The original goal was portability. That would argue for either:
1) Specify unbiased rounding, per IEEE (portability between machines)
2) Specify rounding per the target machine behavior (portability
between implementations on the same machine).
Clearly (2) provides somewhat less portability (though given the enormous
preponderance of IEEE, that is more theoretical than actual),
and (2) minimizes the difference between static and non-static (again,
given the IEEE preponderance, that is pretty minor).
I guess I would vote for (2), since whether to round or not is
based on the 'Machine_Rounds attribute, so we are clearly trying
to match that aspect of the target. Might as well go all the
way. On the other hand, (2) is clearly more work because it requires
additional target-specific tables in the front end to keep track
of what sort of rounding the target machine performs.
It seems that in any case, either (1) or (2) is better than the
current "biased" rounding approach. I still believe the "biased" rounding
approach for real => integer was the right decision, but in retrospect
it seems like it was an unwise generalization of that decision to
the floating point => floating point rounding case.
>
> I assume we have a consensus that biased rounding was the wrong choice,
> the RM should either require unbiased rounding or left mid-point rounding
> implementation-defined.
I don't think implementation-defined is the right choice. It ought
to be determined by the target characteristics, not compiler implementor
whim, or be specified as always unbiased, in my view.
> [discussion of tranforming X / (1.0/3.0) into X * 3.0]...
I don't think we should allow for infinite precision
transformations. That just seems too big a can of worms.
****************************************************************
From: Randy Brukardt
Sent: Thursday, March 29, 2001 6:02 PM
> It seems that in any case, either (1) or (2) is better than the
> current "biased" rounding approach.
I certainly agree with this.
****************************************************************
From: Erhard Ploedereder
Sent: Friday, March 30, 2001 11:05 AM
As a bit of input from left-field on rounding:
I had a discussion two days ago about FP rounding with someone who seems to
know what he is talking about. He told me of significant problems across all
programming languages in actually exploiting the four modes of rounding
available on IEEE-conformant FPs. (He also told me that the (co)author of
the standard is kind of fuming that no programming language let's you get at
this capability.) The capability is needed for most accurate calculations
involving fractions of floating-point values.
There is a good chance that we might get an ada-comment to the effect
that access to those 4 modes be provided via pragmas. A 5. variant may
well be: I don't care.
I know this is not to the point in question, but it might trigger some
thoughts.
****************************************************************
From: Robert Dewar
Sent: Friday, April 06, 2001 11:19 AM
note that this discussion got hijacked by a really-completely-irrelevant
discussion of rounding to integer. Can we get it back where it was, and
ask if we can reconsider the requirement for biased rounding of floating-point
constants (well static expressions in general, but it is constants where
it is most offensive!)
****************************************************************
From: Tucker Taft
Sent: Friday, April 06, 2001 3:36 PM
I agree that static rounding should be specified as being
determined by the target, rather than always away from zero.
This implies there should be an attribute something like:
'Machine_Uses_Unbiased_Rounding
or equivalent. Perhaps this should be a specifiable attribute?
****************************************************************
From: Robert Dewar
Sent: Friday, April 06, 2001 3:43 PM
I am dubious about going that far, without taking on the entire iEEE
rounding semantics, and in particular, let's be sure not to do anything
that inteferes with this semantics.
****************************************************************
From: Robert Dewar
Sent: Friday, April 06, 2001 3:47 PM
Basically the rule should be that the rounding mode is the same as the
default rounding mode at run-time. But that can only be IA I think, the
forml rule should be that it is impl defined.
****************************************************************
From: Randy Brukardt
Sent: Friday, April 06, 2001 6:59 PM
> note that this discussion got hijacked by a really-completely-irrelevant
> discussion of rounding to integer. Can we get it back where it was, and
> ask if we can reconsider the requirement for biased rounding of floating-point
> constants (well static expressions in general, but it is constants where
> it is most offensive!)
Well, really the rounding of floating-point constants discussion had
hijacked the discussion of staticness for generic formal objects, and then
that was hijacked again.
Indeed, there are three issues that we've discussed:
1) Generic formal objects aren't static in the instance by the RM, although
all (?) compilers implement them as such. And that is necessary for Ada 83
compatibility (AI83-00001).
Care must be taken when fixing this to insure that such objects are not
required to be static in the body, otherwise we would be requiring static
rounding and exact evaluation in shared generic bodies.
Which led to the second discussion:
2) Do we really want the biased rounding of floating point static
expressions? It provides different answers than occur when the same
expression is evaluated at run-time.
That had been required by analogy to the real => integer conversion rule,
which then led to:
3) Real => Integer conversions can be pretty expensive. How do we write fast
conversions in Ada 95?
At this point, all of these discussions are attached to an existing AI on
generic staticness. I don't think (2) and especially (3) are related enough
to (1) and the other topic in AI-0263, so I think they will need their own
AI(s). Note that changing (2) does not eliminate the need to handle (1), and
vice versa.
****************************************************************
From: Robert Dewar
Sent: Friday, April 06, 2001 7:16 PM
Quite so. the relation between (2) and (1) is simply that the GNAT warning
for (2) dug up (1) :-)
****************************************************************