!standard 4.09 (38) 01-09-07 AI95-00268/03 !class binding interpretation 01-05-14 !status WG9 approved 01-10-05 !status ARG approved 8-0-0 01-05-20 !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 of static floating point expressions is implementation-defined. It is recommended that the rounding be 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 run-time 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 run time. 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) :-) **************************************************************** From: Brian Wichmann Sent: Saturday, March 9, 2002 6:58 AM I am totally confused by this AI and also what the RM says! 1. Independent of rounding, the RM is clearly wrong for some m/cs. Take a machine which only has double-length hardware and a round instruction. Single length is a type, but by 11.6, the compiler will use double-length operations and just does round on assignment. It would be crazy to truncate static expressions to single length on such a machine, but the RM seems to require that. 2. IEEE is a red-herring. Clearly, the rounding should be appropriate to the target. This is not to the target type, as point 1 shows. 3. The static expression model is just right. Hence Pascal's example is misleading since for a compiler to replace division by multiplication is hardly ever safe (obviously, replacing X/1.0 by X is fine; so some operator juggling is possible). 4. Machine_Rounds is False does not imply truncation. The attribute might be false just because divide is implemented incorrectly - surely that should not mess up static expressions? 5. Rounding is potentially dynamic. As Erhard has pointed out, full IEEE systems have the ability to dynamically control rounding. When using the facility, perhaps the user might like to recompile his program with an option to control the rounding - why not? **************************************************************** From: Pascal Leroy Sent: Tuesday, March 12, 2002 7:13 AM > 1. Independent of rounding, the RM is clearly wrong for some m/cs. > > Take a machine which only has double-length hardware and a round instruction. > Single length is a type, but by 11.6, the compiler will use double-length > operations and just does round on assignment. It would be crazy to truncate > static expressions to single length on such a machine, but the RM seems > to require that. I don't think there is a problem here. This situation is similar to that of extended registers. AARM A.5.3(11.d) makes it clear that we expect Machine_Rounds to return True in the presence of extended registers (assuming that the arithmetic is otherwise well-behaved) even though multiple rounding can sometimes yield results that are not properly rounded. > 2. IEEE is a red-herring. Clearly, the rounding should be appropriate to > the target. This is not to the target type, as point 1 shows. I think that IEEE is only mentioned as an example in the AI. Surely it is an important case in practice. > 3. The static expression model is just right. Hence Pascal's example is > misleading since for a compiler to replace division by multiplication is > hardly ever safe (obviously, replacing X/1.0 by X is fine; so some operator > juggling is possible). Agreed, my comment was misguided. The only divisions that can be safely converted to multiplications are those where the divisor is a power of 2.0. > 4. Machine_Rounds is False does not imply truncation. The attribute might be > false just because divide is implemented incorrectly - surely that should not > mess up static expressions? One possibility would be to say when Machine_Rounds is False, the static computation should emulate what the target arithmetic does. However, that looks like a slippery slope to me. First, it would significantly complicate those cross-compilers that target processors with exotic arithmetic, as emulating the target might be complex and error-prone. Second, it would hamper portability, as the static rounding would vary from target to target. I just don't think that the case where Machine_Rounds is False is important enough to warrant all this complexity. > 5. Rounding is potentially dynamic. As Erhard has pointed out, full IEEE > systems have the ability to dynamically control rounding. When using the > facility, perhaps the user might like to recompile his program with an > option to control the rounding - why not? This is beyond the scope of this AI. If we ever design a mapping to the full IEEE capabilities, maybe we'll want to have a configuration pragma to specifying the default rounding mode. In the meantime, people who use machine code insertions to change the rounding mode should be extra cautious with static expressions (and make them dynamic if they need to use the current rounding mode). **************************************************************** From: Randy Brukardt Sent: Tuesday, March 12, 2002 2:05 PM > 1. Independent of rounding, the RM is clearly wrong for some m/cs. > Take a machine which only has double-length hardware and a round instruction. > Single length is a type, but by 11.6, the compiler will use double-length > operations and just does round on assignment. It would be crazy to truncate > static expressions to single length on such a machine, but the RM seems > to require that. This of course is the situation on the 8086 family (including the Pentiums). I agree that it is crazy that the RM requires rounding to single length (Janus/Ada 83 always created static expressions as double length, no matter what the type, for 8086 family machines). Why discard accuracy? However, this was discussed at the time Ada 95 was designed (I know I objected to this rule), and (as I recall) the answer was essentially that "the numeric guys want it this way". I certainly don't see any new information which would make it valuable to revisit that decision. On the other questions, I agree with Pascal. **************************************************************** From: Robert Dewar Sent: Tuesday, March 12, 2002 3:40 PM I disagree that the rounding of floats should be implementation dependent in this sense. I think it is perfectly reasonable to round appropriate to the type for static expressions. ****************************************************************