[comp.std.c] Questions about NCEG

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."