gcl-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: +-Inf and NaN


From: Camm Maguire
Subject: Re: +-Inf and NaN
Date: Mon, 26 Feb 2024 08:28:05 -0500
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux)

Greetings, and thanks to all for the very helpful feedback!

I agree with the wide consensus that common-lisp implementations should
support the standard IEEE float hardware as seamlessly as possible.
That said, it is clear, as others have noted, that NaN being a float in
particular contravenes the ansi spec in several places.  So in summary,
we do not 'spec-thump' and aim for ansi+'reasonable subsequent
innovations'....

I'd also like to note in passing that the excellent SBCL out of the box
triggers an error, which we separately refer to as an 'exception trap',
when NaN is passed to the comparison functions, e.g. '=.  And low and
behold, gcl does the same when 'trapping' is turned on, but gcl turns
trapping off 'out of the box'.  NaN does not trigger an exception nor
error when passed to '+,'*,'cos etc. but returns NaN.  This is the
current state of play.  What follows is a discussion of what it means
and the best way to fit NaN into the common-lisp type lattice so it does
not get in the way of the compiler.

In summary, NaN seems indistinguishable to me from an ignorable error
indicator at the hardware level.  It is not conceptually a 'valid input'
to any of these functions, but is a clever design to propagate the error
properly when ignored.  Conceptually, it seems no different from
'ignore-errors in common-lisp.  So really the spec is not all that far
away from IEEE.

As for common-lisp types, the obvious path is what GCL and others
currently implement, double precision NaN is of type 'long-float and
hence 'number.  This actually means that lisp will *stray* from IEEE and
*not* trigger an error at the lisp level when NaN is passed to '=, but
rely on the hardware, when available, to trigger a 'super error' when
desired.  Moreover, it substantially complicates the obvious type
calculus for long-floats as the bounds are propagated, as NaN cannot be
ordered with all other members of the type.  So the most probable path
for me with GCL is to break 'long-float into a separate 'nan kingdom
with one element and a subtype 'ordered-long-float for everything else.
This means the compiler will optimize (= a a) (and more importantly
*equivalent* bindings, see below) to t if and only if compiling at
safety 0 (see below) *and* there is some bounding information on the
long float, e.g. (declare ((long-float 0.0) a)) or successful return
from 'cos, etc.  I think this will make all users happy.

Alternatively, double precision NaN is not of type 'long-float nor
number, and common-lisp '= will *follow* IEEE triggering a
common-lisp-ignorable error when NaN is input to '=, with GCL at least
'out-of the-box' having a handler which ignores the error.  '(= a a) and
the like compile to t at safety 0 and the type system is just that
described by the ansi spec.  Other functions are supplied to detect the
NaN at the user level.

On equivalent bindings....

There was some discussion that the gains available to the compiler under
such considerations are trivial.  This has not been my experience, at
least dealing with integer computations.  '(eq a a) essentially never
occurs, but GCL at present can detect when the same object is passed to
'eq in different forms, e.g.

(disassemble '(lambda (x) (let* ((z x)) (eq x (cadr (assoc x (list (list z 
z))))))))

is just 't at safety 0.

On safety levels of the compiler.....

There was some comment that the compiler should produce code yielding
the same results on all valid inputs at all safety levels, and indeed
this is so.  Typically one drops the type checks at the top of all
supplied and inlined functions at safety 0, assuming all inputs are
valid.  Invalid inputs supplied to functions compiled at safety 0 yield
unpredictable results, e.g. NaN.  So with the first proposal,
common-lisp '= will trigger no error on NaN input and there will be
nothing to drop when compiled at safety 0.  In the second proposal, the
(ignored) error is dropped when compiled at safety 0 but the NaN is
still accessible/trappable at the hardware level as currently unless the
compiler reasons that an object is compared to itself.  In both cases
one can 'trap' the 'exception' at the hardware level separately from the
handling of errors at the common-lisp level.  The point here being that
NaN is conceptually an *invalid* input, and what we are discussing is
how/when to trigger/trap it, and to ensure that when not doing so it
propagates properly through subsequent results.  Either proposal
satisfies these criteria.

It might be of interest to note that gcc appears to follow the first
proposal.  d==d is compiled away if and only if compiling with
optimization *and* d has successfully passed some numerical comparison.

It would also be useful in this discussion to be able to enumerate, say
for x86, all machine instructions which can output a NaN on non-NaN
inputs.  0.0/0.0 and (hardware)sqrt(-1) are the only ones known to me.

Thoughts most welcome....

Take care,



Richard Fateman <fateman@gmail.com> writes:

> The answer for lisp is not the same as the answer for Maxima.
> For example, in Maxima, the sum "command" works this way:
> sum(i^2,i,1,10)   returns  55.  Same as lisp  (loop for i from 1 to 10 sum i)
> but
> sum(i^2,i,1,n)   returns  sum(i^2,i,1,n);
> sum(i^2,i,1,n), simpsum returns  (2*n^3+3*n^2+n)/6
>   while lisp would complain about undefined variable n...
>
> Maxima tolerates symbolic stuff, and generally tries to defer reporting an 
> error.
> For  lisp, I would generally hope to push computations as far as possible
> even if some inputs were NaN (etc.)
> Some of this may be "free" in the sense that if you just follow the 
> arithmetic,
> you are ok. Other examples, probably not.
> Consider (cos NaN).  Returning NaN is one option. How about returning
> an interval [-1,1]?
> {one way of implementing intervals may be to encode them ... e.g.  NaN 
> payload is
> a pointer to a hashtable entry. The entry is lower/upper bounds  like [-1,1].
> }
> RJF
>
> On Thu, Feb 22, 2024 at 12:45 PM Camm Maguire <camm@maguirefamily.org> wrote:
>
>  Greetings!  Several thoughts sent separately:
>
>  Stavros Macrakis <macrakis@gmail.com> writes:
>
>  >  >  If NaN was truly 'not a number', the numerical functions would trigger
>  >  >  an error on input only when compiled with safety, and might be arranged
>  >
>  > Don't agree. If you're doing a calculation with more than one result,
>  > it is perfectly reasonable for some of the results to be NaNs and
>  > other results to be valid. Aborting the calculation is not the right
>  > thing to do.
>  >
>
>  Just curious, do you think a lisp function taking several numerical
>  arguments and returning several numerical values should not trigger an
>  error when passed a symbol as argument, presuming that some of the
>  returned values might still be correctly computed?
>
>  To my mind, type checking on arguments is done at the top of a
>  function.  If compiling without safety, the type checks can be dropped. 
>
>  Take care,
>  -- 
>  Camm Maguire                                        camm@maguirefamily.org
>  ==========================================================================
>  "The earth is but one country, and mankind its citizens."  --  Baha'u'llah
>

-- 
Camm Maguire                                        camm@maguirefamily.org
==========================================================================
"The earth is but one country, and mankind its citizens."  --  Baha'u'llah



reply via email to

[Prev in Thread] Current Thread [Next in Thread]