[comp.arch] IEEE floating point & various approaches; long

mash@mips.COM (John Mashey) (11/01/90)

In article <2451@charon.cwi.nl> dik@cwi.nl (Dik T. Winter) writes:
...
>The true Cray and Convex users do not care about IEEE.  Their machines do
>not have IEEE conformant arithmetic; far from it.  They want results; fast.
>They do not care about correctness.  :-)

Note, also, that this somewhat applies to the IBM RS/6000 series.
Legally, but unlike almost all other IEEE FP implementations,
FP OPERATIONS DO NOT NORMALLY TRAP ON EXCEPTION CONDITIONS;
i.e., SIGFPE doesn't normally do anything.

You choices are:
	a) Run the code in a mode where FP operations are sequentialized,
	which of course seriously degrades performance, but gives
	precise exceptions. (good for debug)
	b) Make explicit calls in your code to routines that test the
	status of the various flags. (probably not favored by programmers,
	especially since it de-portabilizes otherwise-portable code.)
	c) (I'm not sure if this is shipped yet, or not; does anybody KNOW?):
	use compiler switches that
	generate the relevant tests, like at end of statement, end of
	function, or program exit.

There are some reasonable reasons from this, and I attach the relevant
quotes from the IBM J of R & D that describes the RS/6000.
HOWEVER, people may want to be warned that code ported from other machines,
may possibly NOT trap exceptions the way that most other IEEE machines do.
(I would assume that if you trap SIGFPE, that the runitme at least, on
program exit, tells you that there has been an exception "sometime".
If it doesn't do this, I suspect there is a bunch of code out there
that thought it was protecting itself, and didn't.)

Here's what the IBM man page says:
  fp_clr_flag, fp_set_flag, fp_read_flag, or fp_swap_flag 
.....
    Description
  
      The RISC System/6000 currently does not generate an interrupt
  for floating-point exceptions.  Therefore, the common  method  of
  catching  the  signal SIGFPE  and  calling  an  appropriate  trap
  handler to identify a floating-point exception is not supported.
  
      These subroutines aid in determining when an exception has
  occurred and the exception type.  These subroutines can be called
  explicitly  around blocks of code that may cause a floating-point
  exception.

Then, here is some explanation (long):

From IBM J Res & Dev, Vol 34, No 1, January 1990 (IBM RS/6000 issue)

p.33-34 (CAPITALS MINE)
"Another very important aspect of fully exploiting floating-point performance
is the method of presentation of floating-point exceptions and the precision
in identifying the instructions that cause floating-point exceptions.
Exceptions are a natural and perhaps expected consequence of floating-point
operaitons, and most can be handled by default rules.  (Default exception
handling is defined by the IEEE standard.)  These default rules can be
managed completely in hardware and require no program intervention after
initialization.

The IEEE default rules do not always provide the desired result, however.
Since the standard allows for a program fix-up after an exception, the
architecture problem is then to define a mechanism to permit program fixu-up.
The most straightforward approach is to specify that a floating-point
interrupt at the failing instruction will occur whenever there is a
floating-point exception that is not defaulted.  The hardware implication
of this is that all instructions after a floating point instruction must
be conditional until it is know that no exceptions are possible on that
instruction.  Some floating point instructions take many cycles, and exceptions
may not be known until the last cycle of the instruction.  Therefore, most
implementations would serialize on floating-point instructions-if not the
first, then the second; if not all, then some.  The inclusion of a floating-
point interrupt would sacrifice much of the potential floating-point
performance.

AN ALTERNATIVE STRATEGY IS NOT TO REPORT AN INTERRUPT AT ALL, BUT SIMPLY TO
SET A BIT INDICATING THAT A FLOATING-POINT EXCEPTION HAS OCCURRED.  IT IS
THEN UP TO A PROGRAM TO TEST FOR FLOATING-POINT EXCEPTIONS.  Different
compiler strategies can be used as to where it is appropriate to test for
these exceptions.  Since the definition of the exception also includes the
setting of summary information, it is possible to test at the end of a program,
at the end of a subprogram, or at the end of a statement where a floating-point
operation was used.  This level of precision can be controlled by
linker/compiler option.  None of these tell exactly where the exception
occurred; they simply identify where it occurred.  In most cases, this
information is sufficient.

However, if the exact failing instruction must be known, there are two
possible strategies.  One can insert a test for the exception after each
floating-point instruction, or one can tag each queued and/or executing
floating-point instruction with its address.  Inserting code to test for
every possible exception is yet another mode for the compiler to manage,
necessitates recompilation, and can significantly expand execution time.
Address tagging of "active" floating-point instructions identifies the
failing instruction exactly.  However, it does require that the
implementation keep track of the address tags.  Moreover, it is not
synchronous; that is, if an exception occurs, the location of the
failing instruction is reported, but not before the program
has gone beyond that point.  Fix-up may still be possible, but in general
this method only permits localization of the failing instruction.
Consider the case ofthe inner-loop product described in Figure 7.
This loop consists of two floating-point loads, one floating-point
multiply-add, and one branch.  The "active" floating-point instructions
will all be instances of the same multiply-add instruction.  If an
exception occurs, what is know is the address of the instruction, not
the iteration number.  The benefit of this approach is speed; floating-point
performance is not limited by exception recognition.  The drawback, as
outline above, is the precision with which the fault is determined.

RISC System/6000 architecture adopted a two-part strategy.  THE PRINCIPAL
APPROACH WOULD BE TEST-CODE INSERTION, with the compilers able to insert
such code at the statement or (sub)program level.  The linker also
supports the enabling of test code at program exit, ensuring the ability 
to report a floating-point exception if it occurs anywhere within the
program.

To avoid recompilation in order to identify the failing operation exactly,
the architecture also adopted a synchronize mode, in which an interrupt
can be generated, identifying the failing instruction by running the
machine with one floating-point instruction dispatched at a time.  This
technqiue has the same weakness as code insertion; THAT IS, FLOATING-POINT
PERFORMANCE IS GREATLY REDUCED.  However, it may not be as bad as code
insertion, because the synchronization can be managed by hardware rather than
by extra code inserted by software.  It is expected that the mode will only
by extra code inserted by software.  IT IS EXPECTED THAT THE MODE WILL ONLY
BE USED BY CERTAIN PROGRAMS AND THEN ONLY TO DEBUG THEIR ALGORITHMS."
-- 
-john mashey	DISCLAIMER: <generic disclaimer, I speak for me only, etc>
UUCP: 	 mash@mips.com OR {ames,decwrl,prls,pyramid}!mips!mash 
DDD:  	408-524-7015, 524-8253 or (main number) 408-720-1700
USPS: 	MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (11/01/90)

In article <42597@mips.mips.COM>, mash@mips.COM (John Mashey) writes:
> Note, also, that this somewhat applies to the IBM RS/6000 series.
> Legally, but unlike almost all other IEEE FP implementations,
> FP OPERATIONS DO NOT NORMALLY TRAP ON EXCEPTION CONDITIONS;
> i.e., SIGFPE doesn't normally do anything.

Am I missing something?  Any floating-point system where the *default*
mode of operation is to generate traps does *NOT* in that mode conform
to the IEEE 754 standard.  If you _want_ traps, then you have to call
some system-specific extension to get them.  So the complaint appears
to be that the RS/6000 (like the Sun implementations of floating point)
conforms to the letter of the standard.
-- 
The problem about real life is that moving one's knight to QB3
may always be replied to with a lob across the net.  --Alasdair Macintyre.

mash@mips.COM (John Mashey) (11/02/90)

In article <4174@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
>In article <42597@mips.mips.COM>, mash@mips.COM (John Mashey) writes:
>> Note, also, that this somewhat applies to the IBM RS/6000 series.
>> Legally, but unlike almost all other IEEE FP implementations,
>> FP OPERATIONS DO NOT NORMALLY TRAP ON EXCEPTION CONDITIONS;
>> i.e., SIGFPE doesn't normally do anything.
>
>Am I missing something?  Any floating-point system where the *default*
>mode of operation is to generate traps does *NOT* in that mode conform
>to the IEEE 754 standard.  If you _want_ traps, then you have to call
>some system-specific extension to get them.  So the complaint appears
>to be that the RS/6000 (like the Sun implementations of floating point)
>conforms to the letter of the standard.

Sorry,  I guess I wasn't clear enough. Let me say what I said again,
but explain further:

1) In IEEE 754:
	1a
	"The implementor may, at his option, implement the following
	modes: traps disabled/enabled, to handle exceptions."

	1b
	"There are five types of exceptions that shall be signaled when
	detected.  The signal entails setting a status flag, taking a
	trap, or possibly doing both. With each exception should be
	associated a trap under user control, as specified in Section 8.
	THE DEFAULT RESPONSE TO AN EXCEPTION SHALL BE TO PROCEED WITHOUT
	A TRAP....For each type of exception the implementation shall
	provide a status flag that shall be set on any occurrence of
	the corresponding exception when no corresponding trap occurs."

	1c
	"A user should be able to request a trap on any of the five
	exceptions by specifying a handler for it......When an
	exception whose trap is disabled is signaled, it shall be
	handled in the manner specified in Section 7. [mash: i.e.,
	by setting setting flags]  WHEN AN EXCEPTION WHOSE TRAP IS ENABLED
	IS SIGNALED THE EXECUTION OF THE PROGRAM IN WHICH THE EXCEPTION
	OCCURRED SHALL BE SUSPENDED, THE TRAP HANDLER PREVIOUSLY SPECIFIED
	BY THE USER SHALL BE ACTIVATED, AND A RESULT, IF SPECIFIED IN
	SECTION 7, SHALL BE DELIVERED TO IT."  goes on to describe what
	trap handlers can do.

2) Now, I take from this that what IBM did IS legal, from 1a above:
they basically made trapping exceptions impossible in normal code
(unless you run in "sequentialize" mode, which is NOT something you'd
do except for debugging.) 

