[mercury-users] IEEE float (was: Newbie problem)

Fergus Henderson fjh at cs.mu.OZ.AU
Mon Jun 21 16:29:44 AEST 1999


On 21-Jun-1999, Richard A. O'Keefe <ok at atlas.otago.ac.nz> wrote:
> [Fergus wrote:]
> > From the Mercury library reference manual:
> > 
> >     % Note that implementations which support IEEE floating point
> >     % should ensure that in cases where the only valid answer is a "NaN"
> >     % (the IEEE float representation for "not a number"), the det
> >     % functions here will halt with a runtime error (or throw an exception)
> >     % rather than returning a NaN.  Quiet (non-signalling) NaNs have a
> >     % semantics which is not valid in Mercury, since they don't obey the
> >     % axiom "all [X] X = X".
> > 
> > So, even on machines which support IEEE floating point, the intent is that
> > the Mercury `float' type will never have any NaN values.
> > 
> This makes perfect sense, *BUT* it violates both the letter and the
> spirit of the IEEE standards (754 and 854).  If you are happy to say
> that Mercury does not conform to the IEEE standard and that simple
> algorithms translated from Pascal, Fortran, or C (which *does* offer
> IEEE compliance as an option in C9x) to Mercury will *not* have the
> same behaviour, then that's fine.

Well, in the long term I would like to also offer a separate `ieee_float'
type, for interfacing with other systems and/or for use with algorithms
designed for IEEE floating point.  In fact it would probably be best
to have several different types, corresponding to the different
precisions: single, extended single, double, and extended double.

However, in order to keep the library interface small and easily
comprehensible, I think that adding those types should wait until
we have typeclass-ized the standard library.

> However, while the IEEE standards (and LIA standards) require certain
> operations, they do NOT require them to have the obvious names.  Mercury
> could reclaim IEEE conformance by offering
> 
>     ieee_compare(O, X, Y) ==>
>     O = (<) iff X < Y according to IEEE rules,
>     O = (=) iff X = Y according to IEEE rules,
>     O = (>) iff X > Y according to IEEE rules,
>     failure otherwise.
> 
> That is, if NaNs were allowed as values.

Yes, that is the kind of approach that we'd use for the IEEE float types.
But that in itself is not sufficient -- we'd also need to change the behaviour
of equality (`=') for the IEEE floating point types so that negative zero
did not compare equal to positive zero.

This would lead to some potentially counter-intuitive behaviour in the
presence of negative zeros.  You would need to be very careful to use
`if ieee_equal(X, 0.0) then ...' rather than `if X = 0.0 then ...'.

IEEE floating point has a number of counter-intuitive properties
which IMHO make it unsuitable for "casual" use.  That's why I wanted
to give the Mercury `float' type a simpler semantics, with no NaNs
or negative zeros.

My observation is that most programmers are not experts on IEEE floating
point arithmetic or numerical analysis.  Some programs don't use floating
point at all, many programs make minor use of floating point, and a few
programs (and/or libraries) make significant use of floating point.
For programs that make significant use of floating point, the IEEE
features are certainly useful, and the programmer must of necessity
become competent with the use of IEEE floating point.
But for programs that make only minor use of floating point, the programmer
will typically not be very competent with the use of IEEE floating point,
and will often fail to consider boundary cases such as floating point
overflow, NaNs, negative zeros, and so forth.

Furthermore, although Mercury does aim to support numerical programming,
numerical programming is not Mercury's "raison detre", and so
realistically speaking I think it is likely that Mercury programs which
make casual use of floating point are going to greatly outnumber those
that make intensive use of it.

> Let's face it, there's a rather nasty
> property that IEEE "equality" has even if you DO rule out NaNs
> and infinities:
> 
>     X is 0.0,
>     Y is -X,
>     ieee_compare(=, X, Y),
>     \+ (X == Y).
> 
> Because X and Y have different behaviour (e.g. 1.0/X is +infinity,
> while 1.0/Y is -infinity, and of course there's copysign() as well),
> they must *not* be regarded as identical (unifying) by Mercury.
> But the IEEE standards say they *must* compare equal.  The only
> way I can see to solve that requires splitting '=' and IEEE
> comparison apart.  That having been done, to ensure correct
> behaviour with +/- 0.0, there remains no equality-based argument
> against QNaNs.

Yes -- my argument against QNaNs and negative zeros is based more on
usability issues; it's not just a question of whether or not we can
implement IEEE conformance and still be compatible with Mercury's rules
on equality, it's also a question of whether the result will be easy to use.

But I didn't explain that in my previous mail, so you are right to pick me
up on that.

> Let's take it one step at a time.
> (0) I've demolished the argument that you should *want* to do this.

I think I failed to explain that argument in sufficient detail.
Perhaps you might reconsider this in the light of the additional
arguments that I have furnished above.

> > Note that unfortunately the current implementation does not yet fully
> > implement this intent -- the library reference manual goes on to explain
> > this:
> > 
> >     % XXX Unfortunately the current Mercury implementation does not
> >     % do that on all platforms, since neither ANSI C nor POSIX provide
> >     % any portable way of ensuring that floating point operations
> >     % whose result is not representable will raise a signal rather
> >     % than returning a NaN.  (Maybe C9X will help...?)
> >     % The behaviour is correct on Linux and Digital Unix,
> >     % but not on Solaris, for example.
> 
> (1) ANSI C and POSIX do not provide a portable way of requiring that
>     certain valid operations with defined results must raise signals.

It is of course possible for the Mercury implementation to implement
floating point arithmetic so that operations which result in NaNs will
raise a Mercury exception, even if neither ANSI C nor POSIX provide
any direct support for this.  The comment in the library reference
manual is there as an explanation, indeed as an excuse ("we haven't
done this yet, because doing it is a non-trivial exercise"), but it
doesn't mean that the task is impossible or even infeasible.

> (2) That rather misses the point of how IEEE arithmetic is *supposed*
>     to be used.  The idea is that you do your calculation, and then
>     you check at the end whether the result is unusual.

That is a fair point.  However, my (perhaps limited) observation is that
most programs which use floating point arithmetic do not use it in that way.
Thus I think it may be worth designing floating point support to make it
easier to use and to match the way it is actually used, rather than the way
that it is *supposed* to be used.

IEEE floating point with quiet NaNs can be dangerous if you're not careful,
because NaNs do not always propagate.  In particular, comparisons
such as `if (X < 1.0)' can silently return false if `X' is a quiet NaN.
This can lead to programs that when fed unexpected inputs will quietly
produce bogus output, rather than complaining or aborting.

> (3) Note that it is not sufficient to rely on requesting floating
>     point exceptions.  You still have to check *every* floating
>     point number returned by C code.  What if a Mercury program
>     calls quiet_nan(1) on a Solaris system?

Yes.  The language interoperability issues are probably the strongest
argument against the current design for Mercury floating point semantics.

However, such checks could easily be inserted by the Mercury compiler,
which for many architectures already has to insert code to box and unbox
floats when passing them to/from C code. 

>     For example, X is sqrt(Y) would have to involve a check under the
>     present scheme, but not under either of the schemes I've suggested.

Yes, the present design is likely to be a little less efficient,
if/when the compiler implements the checks that it requires.
It is designed for simplicity and safety rather than for maximizing
efficiency, in the expectation that most users of `float' in Mercury
will use it infrequently. 

For intensive users of floating point arithmetic, the IEEE floating
point types would be better, so we should eventually provide those too,
as an alternative.

Does that more detailed explanation make the current design seem more
reasonable?

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3        |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-users mailing list
post:  mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the users mailing list