mit-scheme-devel
[Top][All Lists]
Advanced

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

Re: [MIT-Scheme-devel] floating-point environment


From: Taylor R Campbell
Subject: Re: [MIT-Scheme-devel] floating-point environment
Date: Fri, 8 Oct 2010 03:57:34 +0000
User-agent: IMAIL/1.21; Edwin/3.116; MIT-Scheme/9.0.1

(Apologies for the long message; perhaps my first one was too terse.)

   Date: Thu, 7 Oct 2010 12:38:59 -0700
   From: address@hidden (Matt Birkholz)

   Cool.  Reminds me of set-floating-error-mask! in
   src/swat/scheme/control-floating-point.scm, except much
   more... fine-grained?

Yes.

   The "environment" is, in the abstract, a control register whose bits
   choose whether the machine will produce NaN-like values, OR signal
   conditions?  (That's the flavor I got from SWAT's
   set-floating-error-mask!.)

Roughly, yes.  The floating-point environment stores at least

- a rounding mode,

- a set of bits indicating whether particular exceptional situations
  should cause the machine/program to trap (the exception mask), and

- information regarding exceptional situations that have occurred even
  if the trap was masked (the exception flags).

In most systems (e.g., i386 and amd64), the last is simply a set of
bits indicating whether any floating-point operation has encountered a
particular exceptional situation since it was last cleared, which is
reflected by FLO:TEST-EXCEPTIONS.  Theoretically, exception flags
might be more interesting -- they might describe the operation that
was in progress at the time of the exception, they might describe
parts of the floating-point environment, &c.

   I expect we all would like to see compiled code run flat out, with
   float temporaries NOT in the heap, relying implicitly on a
   Scheme-friendly default environment.  Everyone ELSE should pay,
   including threads that tweak the default, and ESPECIALLY callouts to
   alien libraries (or even the many non-open-coded primitives?).  Yes?

The floating-point environment is implemented by the physical machine.
Open-coded flonum primitives do not check for exceptional situations;
they rely on the machine to do that.  Exposing the floating-point
environment won't change the code that LIAR generates for existing
flonum primitives -- or at least, it shouldn't require substantial
changes.

(I had to change the i386 code generated for FLONUM-ROUND &c. when
Chris exposed the rounding mode, because those primitives assumed the
default rounding mode.  Nevertheless, the changes were minor, and it
will not be necessary to change FLO:+, FLO:*, &c., or to box flonums
that are currently unboxed.)

I haven't thought about how microcoded primitives should interact with
the floating-point environment.  E.g., should ((UCODE-PRIMITIVE
FLONUM-DIVIDE) 1. 0.) give an infinity if the divide-by-zero exception
is masked?  Currently it doesn't.

   Can a Scheme-side cache of the machine configuration avoid much of the
   cost of thread switching by punting fesetenv() when switching to a
   thread with the same expectation of the floating-point environment?

I don't understand your question.  What I have in mind is for every
continuation invocation -- including those used to switch threads --
to do fesetenv.  It may also be necessary to frob the floating-point
environment in the thread timer interrupt handler.  This all may turn
out to be prohibitively expensive -- I'll find out when I implement
and measure it.

Does this answer your question?

   I do not understand the motivation for all the complexity around
   flo:defer-exceptions! (and thus unmasked raised [pending?]
   exceptions?, first-class floating-point environments, etc.).  SWAT
   actually only uses set-floating-error-mask! with a mask of 0,
   presumably disabling the signaling (and enabling the NaNs?).  Do you
   have a usage case in mind for flo:defer-exceptions!?

The API is basically the union what one finds in C99, BSD libc, and
glibc.  FLO:DEFER-EXCEPTIONS! corresponds with C99's feholdexcept, and
FLO:UPDATE-ENVIRONMENT! with C99's feupdateenv.  FLO:DEFER-EXCEPTIONS!
and FLO:UPDATE-ENVIRONMENT! are definable in terms of operations on
the exception mask, but C99 has no such operations, although BSD libc
and glibc both do.

IEEE 754-2008 describes exception handling a little more generally,
including a default exception handling mode (where in exceptional
situations operations just return infinities, NaNs, &c.) and several
alternate exception handling modes; and suggests that languages
provide some way to associate a choice of alternate exception handling
mode with each program block for each type of exception, if the
programmer should so desire.  The alternate exception handling modes
are much more elaborate than any machine or language I know provides.

Here is an example of FLO:DEFERRING-EXCEPTIONS:

(flo:deferring-exceptions
 (lambda ()
   (let ((frobnozzle (compute-me-up-a-tiny-frobnozzle)))
     (if (fix:zero? (flo:test-exceptions (flo:exception:underflow)))
         frobnozzle
         (begin
           (flo:clear-exceptions! (flo:exception:underflow))
           0.0)))))

In this program, underflow is acceptable -- it will just give zero
instead.  But any other exception should trap, if the caller wanted
trapping behaviour, and that is what FLO:UPDATE-ENVIRONMENT! does at
the end of FLO:DEFERRING-EXCEPTIONS.

One could instead use

(flo:with-exception-mask
    (fix:or (flo:masked-exceptions) (flo:exception:underflow))
  (lambda ()
    (let ((frobnozzle (compute-me-up-a-tiny-frobnozzle)))
      (if (fix:zero? (flo:test-exceptions (flo:exception:underflow)))
          frobnozzle
          (begin
            (flo:clear-exceptions! (flo:exception:underflow))
            0.0)))))

The difference is subtle: FLO:DEFERRING-EXCEPTIONS restores the whole
floating-point environment (including the rounding mode and exception
flags), and then raises any exceptions that were raised by the
procedure; FLO:WITH-EXCEPTION-MASK restores only the floating-point
exception mask, and traps if the procedure has raised any exceptions
that the caller had not masked.  Which one is more useful, I don't
know.

(And if you are on a non-i386, non-amd64 system with C99 but neither
glibc nor BSD libc, then FLO:WITH-EXCEPTION-MASK won't work at all,
whereas FLO:DEFERRING-EXCEPTIONS will.)



reply via email to

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