3) Now, what I meant in the original quote is:
	a) The default (for anybody) is to ignore exceptions,
	if nobody says anything.
	b) On most, if not all IEEE-compliant machines, if you put
	a trap in for SIGFPE, you get feature 1c; at least, you get
	an exception signalled, and at worst, you go to a part of the
	run-time that tells you something went wrong, and where, and how.
	At best, you get a trap handler than can do what 1c says,
	fixing up values, etc. (I don't know how well all the various
	implementations are at doing this.)
	You get this on the same binary that you would normally distribute,
	that runs at full speed, and you get it by including 1 statement.
	You DO NOT GET this effect from the same IBM binary that
	runs at full speed. 

4) So, anyway, the point is: it's perfectly legal, (and the committee
carefully allowed for such things), but the effect is different:
if you just compile the same portable code that may run on many other
machines, and on those machines, will at least report exceptions
fairly precisely, in the binary you'd distribute, you will NOT get
that effect on the IBMs.  Clearly, the supercomputer world has proved
that there exists a market that wants max speed, perhaps with some
loss in error handling. People should understand what the difference
is.

Now, how about input from people who actually develop FP programs:
a) Do you use traps for SIGFPE, or not?
b) If SIGFPE doesn't do anything, and you have to change the source code
widely to check for traps, is this: no problem, slightly painful,
or excruciating.
-- 
-john mashey	DISCLAIMER: <generic disclaimer, I speak for me only, etc>
UUCP: 	 mash@mips.com OR {ames,decwrl,prls,pyramid}!mips!mash 
DDD:  	408-524-7015, 524-8253 or (main number) 408-720-1700
USPS: 	MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086

