richard@pantor.UUCP (Richard Sargent) (05/24/90)
> From: tneff@bfmny0.UU.NET (Tom Neff) > Message-ID: <15519@bfmny0.UU.NET> > ... > > People want hex float so they can represent 7/4096, say, without > worrying about flipping a digit and making hash. I don't get it. Why would you code some indecipherable 'magic' fraction rather than just simply declare: float fract = 7.0/4096.0; and let the optimizer/compiler turn that into the correct constant? Maybe Rex J. can tell us why have hex constants for float. Richard Sargent Internet: richard@pantor.UUCP Systems Analyst UUCP: ...!mnetor!becker!pantor!richard
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (05/28/90)
In article <73.UUL1.3#5109@pantor.UUCP>, richard@pantor.UUCP (Richard Sargent) writes: > Maybe Rex J. can tell us why have hex constants for float. I'm not Rex J., but I can point out several reasons. (1) If you stick to the letter of the IEEE 754 and IEEE 854 standards, conversion of numeric literals from decimal to binary (or possibly, in the case of 854, to internal decimal) is a *run* *time* operation, and is supposed to be sensitive to the rounding mode in effect at the time. That is, if I do set_rounding_mode(IEEE_CEILING_ROUNDING_MODE); x = 1.324567891234567812367489; set_rounding_mode(IEEE_FLOOR_ROUNDING_MODE); y = 1.324567891234567812367489; x and y ought to be different (well, it depends on the exact constant). However, the IEEE 754 and 854 standards say nothing about conversion to or from hex. (2) There was an algorithm published in ACM TOMS last year for computing exp() with *amazingly* good accuracy (about half an ULP!) in IEEE arithmetic. If I remember correctly, the magic numbers were expressed in hex. The point was to be absolutely specific about precisely what bit pattern was wanted. (3) There are special numbers (+ and - infinity, -0, NaNs, Vax reserved operands, &c) which have no standard syntax. (4) If a float is written in hex, it is a clear signal that the number depends on the details of floating-point arithmetic. It would be a common _notation_ for non-portable numbers, without going through unions and such. (5) I would _much_ rather trust a compiler to convert a hex constant accurately to the last bit than trust it to get the decimal conversion right. (I've read the comments in <math.h>.) All in all, it's not a feature that many C users will want, but it's something that people writing special functions and other parts of math libraries would like a lot. (Besides, it's a common extension to Fortran. How can we let Fortran stay one up? (:-)) -- "A 7th class of programs, correct in every way, is believed to exist by a few computer scientists. However, no example could be found to include here."
eggert@twinsun.com (Paul Eggert) (05/30/90)
O'Keefe makes several good points about IEEE floating point and ANSI C, including: |(1) If you stick to the letter of the IEEE 754 and IEEE 854 standards, | conversion of numeric literals from decimal to binary (or possibly, | in the case of 854, to internal decimal) is a *run* *time* operation, No C compiler that I know of does this. Does this mean that no C compiler can claim conformance to both IEEE 754 and ANSI C? I fear that if we ask either standards committee for a ruling, they'll refer us to the other committee. |(3) There are special numbers (+ and - infinity, -0, NaNs, Vax reserved | operands, &c) which have no standard syntax. There are ways to say the IEEE numbers in ANSI C + IEEE 754: #define minus_0 (-0.0) #define infinity (1e300*1e300) #define NaN1 (infinity-infinity) #define minus_infinity (-infinity) #define minus_NaN1 (-NaN1) This lets you say some things portably that you can't say with hex contants, e.g. ``the NaN that you get when you subtract infinity from itself'', which has a different hex value in different implementations of IEEE 754. Unfortunately, many compilers (e.g. GCC) get this sort of thing wrong, and these bugs are further arguments for hex floating constants.
tneff@bfmny0.BFM.COM (Tom Neff) (05/30/90)
In article <1990May30.203146.11442@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >The question is whether an implementation can conform to both ANSI C and IEEE >754. For example, IEEE 754 requires that -0.0 and 0.0 be distinguishable. Where does it say this? Requiring that "minus zero" have a distinct *internal* representation from "plus zero" says NOTHING about what a parser is supposed to do with the string "-0.0". >Suppose ANSI C somehow required that -0.0 and 0.0 be identical: then, because >the standards would be inconsistent, an implementation couldn't conform to both >standards. ANSI C does require that the strings "-0.0" and "0.0" parse to the same internal representation number (right Doug?). I think the root problem here is that 754 requires some animals to exist which X3.159 isn't very helpful in telling us how to write. I know that the AT&T V/386 support code for NaN's etc is pretty brutal -- we just rip into the raw bits and look at subfields. ;-)
jsalter@slo.uucp (James Salter) (05/30/90)
In article <1990May29.193451.6533@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >|(3) There are special numbers (+ and - infinity, -0, NaNs, Vax reserved >| operands, &c) which have no standard syntax. Nitpick: -0 is an integer and doesn't exist. -0.0 is a floating-point value and does exist. >There are ways to say the IEEE numbers in ANSI C + IEEE 754: > > #define minus_0 (-0.0) But IEEE 754 (Sec. 5.7) says that when comparing the two, (-0.0 == +0.0) so that can't be used for comparison. The way I test this is to strip off the sign bit and test that. Thank Knuth for big/little-endian :-). > #define infinity (1e300*1e300) This can be tricky. The numbers you use are big enough, but I've run into cases with the i387 where the two numbers multiplied together would give infinity in 64 bit (double) precision, but because the i387 has an 80 bit (extended precision) register, computations done, without restore into memory, don't get the correct exceptional value. This is especially true when the above is combined with modifying rounding modes. It's happened on both IBM and MetaWare compilers. And, I'm assuming it can/has happen/ed on other coprocessors, too. > #define NaN1 (infinity-infinity) > #define minus_infinity (-infinity) > #define minus_NaN1 (-NaN1) Will be (correctly) 0.0 if the above condition occurs. >This lets you say some things portably that you can't say with hex contants, >e.g. ``the NaN that you get when you subtract infinity from itself'', >which has a different hex value in different implementations of IEEE 754. There aren't a lot of people who actually use the data that can be embedded in NaNs, so it's not very worrysome. >Unfortunately, many compilers (e.g. GCC) get this sort of thing wrong, >and these bugs are further arguments for hex floating constants. jim/jsalter IBM AWD T465/(415)855-4427 VNET: JSALTER at AUSVMQ UUCP: ..!uunet!ibmsupt!jsalter Disc: Any opinions are mine. IP: ibmsupt!jsalter@uunet.uu.net "PS/2 it, or DIE!"
kingdon@pogo.ai.mit.edu (Jim Kingdon) (05/30/90)
In comp.std.c, O'Keefe writes:
(1) If you stick to the letter of the IEEE 754 and IEEE 854
standards, conversion of numeric literals from decimal to binary
(or possibly, in the case of 854, to internal decimal) is a *run*
*time* operation,
Looking at 754-1985 I don't see how that interpretation makes sense.
Section 4.2 says "An implementation shall also [in addition to round
to nearest] provide three user-selectable directed rounding modes".
"User", as defined in this standard, can be the compiler, rather than
the program you are writing. This is made explicit in section 4.3,
"the user, which may be a high-level language compiler". So when
section 5.6 says "the procedures used for binary <-> decimal
conversion should give the same results regardless of whether the
conversion is performed during language translation (interpretation,
compilation, or assembly) or during program execution (run-time and
itneractive input output)" it doesn't say what rounding mode the
compiler has to use. 754 just says that the compiler needs to be able
to choose a rounding mode. Presumably the compiler will either just
pick one (and if you're lucky document which one it is), or it will
provide some sort of directive to control it ("#pragma rounding_mode",
"__rounding(round_toward_infinity)", etc).
This is based on 754; I have no idea whether 854 is similar.
gwyn@smoke.BRL.MIL (Doug Gwyn) (05/30/90)
In article <1990May29.193451.6533@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >|(1) If you stick to the letter of the IEEE 754 and IEEE 854 standards, >| conversion of numeric literals from decimal to binary (or possibly, >| in the case of 854, to internal decimal) is a *run* *time* operation, >No C compiler that I know of does this. Does this mean that no C compiler can >claim conformance to both IEEE 754 and ANSI C? I fear that if we ask either >standards committee for a ruling, they'll refer us to the other committee. Fear what you will. X3J11 certainly did consider this and other floating- point issues. The C standard supports use of IEEE floating-point. However, it doesn't require that access be granted to all features of it, just the consistent subset chosen for the C implementation. Use of extensions that access such features as selection of rounding mode render a program not strictly conforming, so the implementation is at liberty to give the expected meaning to them. >There are ways to say the IEEE numbers in ANSI C + IEEE 754: > #define minus_0 (-0.0) > #define infinity (1e300*1e300) > #define NaN1 (infinity-infinity) > #define minus_infinity (-infinity) > #define minus_NaN1 (-NaN1) These don't work. -0.0 is NOT a "minus zero"; it's identical to 0.0. Assuming that 1.0e+300*1.0e+300 is not representable as a (floating- pint) number, the behavior is explicitly undefined, and the implementation is not obliged to produce a representation of "infinity" for it. Assuming you had defined "infinity" properly, the remaining expressions lie outside the scope of the C standard, and nothing obliges an implementation to do what you appear to wish be done about them.
steve@taumet.COM (Stephen Clamage) (05/31/90)
In article <1990May29.193451.6533@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >There are ways to say the IEEE numbers in ANSI C + IEEE 754: > [examples deleted] >This lets you say some things portably that you can't say with hex contants, Sorry. This is hardly portable. The compiler is allowed to object to (refuse to compile) any of these. If it compiles, the program may behave in undefined ways in attempting to evaluate such expressions at run time. (The compiler is not obligated to evaluate them at compiler time.) -- Steve Clamage, TauMetric Corp, steve@taumet.com
eggert@twinsun.com (Paul Eggert) (05/31/90)
Doug Gwyn writes:
The C standard supports use of IEEE floating-point. However, it
doesn't require that access be granted to all features of it, just the
consistent subset chosen for the C implementation.
The question is whether an implementation can conform to both ANSI C and IEEE
754. For example, IEEE 754 requires that -0.0 and 0.0 be distinguishable.
Suppose ANSI C somehow required that -0.0 and 0.0 be identical: then, because
the standards would be inconsistent, an implementation couldn't conform to both
standards. O'Keefe's contention that IEEE 754 requires run-time
decimal<->binary conversion made me worry about this issue, because it would
make implementations impractical, but more careful reading of IEEE 754 seems to
have found a loophole in O'Keefe's argument.
eggert@twinsun.com (Paul Eggert) (05/31/90)
I wrote: There are ways to say the IEEE numbers in ANSI C + IEEE 754: #define minus_0 (-0.0) #define infinity (1e300*1e300) ... Doug Gwyn replied: These don't work. -0.0 is NOT a "minus zero"; it's identical to 0.0. Surely ANSI C doesn't require this; it is inconsistent with IEEE 754. For example, given the following strictly conforming ANSI C program #include <stdio.h> int main() { printf("%g\n", -0.0); return 0; } an implementation that conforms to both ANSI C and IEEE 754 must print "-0", not "0". IEEE 754 doesn't specify the output format, but it does require that +0 and -0 be output differently, and "-0" is the only real choice here. This is not purely academic. Of the two C compilers on my IEEE 754-based machine, one prints "-0" and the other "0" when given the above program. I've sent a bug report to the latter's implementer, and fully expect it to get fixed. Finally, Gwyn wrote: Assuming that 1.0e+300*1.0e+300 is not representable as a (floating-point) number, the behavior is explicitly undefined... (Steve Clamage made a similar point.) True, if you assume only conformance to ANSI C. But I explicitly assumed conformance to both ANSI C and IEEE 754.
eggert@twinsun.com (Paul Eggert) (05/31/90)
I wrote that IEEE 754 requires that -0.0 and 0.0 be distinguishable.
Tom Neff asks: ``Where does it say this?'' IEEE 754 section 5.6 says
When rounding to nearest, conversion from binary to decimal and back to
binary shall be the identity as long as the decimal string is carried
to the maximum precision specified in Table 2, namely 9 digits for
single and 17 digits for double.
In other words, print a binary floating-point number, then read it back in:
you'll get the original bit pattern, so long as you use enough digits and use
the default rounding mode. To satisfy this requirement, -0.0 and 0.0 must have
different external forms, since their internal forms differ.
Neff also writes:
ANSI C does require that the strings "-0.0" and "0.0" parse to the same
internal representation number (right Doug?).
On the contrary, the wording for strtod() (ANSI C 4.10.1.4) provides for the
distinction between 0.0 and -0.0:
If the subject sequence has the expected form, [it, minus any sign,]
... is interpreted as a floating constant.... If the subject
sequence begins with a minus sign, the value resulting from the
conversion is negated.
Thus on an IEEE 754 host, strtod("-0.0",NULL) computes 0.0 and then negates it,
yielding -0.0.
amull@Morgan.COM (Andrew P. Mullhaupt) (05/31/90)
In article <1990May29.193451.6533@twinsun.com>, eggert@twinsun.com (Paul Eggert) writes: > There are ways to say the IEEE numbers in ANSI C + IEEE 754: > #define infinity (1e300*1e300) > This lets you say some things portably that you can't say with hex contants, Will this work if the compiler supports IEEE extended format for constants? Or would this preclude ANSI C compliance? The constant 1e600 is not out of extended range. You might replace 1e300 in your example by a larger number, but then what about the compilers which _don't_ use extended? Both behaviors are permitted by IEEE arithmetic. A conditional compilation solution is possible only if you can ensure that the appropriate symbol will be defined. Once you've been reduced to that, you should use the hex constant method, on the grounds that even on the same hardware, different compilers, or even different preprocessors for the same compiler may handle the constant arithmetic differently, but the floating point representation is less likely to change. Later, Andrew Mullhaupt
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (05/31/90)
In article <1990May29.193451.6533@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >|(3) There are special numbers (+ and - infinity, -0, NaNs, Vax reserved >| operands, &c) which have no standard syntax. [actually, I think I wrote that bit] In article <5491@ibmpa.UUCP>, jsalter@slo.uucp (James Salter) writes: > Nitpick: -0 is an integer and doesn't exist. -0.0 is a floating-point value > and does exist. Counter-nitpick: the text of ANSI/IEEE Std 754-1985 consistently says -0, not -0.0 > But IEEE 754 (Sec. 5.7) says that when comparing the two, (-0.0 == +0.0) > so that can't be used for comparison. The way I test this is to strip > off the sign bit and test that. Would it not be better to use copysign()? (754 Appendix (1)). By the way, someone said that -0.0 is 0.0 in C. The Appendix *is not binding*, but it does say "-X is X copied with its sign reversed, not 0-X; the distinction is germane when X is +/- 0 or NaN." > There aren't a lot of people who actually use the data that can be embedded > in NaNs, so it's not very worrysome. IEEE 754 says that "at least one signalling Nan" and "at least one quiet Nan" "shall be provided". The encoding for single and double is spelled out and has room for more, but there is nothing to say that single extended or double extended has more than 2 NaNs. With reference to hex float, the encoding of single and double extended isn't specified either, so there is _no_ method of expressing a long double that is covered by IEEE 754. Of course, C also has to cater for people using /370s, Crays, non-IEEE-conformant chips like 8087s (ask about the details in comp.arch, I've forgotten), VAXen, PR1MEs. -- "A 7th class of programs, correct in every way, is believed to exist by a few computer scientists. However, no example could be found to include here."
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (05/31/90)
In article <15570@bfmny0.BFM.COM>, tneff@bfmny0.BFM.COM (Tom Neff) writes: > Where does it say this? Requiring that "minus zero" have a distinct > *internal* representation from "plus zero" says NOTHING about what a > parser is supposed to do with the string "-0.0". It's not just a different internal representation. It behaves differently. There are three cases: (a) the language defines -0.0 as one single token string. Then the translation is governed by 5.6 and 3.1, so the answer must be minus zero (b) the language defines -0.0 as two tokens then 0.0 converts to plus zero, and -(plus zero) is minus zero. (c) the language defines -0.0 as two tokens, and says explicitly that "- Literal" is translated as the value of "0 - Literal". In that case, the translation must be plus zero. Interpretation (c) violates the fairly strong recommendation of the appendix, so although it is technically legal, it would be inadvisable. > ANSI C does require that the strings "-0.0" and "0.0" parse to the same > internal representation number (right Doug?). OOPS. I would be surprised if it did: consider the IBM/370 series. If I remember correctly, because they don't use a hidden bit, and they do allow unnormalised numbers, they have a _lot_ of internal representations of 0.0, _one_ of them being all zero bits. Certainly the B6700's floating- point representation had that property (though I'd be surprised to find a C compiler on the descendants of that machine). -- "A 7th class of programs, correct in every way, is believed to exist by a few computer scientists. However, no example could be found to include here."
gwyn@smoke.BRL.MIL (Doug Gwyn) (05/31/90)
In article <1990May30.203146.11442@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: > The C standard supports use of IEEE floating-point. However, it > doesn't require that access be granted to all features of it, just the > consistent subset chosen for the C implementation. >The question is whether an implementation can conform to both ANSI C and IEEE >754. For example, IEEE 754 requires that -0.0 and 0.0 be distinguishable. Yes, but C doesn't. C can be implemented on top of IEEE 754 conforming hardware. As someone else pointed out, the IEEE 754 "user" can be taken as the C implementation. There is no requirement that the "user" actually exploit all the features that IEEE 754 requires be made available to the "user". Now, some programmers (for example those involved with NCEG) may well want to provide some means by which a C programmer who is using a C implementation on top of an IEEE 754 platform would be able to get his hands on some of the weird IEEE 754 features other than the default subset that the implementation uses. Although I wouldn't recommend this myself, I understand that some people feel they "need" this. Such additional access can be provided as extensions in a conforming C implementation. Any time a programmer uses such extensions his program is no longer strictly conforming, so they can alter the behavior of the implementation in ways that would not be suitable for support of strictly conforming programs. I see no problem here.
gwyn@smoke.BRL.MIL (Doug Gwyn) (05/31/90)
In article <1990May30.205436.11534@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >Doug Gwyn replied: > These don't work. -0.0 is NOT a "minus zero"; it's identical to 0.0. >Surely ANSI C doesn't require this; it is inconsistent with IEEE 754. I sometimes wonder why I waste my time responding. IEEE 754 does not apply to C source code! The C standard says simply that the value of "-x" is the "negative of x"; in the case of integer operations on a ones' complement architecture, which does have a representation for "negative zero" integers, one can deduce from a combination of specifications in the standard that "-0" must have the same representation as "0", not all-one-bits. While I doubt that there are tight enough constraints to deduce that the analogous situation is mandated for floating-point, I'm sure that it is allowed. Since no special requirements are made for IEEE FP environments in the C standard, it would then be allowed by the C standard for such environments also. Note that "-0.0" consists of the TWO tokens "-" and "0.0"; it is NOT a single floating constant, and thus is covered by the ordinary rules for negation in expressions, not treated as some special code that must be represented by the IEEE bit pattern for "minus zero". >IEEE 754 doesn't specify the output format, but it does require that >+0 and -0 be output differently, ... However, it does not require that writing "-0.0" in C source code be a way of producing a "minus zero". IF you are somehow able to cobble up a minus zero result, perhaps through bitwise operations or by dividing 1 by -infinity (produced as an overflow or something), THEN printing that result would have to print something distinguishable from (plus) zero in order to satisfy IEEE 754. >This is not purely academic. Of the two C compilers on my IEEE 754-based >machine, one prints "-0" and the other "0" when given the above program. I've >sent a bug report to the latter's implementer, and fully expect it to get fixed. I would have sent a bug report to the former, since although the former may technically be allowed it is not what I would expect for negation of an expression that happened to have the value 0. Mathematically it is nonsense to say -0 is not identical to 0. (Yes, I've heard all the arguments for the IEEE 754 extended number system, but I don't buy it. There are much more useful extensions that would also have been mathematically valid, but the real number represented by -0 is identically the real number represented by 0.) >But I explicitly assumed conformance to both ANSI C and IEEE 754. So did I.
tim@proton.amd.com (Tim Olson) (05/31/90)
In article <15570@bfmny0.BFM.COM> tneff@bfmny0.BFM.COM (Tom Neff) writes: | ANSI C does require that the strings "-0.0" and "0.0" parse to the same | internal representation number (right Doug?). Not that I can see. Neither the Floating Constants section in LEXICAL ELEMENTS nor the runtime specification of library routines like fscanf say anything about internal representations. The MetaWare compiler we use generates IEEE +0.0 (0x00000000) for the string "0.0" and IEEE -0.0 (0x80000000) for the string "-0.0". -- Tim Olson Advanced Micro Devices (tim@amd.com)
gwyn@smoke.BRL.MIL (Doug Gwyn) (06/01/90)
In article <1990May31.023244.12359@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: > ANSI C does require that the strings "-0.0" and "0.0" parse to the same > internal representation number (right Doug?). >On the contrary, the wording for strtod() (ANSI C 4.10.1.4) provides for the >distinction between 0.0 and -0.0: > If the subject sequence has the expected form, [it, minus any sign,] > ... is interpreted as a floating constant.... If the subject > sequence begins with a minus sign, the value resulting from the > conversion is negated. >Thus on an IEEE 754 host, strtod("-0.0",NULL) computes 0.0 and then negates it, >yielding -0.0. Sorry, but your conclusion does not follow from the premises. "Negation" of a number can (and should) be taken as an ordinary English description of the mathematical process. Mathematically, the set of real numbers does not contain distinct members "plus zero" and "minus zero", just as it does not include plus or minus infinity. (If you want to extend the real number system like that, I suppose you are free to do so, but certainly that lies beyond the meaning of the specification in the C standard.) While the standard does not specify in detail what objects having floating- point types are good for, it does call them "arithmetic types", and applies ordinary arithmetic and functional analysis terms to operations involving them, implying that they somehow represent the kind of entity that we are accustomed to using for ordinary arithmetic etc., i.e. real numbers. I know our schools are messed up but I doubt if even they are teaching that negating zero does not produce zero. Indeed, most algebra classes seem to at some point prove uniqueness of the additive (group) identity, and since it is easy to show that the negative (group inverse) of 0 acts as an additive identity, it must be identically 0. I believe that you might be able to correctly argue that the C standard wording about negation ALLOWS your interpretation but not that it REQUIRES it, even on systems supporting IEEE 754 floating- point. As I stated in another posting, IEEE 754 does not constrain the services that the C implementation provides to a programmer, although it does control some aspects of what the C implementation has to deal with. (It also apparently conditions programmers to expect to be able to access all the IEEE 754 warts.) Anyone who really wants to play with distinct "minus zero" should first learn a lesson from CDC's experience with such arithmetic units: Valid arithmetic operations on non-special values should never result in a special value; the only way you should obtain a special value is to have a special value as an operand or possibly perform an invalid operation on (non-special) values. Thus, -1.0/(1.0e+300*1.0e+300) may very well produce an IEEE 754 "minus zero" result, but as such overflow is explicitly specified in the C standard as resulting in undefined behavior, it is not correct to state that the C standard requires any particular result in this case.
eggert@twinsun.com (Paul Eggert) (06/01/90)
Andrew P. Mullhaupt asks (re `#define infinity (1e300*1e300)'): Will this work if the compiler supports IEEE extended format for constants? In theory, yes. In practice, no. I'd prefer #define infinity (1.0/0.0) which should work on ANSI C + IEEE 754 implementations regardless of format. However, some compilers balk at `1.0/0.0'. `1e300*1e300' works on all IEEE 754 implementations that I know of, and also gets through the broken compilers.
ps@fps.com (Patricia Shanahan) (06/01/90)
In article <13028@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >In article <1990May30.205436.11534@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >>Doug Gwyn replied: >> These don't work. -0.0 is NOT a "minus zero"; it's identical to 0.0. >>Surely ANSI C doesn't require this; it is inconsistent with IEEE 754. > >I sometimes wonder why I waste my time responding. >IEEE 754 does not apply to C source code! The issue is even clearer for Fortran. According to the May 89 Fortran 8x draft "Each numeric type includes a zero value, which is considered to be neither negative nor positive. The value of a signed zero is the same as the value of an unsigned zero." (there is a similar statement in the ANSI Fortran 77 standard). For output conversion the draft specifies "a minus if the value of the internal datum is negative, or an optional plus sign otherwise...". According to the standard "+0.0" would be O.K., but "-0.0" is prohibited. In other words, there is no experiment you could perform in a standard conforming program on a standard conforming Fortran implementation that would distinguish two different values of zero from each other. Fortran users and implementers generally seem to take floating point types far more seriously than typical C users and implementers. If distinguishing -0.0 from 0.0 is such a good thing, why is it so expressly prohibited, not just in early Fortran standards, but in the draft for the next Fortran standard? I realize that C can (and does) have different rules from Fortran for floating point arithmetic. However, there is about 30 years experience of implementing numerical algorithms contributing to the Fortran standard. Is it wise to ignore the choices that have been made for Fortran in this area? Note that there is nothing about this that prevents a standard conforming Fortran implementation from being based on an IEEE floating point conforming platform. It just requires a little care on the part of the language implementer to ensure that all zeros are equal to each other and are never printed with minus signs. -- Patricia Shanahan ps@fps.com uucp : ucsd!celerity!ps phone: (619) 271-9940
eggert@twinsun.com (Paul Eggert) (06/01/90)
Doug Gwyn writes:
... the IEEE 754 "user" can be taken as the C implementation. There is
no requirement that the "user" actually exploit all the features that
IEEE 754 requires be made available to the "user".
To conform to IEEE 754, an ANSI C implementation can't merely use IEEE 754
internally. It must make IEEE 754 available to its users, namely to the
programs that it accepts. If a company advertised that its C implementation
conformed to both ANSI C and IEEE 754, and its customers complained about IEEE
754 features that were missing, could it get away with telling them that their
programs weren't the real ``users'' of IEEE 754?
[IEEE 754] does not require that writing "-0.0" in C source code be a
way of producing a "minus zero".
True, but only because neither standard talks about each other, so an
implementation that conforms to both ANSI C and IEEE 754 can combine them
perversely. For example, a perverse implementer could say ``to use IEEE 754,
one must use the types like _ieee_754_double_format, and invoke functions like
_ieee_754_double_negate(x)''. But in a normal implementation, one would expect
the ANSI C types float and double to stand for IEEE 754 single format and
double format, and the ANSI C unary floating `-' operator to stand for the IEEE
754 `-x' function. In such an implementation, -0.0 must evaluate to minus
zero.
Mathematically it is nonsense to say -0 is not identical to 0.
I also dislike some of IEEE 754's provisions. But that's not the issue here.
Granted, no standard specifies the relationship between IEEE 754 and ANSI C,
and what I think is ``perverse'' is only my opinion; but surely most users
would agree with the ``normal'' implementation described above. However, if we
don't all agree on this question, shouldn't it be addressed by NCEG?
While we're on the subject, NCEG should provide for a way for a program to
print a number so that it can be read back. ANSI C's "C" locale requires that
an IEEE 754 implementation's scanf() cannot read some numbers that its printf()
should be able to output, namely infinities and NaNs. This is unfortunate: it
should be easy for a program to scan numbers that it prints. How can I forward
comments like this to the NCEG committee?
diamond@tkou02.enet.dec.com (diamond@tkovoa) (06/01/90)
In article <15570@bfmny0.BFM.COM> tneff@bfmny0.BFM.COM (Tom Neff) writes: >ANSI C does require that the strings "-0.0" and "0.0" parse to the same >internal representation number The closest I can find is the "Semantics" section of 3.1.3.1: "If the scaled value is in the range of representable values (for its type) the result is either the nearest representable value, or the larger or smaller representable value immediately adjacent to the nearest representable value, chosen in an implementation-defined manner." No rule is made for choosing which of two nearest representable values should be chosen. No rule suggests choosing an EQUAL representable value immediately addjacent to the nearest representable "value" (values?). Hmm. If the implementation so defines, then parsing 2.0 gives a result of 2.0, 2.0 plus some epsilon, or 2.0 minus some (possibly different) epsilon, that is, the larger or smaller immediately adjacent to the nearest. The results of floating-point arithmetic are not required to be even this accurate. For example, 2.0 + 2.0 is allowed to yield 4.5 even if there exist representations for 4.1 and 4.4. I think this means that if your parser is coded in ANSI C, it might not be an ANSI parser. You think you're parsing floating-point constants, but you're not doing a good enough job of accuracy! A machine-dependent parser can do it by doing calculations in ints, playing with the bits of some ints and unioning those to a float. (Whew! For a moment, I thought that an ANSI C compiler couldn't be coded in ANSI C. It still can be, but just not portably.) -- Norman Diamond, Nihon DEC diamond@tkou02.enet.dec.com Proposed group comp.networks.load-reduction: send your "yes" vote to /dev/null.
diamond@tkou02.enet.dec.com (diamond@tkovoa) (06/01/90)
In article <1990May31.023244.12359@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >IEEE 754 section 5.6 says > When rounding to nearest, conversion from binary to decimal and back to > binary shall be the identity as long as the decimal string is carried > to the maximum precision specified in Table 2, namely 9 digits for > single and 17 digits for double. >In other words, print a binary floating-point number, then read it back in: >you'll get the original bit pattern, so long as you use enough digits and use >the default rounding mode. Digression: No. You have to use EXACTLY the right number of digits. (Assuming that the quotation of section 5.6 is the complete binding specification for this issue.) If you use too many, it might be rounded differently on re-reading. >To satisfy this requirement, -0.0 and 0.0 must have >different external forms, since their internal forms differ. For IEEE 754, yes. >the wording for strtod() (ANSI C 4.10.1.4) provides for the >distinction between 0.0 and -0.0: > If the subject sequence has the expected form, [it, minus any sign,] > ... is interpreted as a floating constant.... If the subject > sequence begins with a minus sign, the value resulting from the > conversion is negated. >Thus on an IEEE 754 host, strtod("-0.0",NULL) computes 0.0 and then negates it, >yielding -0.0. Yes, it seems that (ANSI C plus IEEE 754) yields essentially this result. (Digression: it seems legal for a perverse implementation to reverse the signs just for the values of 0.0 and -0.0.) However, this result is only required for two-way conversions in a single implementation. It still is not necessary for a parser at compile-time to distinguish -0.0 from 0.0. inews fodder inews fodder inews fodder inews fodder inews fodder -- Norman Diamond, Nihon DEC diamond@tkou02.enet.dec.com Proposed group comp.networks.load-reduction: send your "yes" vote to /dev/null.
tneff@bfmny0.BFM.COM (Tom Neff) (06/01/90)
>"If the scaled value is in the range of representable values (for its >type) the result is either the nearest representable value, or the >larger or smaller representable value immediately adjacent to the >nearest representable value, chosen in an implementation-defined manner." ^^^^^^^^^^^^^^^^^^^^^ Does anyone else think this word choice is strange? I can understand dealing with values outside the domain of exactly representable numbers in this way -- let the compiler round up, down or to nearest as it sees fit -- but if the target value is exactly representable, surely that representation's use should be mandatory. If the committee had a specific counter rationale I'd like to know what it is. If this is just imprecise wording, an interpretation might be in order.
henry@utzoo.uucp (Henry Spencer) (06/02/90)
In article <8846@celit.fps.com> ps@fps.com (Patricia Shanahan) writes: >Fortran users and implementers generally seem to take floating point types >far more seriously than typical C users and implementers. If distinguishing >-0.0 from 0.0 is such a good thing, why is it so expressly prohibited, not >just in early Fortran standards, but in the draft for the next Fortran >standard? Don't overlook the possibility of sheer ignorance. Most Fortran users work on non-IEEE machines, and for that matter most of them (in my rather limited contacts with them) really do not understand the fine points of floating-point arithmetic particularly well. Don't confuse Fortran programmers with competent numerical analysts; the latter group is a small subset of the former. Note, by the way, a point of confusion: as I understand it, IEEE FP does very specifically mandate that -0 compare equal to +0. The distinction between the two is significant only in unusual circumstances. -- As a user I'll take speed over| Henry Spencer at U of Toronto Zoology features any day. -A.Tanenbaum| uunet!attcan!utzoo!henry henry@zoo.toronto.edu
amull@Morgan.COM (Andrew P. Mullhaupt) (06/02/90)
In article <15576@bfmny0.BFM.COM>, tneff@bfmny0.BFM.COM (Tom Neff) writes: | >"If the scaled value is in the range of representable values (for its | >type) the result is either the nearest representable value, or the | >larger or smaller representable value immediately adjacent to the | >nearest representable value, chosen in an implementation-defined manner." | Does anyone else think this word choice is strange? I can understand It looks to me like the standard (bless its soul) is making provision for stable rounding, (see also banker's rounding). See Knuth for details. Later, Andrew Mullhaupt
gwyn@smoke.BRL.MIL (Doug Gwyn) (06/04/90)
In article <8846@celit.fps.com> ps@fps.com (Patricia Shanahan) writes: >The issue is even clearer for Fortran. Thank heavens. >Fortran users and implementers generally seem to take floating point types >far more seriously than typical C users and implementers. If distinguishing >-0.0 from 0.0 is such a good thing, why is it so expressly prohibited, not >just in early Fortran standards, but in the draft for the next Fortran >standard? I'm not sure I like this line of argumentation, even though it would lead to a conclusion in line with mine. Certainly there is no guarantee that the best possible decisions would be made by the Fortran committee, although this particular one does sound good to me. One thing I would like to challenge in this thread is the assumption that it is rational to specify conformance to IEEE 754 and/or 854. If I had an engineer working for me and upon being requested to design a portable format for floating-point representation he had come back to me with a specification like IEEE 754, I would fire the bastard on the grounds of professional incompetence. Not only does IEEE 754 fail in its primary goal of standardizing the format, but it invents a multiplicity of variations plus a new fantasy world of nonstandard mathematics, the latter solely for the reason that in SOME artificially simple situations, lack of care on the part of algorithm designers could be automatically compensated for by carrying invalid arithmetic results as the IEEE 754 special values. Catering to amateur sloppiness while making life more difficult for the careful professional is inexcusable in a technical standard. It is insane to insist on conformance to such an abomination. At best, it is a fact of life (especially in the small computer world) that has to be somehow coped with, hopefully by the implementor of one's programming language. >Note that there is nothing about this that prevents a standard conforming >Fortran implementation from being based on an IEEE floating point conforming >platform. It just requires a little care on the part of the language >implementer to ensure that all zeros are equal to each other and are never >printed with minus signs. It is also probably advisable for implementors faced with 68881 etc. FPUs to implement the programming language's negation operation by loading a FP register with true_zero then subtracting the expression to be negated from the register (leaving a sensible negative in the register even when the expression happens to have zero value).
gwyn@smoke.BRL.MIL (Doug Gwyn) (06/04/90)
In article <1990May31.223436.23066@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >But in a normal implementation, one would expect the ANSI C types >float and double to stand for IEEE 754 single format and double format, >and the ANSI C unary floating `-' operator to stand for the IEEE 754 >`-x' function. I totally disagree with the entire argument. I would rather "double" be what in Apple-speak is denoted as "extended", and certainly in many discussions on the subject in X3J11 meetings, even the proponents of IEEE floating point agreed that such a choice is intended to be entirely up to the implementor (perhaps in consultation with his customers). It is utterly fantastic to expect a one-to-one correspondence between operators/functions in the high-level language and low-level machine operations; in fact even for integer operations such a correspondence is seldom realizable. >However, if we don't all agree on this question, shouldn't it be >addressed by NCEG? What NCEG can reasonably address about this is the specification of extensions to provide programs access to some of the IEEE FP warts, or provision of a separate specification that, taken in conjunction with the C standard, would constrain the C implementation to follow rules such that the ones you say would be "expected". (However, I personally would hope that they do not insist on IEEE FP for whatever standard might result from their work.) >While we're on the subject, NCEG should provide for a way for a program to >print a number so that it can be read back. ANSI C's "C" locale requires >that an IEEE 754 implementation's scanf() cannot read some numbers that >its printf() should be able to output, namely infinities and NaNs. No, any use of NaNs and Infinities lies in the realm of undefined or implementation-defined behavior, and thus is not constrained by the C standard. Certainly NCEG could consider imposing additional requirements on printf()/scanf() in an optional, supplemental standard that might result from their deliberations.
diamond@tkou02.enet.dec.com (diamond@tkovoa) (06/04/90)
In article <1017@s6.Morgan.COM> amull@Morgan.COM (Andrew P. Mullhaupt) writes: |In article <15576@bfmny0.BFM.COM>, tneff@bfmny0.BFM.COM (Tom Neff) writes: |||"If the scaled value is in the range of representable values (for its |||type) the result is either the nearest representable value, or the |||larger or smaller representable value immediately adjacent to the |||nearest representable value, chosen in an implementation-defined manner." ||Does anyone else think this word choice is strange? |It looks to me like the standard (bless its soul) is making provision |for stable rounding, (see also banker's rounding). See Knuth for |details. In fact most numerical engineers prefer stable rounding, in most cases (i.e. not just bankers). Unfortunately various other programming languages and various other sorts of standards make this difficult. Unfortunately, C errs in the opposite direction. It would have been better to say nothing about precision (as is done for arithmetic and fprintf) than to encourage excessive carelessness. The existing wording really ought to be changed one way or another by the interpretation committee. -- Norman Diamond, Nihon DEC diamond@tkou02.enet.dec.com Proposed group comp.networks.load-reduction: send your "yes" vote to /dev/null.
kingdon@pogo.ai.mit.edu (Jim Kingdon) (06/04/90)
eggert@twinsun.com (Paul Eggert) writes: >While we're on the subject, NCEG should provide for a way for a program to >print a number so that it can be read back. ANSI C's "C" locale requires >that an IEEE 754 implementation's scanf() cannot read some numbers that >its printf() should be able to output, namely infinities and NaNs. gwyn@smoke.BRL.MIL (Doug Gwyn) writes: No, any use of NaNs and Infinities lies in the realm of undefined or implementation-defined behavior, and thus is not constrained by the C standard. Unless I'm misreading something, in the "C" locale, scanf ("%f", &x); where the input contains NaN has to return zero without reading any of the characters "NaN". Picking an alternate syntax like "0rnan" or something which is supposed to be more number-like doesn't help, as section 4.10.1.4 is very specific about what a number as input to strtod() (or scanf()) is supposed to look like. In another locale, the implementation is allowed to recognize other forms, so presumably NCEG wants to standardize a "NUMERIC" locale.
gwyn@smoke.BRL.MIL (Doug Gwyn) (06/04/90)
In article <1762@tkou02.enet.dec.com> diamond@tkou02.enet.dec.com (diamond@tkovoa) writes: >Unfortunately, C errs in the opposite direction. It would have been >better to say nothing about precision (as is done for arithmetic and >fprintf) than to encourage excessive carelessness. The existing wording >really ought to be changed one way or another by the interpretation >committee. To the contrary, apart from the fact that X3J11 during the interpretations phase is not in a position to change wording in the standard, the existing wording on this was carefully hammered out in consultation with numerical programming specialists, and includes sufficient latitude to guarantee reasonable implementation on a variety of real architectures. I don't think the wording "encourages excessive carelessness", because to assure conformance with the specification the implementor has to do pretty much the same work as he would were the specification even tighter (which it couldn't be without causing problems for many existing architectures). After taking that much trouble to ensure adequate precision, why would one expect the implementor to then deliberately provide suboptimal precision?
ps@fps.com (Patricia Shanahan) (06/04/90)
In article <13038@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >In article <8846@celit.fps.com> ps@fps.com (Patricia Shanahan) writes: >>The issue is even clearer for Fortran. > >Thank heavens. > >>Fortran users and implementers generally seem to take floating point types >>far more seriously than typical C users and implementers. If distinguishing >>-0.0 from 0.0 is such a good thing, why is it so expressly prohibited, not >>just in early Fortran standards, but in the draft for the next Fortran >>standard? > >I'm not sure I like this line of argumentation, even though it would >lead to a conclusion in line with mine. Certainly there is no guarantee >that the best possible decisions would be made by the Fortran committee, >although this particular one does sound good to me. > I am not trying to suggest that all decisions made for Fortran should automatically be adopted for C. I just think that differences should be justified on their own merits, not simply by pointing to IEEE 754. I do not think that the IEEE treatment of zero can be justified. However, the Fortran community has an admirable track record of writing numerical software that gets right results within the expected degree of accuracy of the program across a wide range of floating point formats and rounding implementations, with major optimization order-of-evaluation changes. There are exceptions, but the usual rule is easy porting to new implementations. >One thing I would like to challenge in this thread is the assumption >that it is rational to specify conformance to IEEE 754 and/or 854. >If I had an engineer working for me and upon being requested to >design a portable format for floating-point representation he had >come back to me with a specification like IEEE 754, I would fire the >bastard on the grounds of professional incompetence. Not only does >IEEE 754 fail in its primary goal of standardizing the format, but >it invents a multiplicity of variations plus a new fantasy world of >nonstandard mathematics, the latter solely for the reason that in >SOME artificially simple situations, lack of care on the part of >algorithm designers could be automatically compensated for by >carrying invalid arithmetic results as the IEEE 754 special values. >Catering to amateur sloppiness while making life more difficult for >the careful professional is inexcusable in a technical standard. >It is insane to insist on conformance to such an abomination. At >best, it is a fact of life (especially in the small computer world) >that has to be somehow coped with, hopefully by the implementor of >one's programming language. I totally agree with this. If a calculation is being done whose result does not affect the final answer, it is a waste of time and possibly numerical accuracy. If it does affect the final answer, and it overflows, it is usually best to stop the calculation immediately and re-think the treatment of that value. There are a few cases where this is not true, but I think they are the exception, not the rule. Even where a calculation should proceed in the face of some out-of-range input, the right solution is more likely to be to substitute a finite number within the reasonable range than to go ahead with an infinite result. The best case for proceeding with an out-of-range input is when smoothing is going to be used to allow for the possibility that a few input values are totally wrong. Smoothing methods have an unfortunate tendency to spread infinities over many surrounding values. The problems with IEEE 754 nonstandard mathematics have real high level language implications. For example, there is a deep assumption that, for any two expressions x and y of arithmetic types, "x > y" is either true or false. This is embedded in the structure of the "if - then - else" construct. A truly logical high level language use of IEEE 754 would have to make explicit allowance for the possibility that "x > y" is neither true nor false, but meaningless because x and y are not ordered relative to each other. I am afraid that any attempt to extend high level languages to expose IEEE to the user program will have to be inconsistent in one of several ways. There is however, from a computer manufacturer point of view, tremendous utility in having an industry standard floating point format and rounding rules. For the forseeable future, we will be able to buy, in just about any required technology, off the shelf implementations of floating point hardware that will accept the same internal representation of floating point numbers. The current position on the FPS Model 500 is that we use IEEE format floating point, but run by default with all infinity and NaN generating operations trapped, and treat both zero representations as being true zero. This gets the benefits of a standard format, while conforming to the usual rule that it is best to report an error at the first point in the execution of the program that it is detectable. > >>Note that there is nothing about this that prevents a standard conforming >>Fortran implementation from being based on an IEEE floating point conforming >>platform. It just requires a little care on the part of the language >>implementer to ensure that all zeros are equal to each other and are never >>printed with minus signs. > >It is also probably advisable for implementors faced with 68881 etc. >FPUs to implement the programming language's negation operation by >loading a FP register with true_zero then subtracting the expression >to be negated from the register (leaving a sensible negative in the >register even when the expression happens to have zero value). There are actually two solutions to implementing a single, consistent zero, based on hardware that can yield a negative zero. One is to prevent negative zero from occurring. The other is to take care in displaying data (which is usually less frequent than negation) that the minus sign decision is based on comparison to zero, not a sign copy. Either solution, applied consistently, will work. They do yield different results in the face of explicit sign copy, and equivalence/union between integers and floating point. -- Patricia Shanahan ps@fps.com uucp : ucsd!celerity!ps phone: (619) 271-9940
gwyn@smoke.BRL.MIL (Doug Gwyn) (06/04/90)
In article <KINGDON.90Jun3221716@pogo.ai.mit.edu> kingdon@pogo.ai.mit.edu (Jim Kingdon) writes: > No, any use of NaNs and Infinities lies in the realm of undefined or > implementation-defined behavior, and thus is not constrained by the > C standard. >Unless I'm misreading something, in the "C" locale, ... I think you're reading the standard correctly. My point was that "reading back in" a NaN or Infinity is beyond the scope of the standard, because "writing it out" is beyond the scope. It is true that "NaN" would not be an appropriate way to externally represent a NaN, because of the argument you gave (strtod is well- defined as not parsing those characters as a number).
scjones@sdrc.UUCP (Larry Jones) (06/05/90)
In article <15576@bfmny0.BFM.COM>, tneff@bfmny0.BFM.COM (Tom Neff) writes: > >"If the scaled value is in the range of representable values (for its > >type) the result is either the nearest representable value, or the > >larger or smaller representable value immediately adjacent to the > >nearest representable value, chosen in an implementation-defined manner." > ^^^^^^^^^^^^^^^^^^^^^ > Does anyone else think this word choice is strange? I can understand > dealing with values outside the domain of exactly representable numbers > in this way -- let the compiler round up, down or to nearest as it sees > fit -- but if the target value is exactly representable, surely that > representation's use should be mandatory. As I recall, it was pointed out to us by someone far wiser in the matters of floating point than we, that requiring the compiler to get it exactly right was nearly impossible. Apparently, there are some values which would require (nearly?) infinite precision arithmetic to get exactly right. Even getting it to within one bit is exceedingly hard, which is why the standard allows for either of three values rather than just two. ---- Larry Jones UUCP: uunet!sdrc!scjones SDRC scjones@SDRC.UU.NET 2000 Eastman Dr. BIX: ltl Milford, OH 45150-2789 AT&T: (513) 576-2070 "You know how Einstein got bad grades as a kid? Well MINE are even WORSE!" -Calvin
eggert@twinsun.com (Paul Eggert) (06/05/90)
Patricia Shanahan writes:
A truly logical high level language use of IEEE 754 would have to make
explicit allowance for the possibility that "x > y" is neither true nor
false, but meaningless because x and y are not ordered relative to each
other.
In IEEE 754, x>y is always either true or false, and is never ``meaningless''.
A high level language can thus treat x>y like any other formula.
Granted, x>y differs from !(x<=y) when either x or y is not a number.
... any attempt to extend high level languages to expose IEEE to the
user program will have to be inconsistent ...
I hope that ``inconsistent'' here does not mean ``logically inconsistent'',
because then an implementation could not conform to both ANSI C and IEEE 754.
Instead it must mean something like ``inconsistent with what programmers
expect''. Not everyone agrees. Let us hope NCEG resolves this for C.
... we use IEEE format floating point, but run by default with all
infinity and NaN generating operations trapped, and treat both zero
representations as being true zero.
This does not conform to IEEE 754, which distinguishes -0 from 0, and in which
``The default response to an exception shall be to proceed without a trap.''
Of course, implementers who feel strongly that a standard is wrong need not
conform to it, so long as they do not claim conformance. Implementers can also
compromise by supplying compiler options that enable conformance, e.g. GCC's
-ansi and -pedantic options.
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/05/90)
In article <8924@celit.fps.com>, ps@fps.com (Patricia Shanahan) writes: > The problems with IEEE 754 nonstandard mathematics have real high level > language implications. For example, there is a deep assumption that, > for any two expressions x and y of arithmetic types, "x > y" is either true > or false. This is embedded in the structure of the "if - then - else" > construct. A truly logical high level language use of IEEE 754 would have > to make explicit allowance for the possibility that "x > y" is neither > true nor false, but meaningless because x and y are not ordered relative > to each other. Pop! (That was a credibility bubble bursting.) The comparisons >, >=, <, and <= are required by IEEE 754 to return true or false in the cases where you would expect them to return true or false, and when it is not possible to determine the ordering (e.g. Infinity < Infinity) the IEEE 754 standard requires an exception. A program that uses IEEE 754 arithmetic *CAN* rely on x > y being either true or false. The standard also recommends providing _additional_ operators (e.g. for C we might have !> meaning less, equal, or unordered), but that's only a recommendation, not a requirement. So IEEE 754 arithmetic does _exactly_ what you're asking for. > The current position on the FPS Model 500 is that we use IEEE format floating > point, but run by default with all infinity and NaN generating operations > trapped, and treat both zero representations as being true zero. This gets > the benefits of a standard format, while conforming to the usual rule that > it is best to report an error at the first point in the execution of the > program that it is detectable. Except for treating -0 and +0 as identical, that's absolutely fine according to the standard. (There must, to be fully conformant, be a mode in which NaNs and Infinities are returned, but nobody said that mode had to be fast. Patching up the results in a system-supplied signal handler is just fine.) In the absence of Infinities, there are two ways of generating and recognising -0: the copysign() function and friends, which are in the appendix and are therefore not required, and decimal<->binary conversion. If you haven't got copysign() and friends (and you are recommended but not required to) and you have divide by zero trapping instead of producing infinities, there is no arithmetic operation (in the usual sense) that can tell the difference. -- "A 7th class of programs, correct in every way, is believed to exist by a few computer scientists. However, no example could be found to include here."
ps@fps.com (Patricia Shanahan) (06/05/90)
I am actually replying to two articles together. In article <1990Jun5.024808.14003@twinsun.com> eggert@twinsun.com (Paul Eggert) writes: >Patricia Shanahan writes: > > A truly logical high level language use of IEEE 754 would have to make > explicit allowance for the possibility that "x > y" is neither true nor > false, but meaningless because x and y are not ordered relative to each > other. > >In IEEE 754, x>y is always either true or false, and is never ``meaningless''. >A high level language can thus treat x>y like any other formula. >Granted, x>y differs from !(x<=y) when either x or y is not a number. Arbitrarily treating relational operations between unordered floats as returning false is certainly one way of handling them. Because of exactly the point you make about the difference between x>y and !(x<=y) I do not consider it a truly logical solution. Suppose I have an if(x>y)then A else B; construct, and in code block B I assumed x<=y, because at the time I wrote it I thought floats were ordered, so !(x>y) implied (x<=y). Should block B be executed if x and y are unordered? Remember, I may have selected which way round to run the test on grounds of program readability. I could equally well have coded it as if(x<=y)then B else A;, and I won't really like it if they give different results. The real problem with treating relations between unordered floats as being inherently false is in the case when the contribution of some NaN/Infinity results to the final result of the program is limited to controlling the flow of program execution. In that case you could get a meaningless output, that does not have any NaN/Infinity results printed. In article <3158@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes: > Pop! (That was a credibility bubble bursting.) > The comparisons >, >=, <, and <= are required by IEEE 754 > to return true or false in the cases where you would expect > them to return true or false, and when it is not possible > to determine the ordering (e.g. Infinity < Infinity) the > IEEE 754 standard requires an exception. A program that uses > IEEE 754 arithmetic *CAN* rely on x > y being either true or > false. The standard also recommends providing _additional_ operators > (e.g. for C we might have !> meaning less, equal, or unordered), but > that's only a recommendation, not a requirement. > So IEEE 754 arithmetic does _exactly_ what you're asking for. Although I have said a lot about what I think a truly consistent implementation of the idea of NaN's and infinities in a high level language would have to do, I have not actually said anything about what IEEE 754 itself said on the subject. That is deliberate, because I do not have a copy of the standard in front of me and it is several years since the last time I read it all the way through. I do not claim to be an expert on what IEEE 754 says. My only real interest in this (other than curiosity) is that any extensions to C or Fortran for exposing IEEE 754 at the user level should be clean, robust, and consistent. Treating unordered comparisons as a trap seems more reasonable to me than Paul Eggert's statement that they should be treated as false. The only problem is that it is inconsistent with what seems to be the objective of delaying error reports to final output. I think my own preference would be to treat unordered comparison as an error and trap it, unless the program (through some new high level language construct) indicated what should be done with the unordered case. For example, one could add an additional clause to the traditional if-then-else, with the option of combining the additional clause with one of the cases. if(x>y) then{ /* code for x > y case */ } else{ /* cose for x <= y case */ } unordered{ /* give up gracefully, without taking a trap.*/ } if(x>y) then{ /* code for x > y case */ } else-unordered{ /* code for (x <= y) and unordered cases */ } This type of structure, combined with a trap on unordered comparison if no unordered clause was specified, would let the programmer control the treatment of unordered comparisons in a logical fashion. Incidentally, just as a matter of curiosity, if any of you have a copy of IEEE 754 handy, which does it say? Treat unordered comparisons as traps, unless a special operator is used to handle them, or treat all unordered relations as false. Although I am interested in whether it is possible to construct a set of extensions to a high level language that will provide truly consistent, logical, support for IEEE 754, I am still very dubious about whether the number of programs that would benefit would be sufficient, compared to the number for which taking a trap and core dump at the first arithmetic exception would be the right solution, to justify the effort. The biggest advantage I can see to all this, if it can be made consistent, is in optimization and vectorization. Currently we have to go to great lengths to avoid calculating a result if the result is not required by the program and its calculation could cause a trap. For example, consider a loop invariant but conditionally executed divide. It would make some optimizations easier if everything could be calculated and if it was not needed it just gets thrown away. -- Patricia Shanahan ps@fps.com uucp : ucsd!celerity!ps phone: (619) 271-9940
tim@proton.amd.com (Tim Olson) (06/06/90)
In article <8949@celit.fps.com> ps@fps.com (Patricia Shanahan) writes: | Incidentally, just as a matter of curiosity, if any of you have a copy of | IEEE 754 handy, which does it say? Treat unordered comparisons as traps, | unless a special operator is used to handle them, or treat all unordered | relations as false. The standard says that the result of a comparison shall be delivered in one of two ways -- either as a condition code identifying one of: less than equal to greater than unordered or as a true-false response to a comparison predicate. In the later case, if one or both of the operands is unordered, then the predicate returns the value defined in the spec (mostly "false", but not-equal (!=) would return "true"), and most predicates also signal an invalid operand exception. Whether this exception causes a trap or simply sets a "sticky" status bit depends upon whether traps have been enabled or not for the invalid operand exception. -- Tim Olson Advanced Micro Devices (tim@amd.com)
flaps@dgp.toronto.edu (Alan J Rosenthal) (06/06/90)
About external formats of bizarre non-numbers such as NaN. It's true that NaN and 0NaN aren't too good because strtod() et al have well-defined behaviour already on those strings. So how about something like \377NaN?
eggert@twinsun.com (Paul Eggert) (06/06/90)
Patricia Shanahan suggests trapping unordered comparison, combined with new syntax like: if (x>y) /* code for x > y case */; else /* code for x <= y case */; unordered /* give up gracefully, without taking a trap. */; There is no need to change C's syntax. For example, one could write: if (unordered(x,y)) /* give up gracefully, without taking a trap. */; else if (x>y) /* code for x > y case */; else /* code for x <= y case */; using the unordered() predicate suggested in IEEE 754's appendix. Unfortunately, the appendix is not binding. For example, Sun does not support unordered(x,y), and on Suns one must write something like isnan(x)||isnan(y) instead. NCEG should standardize this area. Shanahan also asks: ... if any of you have a copy of IEEE 754 handy, which does it say? Treat unordered comparisons as traps, unless a special operator is used to handle them, or treat all unordered relations as false. IEEE 754 says comparisons involving < or > signal the invalid operation exception if either operand is not a number. The default response to such an exception ``shall be to proceed without a trap'', and yield false. A user ``should be able to request a trap'' on such an exception. I urge those who seriously build or use ANSI/IEEE Std 754-1985 implementations to get and read a copy of the standard, if only to learn what they dislike about it! Aside from boilerplate, it is only eleven pages long, and it is easy to read.
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/07/90)
In article <8949@celit.fps.com>, ps@fps.com (Patricia Shanahan) writes: > Arbitrarily treating relational operations between unordered floats as > returning false is certainly one way of handling them. Because of exactly > the point you make about the difference between x>y and !(x<=y) I do not > consider it a truly logical solution. This is still bashing IEEE 754 for something it does not do. Let me quote section 5.7 of the IEEE 754 standard: It shall be possible to compare floating-point numbers in all supported formats, even if the operands' formats differ. Comparisons are exact and never overflow nor underflow. Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unodered with everything, including itself. Comparisons shall ignore the size of zero (so +0 = -0). The result of a comparison shall be delivered in one of two ways at the implementors option: either as a condition code identifying one of the four relations listed above, or as a true-false response to a predicate that names the specific comparison desired. In addition to the true-false response, an invalid operation exception (7.1) SHALL be signalled when, as indicated in Table 4, last column, unordered operands are compared [using <, >, <=, or >=]. Implementations that provide predicates SHALL provide the first six predicates in Table 4 [= and != do not signal exceptions for NaNs, but NaNs are never equal to anything, so #define isNaN(x) ((x) != (x)); <, >, <= and >= SHALL signal exceptions for NaNs] and SHOULD provide the seventh [ x ? y = x, y, unordered = isNaN(x)||isNaN(y)], and a means of logically negating predicates. Note that this invalid operation exception is one where it is _impossible_ to deliver a quiet NaN. > I think my own preference would be to treat unordered comparison as an error > and trap it, unless the program (through some new high level language > construct) indicated what should be done with the unordered case. For example, > one could add an additional clause to the traditional if-then-else, with the > option of combining the additional clause with one of the cases. No new construct is needed. #ifdef IEEE #define isNaN(x) ((x) != (x)) #define unOrd(x,y) (isNaN(x) || isNaN(y)) #else #define isNaN(x) ((x),0) #define unOrd(x,y) ((x),(y),0) #endif if (unOrd(x,y)) { /* either x or y is NaN */ } else if (x > y) { /* x > y; neither is NaN */ } else { /* x <= y; neither is NaN */ } > Although I am interested in whether it is possible to construct a set of > extensions to a high level language that will provide truly consistent, > logical, support for IEEE 754, I am still very dubious about whether the > number of programs that would benefit would be sufficient, compared to > the number for which taking a trap and core dump at the first arithmetic > exception would be the right solution, to justify the effort. What effort? Let your hardware be IEEE-ish (that is to say, accurate) except for always signalling exceptions. Surely you would like to have an exception-handling mechanism which lets you substitute an answer and continue (e.g. BSD signals). That's all you need. The classical example (it's in Apple's SANE book, for example) is continued fraction evaluation: there are ways of organising the calculation which give you good accuracy but would be simpler if 1/(1/0)) were 0. Obviously, little or no _pre_-IEEE754 code would benefit, precisely because it was written for arithmetic systems which didn't have features like gradual underflow (that's the one _I_ like; at last an arithmetic system where (x-y == 0) implies (x == y)) or infinities. And the fact that it is possible to write "self-checking" code in an IEEE 754 system (compute the same thing twice with rounding set differently and see how different the answers are) is something worth having. -- "A 7th class of programs, correct in every way, is believed to exist by a few computer scientists. However, no example could be found to include here."