mcdonald@aries.scs.uiuc.edu (Doug McDonald) (11/02/90)

In article <42618@mips.mips.COM> mash@mips.COM (John Mashey) writes:
>
>Now, how about input from people who actually develop FP programs:
>a) Do you use traps for SIGFPE, or not?

No. In my scientific codes, the correct answer for an underflow is zero, 
and I test for zero before I divide, if this could ever happen
in normal use. In very rare cases I have seen NaNs and Infs. These
imply to me that there is a bug in my code. So I debug it.

Doug McDonald

jonah@dgp.toronto.edu (Jeff Lee) (11/02/90)

mash@mips.COM (John Mashey) writes:
>Now, how about input from people who actually develop FP programs:
>a) Do you use traps for SIGFPE, or not?

I'm not a numerical analyst, but I did recently take a graduate course
on numerical software and the advice given by the professor was to
avoid traps and make explicit tests for +INF, -INF, NaN, and 0 where
appropriate.  Usually only one or two are appropriate in any given
case and explicitly checking makes sure that you *think* about the
possibilities for overflow/underflow, how to avoid it (if possible),
and how to retain accuracy.  The opinion on traps was that they were
generally more trouble than they were worth, especially if the trap
handler overhead was any significant amount.

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (11/02/90)

In article <42618@mips.mips.COM>, mash@mips.COM (John Mashey) writes:
> 	1b
> 	"There are five types of exceptions that shall be signaled when
		   ****
> 	detected.  The signal entails setting a status flag, taking a
> 	trap, or possibly doing both. With each exception should be
> 	associated a trap under user control, as specified in Section 8.
> 	THE DEFAULT RESPONSE TO AN EXCEPTION SHALL BE TO PROCEED WITHOUT
> 	A TRAP....For each type of exception the implementation shall
> 	provide a status flag that shall be set on any occurrence of
> 	the corresponding exception when no corresponding trap occurs."
> 
> 	1c
> 	"A user should be able to request a trap on any of the five
> 	exceptions by specifying a handler for it......When an
					       **
> 	exception whose trap is disabled is signaled, it shall be
> 	handled in the manner specified in Section 7. [mash: i.e.,
> 	by setting setting flags]  WHEN AN EXCEPTION WHOSE TRAP IS ENABLED
> 	IS SIGNALED THE EXECUTION OF THE PROGRAM IN WHICH THE EXCEPTION
> 	OCCURRED SHALL BE SUSPENDED, THE TRAP HANDLER PREVIOUSLY SPECIFIED
> 	BY THE USER SHALL BE ACTIVATED, AND A RESULT, IF SPECIFIED IN
> 	SECTION 7, SHALL BE DELIVERED TO IT."  goes on to describe what
> 	trap handlers can do.

> 	b) On most, if not all IEEE-compliant machines, if you put
> 	a trap in for SIGFPE, you get feature 1c;

It's worth pointing out that SIGFPE does not meet the IEEE criteria.
SIGFPE is a blanket "some kind of floating point exception, or maybe
some kind of integer exception, _I_ know but I'm not going to tell _you_".
(That is, according to SVID 2.  I don't know what SVr4 does.  4.xBSD
does have the courtesy to tell you what kind of exception you got.)
The IEEE standard calls for *separate* enabling and disabling of each
of the five traps, with separate handlers of the user's choice for each.
I'm going to cite the SVID rather than 1003.1, and that's for two reasons.
First, my copy of 1003.1 is in another city and I don't remember exactly
what it says.  Second, the degree to which the RS/6000 behaviour has
something to do with the degree to which you have been able to rely on
SIGFPE in the past, and 1003.1 is not the past.  The SVID says that
	SIGFPE		floating-point exception
			[What does this mean?  Which things generate
			exceptions?  The SVID does not say.  You have
			never been given _any_ guarantee.]
when you supply a handler:
	"the receiving process is to execute the signal-catching function...
	The signal number sig [here SIGFPE] will be passed as the only
	argument to the signal-catching function.
	Additional arguments may be passed to the signal-catching
	function for hardware-generated signals."
	[The last two sentences seem contradictory.  I take it that if
	you have hardware floating point it may pass additional arguments
	to your signal handler, but a software emulation may only pass
	SIGFPE and nothing else.  Absurd, but that seems to be what it says.]
	"Upon return from the signal-catching funciton, the receiving
	process will resume execution at the point at which it was
	interrupted, except for implementation defined signals where this
	may not be true."
	[I.e. it's anyone's guess what return from a handler may do, but
	the system documentation should _say_.]
			
So UNIX code written to the SVID
    -- never had any guarantee about what floating-point operations
       could generate signals
    -- had no way of telling the difference between underflow and overflow
       or for that matter between overflow and INTEGER divide by 0
    -- had no way of finding out where the exception had occurred
    -- could not resume or bypass the operation, but had either to
       halt the program or longjmp() out

UNIX systems providing IEEE-conforming traps do exist, but differ.
-- 
The problem about real life is that moving one's knight to QB3
may always be replied to with a lob across the net.  --Alasdair Macintyre.

akhiani@ricks.enet.dec.com (Homayoon Akhiani) (11/02/90)

Can anyone mail me the following article, I can not find it.

[IEEE floating point & various approaches;long]

thanks in advance,

akhiani@ricks.enet.dec.com

henry@zoo.toronto.edu (Henry Spencer) (11/03/90)

In article <1990Nov1.232508.18287@jarvis.csri.toronto.edu> jonah@dgp.toronto.edu (Jeff Lee) writes:
>...The opinion on traps was that they were
>generally more trouble than they were worth, especially if the trap
>handler overhead was any significant amount.

All the more so if you run into a situation like Mike O'Dell tells about
at Prisma, in which catching the trap drastically slows down your code
(because it demands that the trap occur at well-defined times, which a
heavily pipelined blazing-fast machine has real trouble with).
-- 
"I don't *want* to be normal!"         | Henry Spencer at U of Toronto Zoology
"Not to worry."                        |  henry@zoo.toronto.edu   utzoo!henry

mash@mips.COM (John Mashey) (11/03/90)

In article <4186@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:

>It's worth pointing out that SIGFPE does not meet the IEEE criteria.
>SIGFPE is a blanket "some kind of floating point exception, or maybe
>some kind of integer exception, _I_ know but I'm not going to tell _you_".
>(That is, according to SVID 2.  I don't know what SVr4 does.  4.xBSD
>does have the courtesy to tell you what kind of exception you got.)
>The IEEE standard calls for *separate* enabling and disabling of each
>of the five traps, with separate handlers of the user's choice for each.
>I'm going to cite the SVID rather than 1003.1, and that's for two reasons.
>First, my copy of 1003.1 is in another city and I don't remember exactly
>what it says.  Second, the degree to which the RS/6000 behaviour has
>something to do with the degree to which you have been able to rely on
>SIGFPE in the past, and 1003.1 is not the past.  The SVID says that

1) SVID, to be honest, was fairly irrelevant to this, in that it
described an interface, but gave as a future direction IEEE
exception-handling.  (note, of course, that FP was not exactly a major
concern of that issue of the SVID, and I'd certainly guess that the bulk
of the UNIX-based FP computing has been done on BSD-derived OSs,
or merged variants.)

2) People who've built IEEE-based computers who worried about FP
have long ago included some approximation or other to full IEEE-signal
handlers.  I'm sure Sun did; we did, and I'd assume others did also.
Maybe somebody from Sun & HP would say what they do.  We of course let you
turn on/off specific traps, and when you get a SIGFPE, there's a field
to tell you which one it was.
-- 
-john mashey	DISCLAIMER: <generic disclaimer, I speak for me only, etc>
UUCP: 	 mash@mips.com OR {ames,decwrl,prls,pyramid}!mips!mash 
DDD:  	408-524-7015, 524-8253 or (main number) 408-720-1700
USPS: 	MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (11/05/90)

In article <42677@mips.mips.COM>, mash@mips.COM (John Mashey) writes:
> In article <4186@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:

> 1) SVID, to be honest, was fairly irrelevant to this,

Eh?  Oh gosh, this means I have been fantastically stupid.
You see, when I tried to write numeric code that would run under "UNIX",
I used the SVID as a portability guide.  

> in that it described an interface, but gave as a future direction IEEE
> exception-handling.

Darn it, so _that_ was the answer.  Instead of trying to write programs
that would use the facilities available *now* (actually, *then*) I should
have waited for the future (no IEEE signal handling in V.3).

BSD UNIX *does* provide adequate information to an SIGFPE handler,
for my former purposes, except that the documentation could do with
improving (what is the difference between FPE_FLTOVF_FAULT and
FPE_FLTOVF_TRAP, for example?).  But if your code is to run on a V.2
box, that really doesn't help, it merely adds the pain of Tantalus
to that of Sisyphus.

> 2) People who've built IEEE-based computers who worried about FP
> have long ago included some approximation or other to full IEEE-signal
> handlers.  I'm sure Sun did; we did, and I'd assume others did also.

(a) Not all.  (I suppose it depends on how worried they were.)
(b) Certainly Sun did.  Twice.  And Sun's software FP on the 3/50 never
generated no signals nohow (I don't know about 4.x). People who wanted
their code to be portable *had* to write it so that it would work
without IEEE exception handling, even if they only wanted their code to
work on IEEE + 4.xBSD UNIX boxes, even if they further restricted
portability to MC680x0-based machines, because there wasn't a
*standard*, not even a de-facto standard, interface.  The really
horrible thing was that Sun were one of a very small number of companies
that obeyed the letter of IEEE 754 law and gave you the default
behaviour required by that standard, so not only could you not rely on
*getting* interrupts, you couldn't rely on *not* getting them either.


I suppose the contrast is between companies/universities/research units
that had serious number-crunching requirements and picked a UNIX system
to suit and inviduals who having a UNIX system ready to hand wanted to
get some number-crunching done and wanted it to work on whichever system
they'd be able to get time on next.

-- 
The problem about real life is that moving one's knight to QB3
may always be replied to with a lob across the net.  --Alasdair Macintyre.

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (11/06/90)

  While this discussion is interesting, I suspect that most of the FP
code in the world runs without exceptions, trapped, untrapped, defined
or anonymous. That's what they pay numerical analysts to insure.

  I think the IEEE behavior will come in the future, but I don't see a
huge demand for it. Partially because the big CPU users are usually
running on some box with non-iEEE math anyway.
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
      The Twin Peaks Halloween costume: stark naked in a body bag

khb@chiba.Eng.Sun.COM (Keith Bierman fpgroup) (11/06/90)

In article <42677@mips.mips.COM> mash@mips.COM (John Mashey) writes:
...
   2) People who've built IEEE-based computers who worried about FP
   have long ago included some approximation or other to full IEEE-signal
   handlers.  I'm sure Sun did; we did, and I'd assume others did also.
   Maybe somebody from Sun & HP would say what they do.  We of course let you
   turn on/off specific traps, and when you get a SIGFPE, there's a field
   to tell you which one it was.


This is a topic worthy of a long discussion, however I will cop out
and note that

	800-3555-10	Numerical Computation Guide

was our (speaking as member of the fpgroup, I did not actually touch
the text much) last best shot at it. The Reader's Digest version is

	istat=ieee_handler("set",ieee_execption_of_your_choice,handler)

The NCG is part of the "normal" languages release, so f77v1.3+, c1.0+,
etc. releases should have it tucked into the binder. It is in
"binder2" of the Fortran docs, for example.

--
----------------------------------------------------------------
Keith H. Bierman    kbierman@Eng.Sun.COM | khb@chiba.Eng.Sun.COM
SMI 2550 Garcia 12-33			 | (415 336 2648)   
    Mountain View, CA 94043