delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2000/05/16/09:54:08

Message-Id: <200005161320.JAA11285@delorie.com>
From: "Dieter Buerssner" <buers AT gmx DOT de>
To: djgpp-workers AT delorie DOT com
Date: Tue, 16 May 2000 15:26:20 +0200
MIME-Version: 1.0
Subject: Re: Math functions
CC: djgpp-workers AT delorie DOT com
References: <200005151237 DOT PAA11341 AT is DOT elta DOT co DOT il>
In-reply-to: <Pine.SUN.3.91.1000516142823.24814B@is>
X-mailer: Pegasus Mail for Win32 (v3.12b)
X-MIME-Autoconverted: from Quoted-printable to 8bit by delorie.com id JAA11287
Reply-To: djgpp-workers AT delorie DOT com

On 16 May 00, at 14:28, Eli Zaretskii wrote:

> On Mon, 15 May 2000, Dieter Buerssner wrote:
> > This would only be a problem, when the FPU instruction would generate
> > different results, depending on the exception flags.
> 
> No, that's not what I meant.

Ok. We obviously agree, that when a result for some special arguments 
differs, from what the FPU creates, that this must be checked.
My comment was about your sentence

>We avoid exceptions to have control on what values are returned in
>abnormal cases:

From this I thought, there might be a possibility I dindn't know, 
that there are different results depending on the FPU-CW. Otherwise, 
I would think, that this has nothing to do with exception. Obviously 
I want to produce correct results.

Also, most FPU instructions, that I can think of, already produce 
"correct" results for abnormal cases. (Even fpatan2 for 0,0 would 
produce the stupid result, that is demanded by Standard C.)

> > When you "silently" return a NaN, say by log(-1.0), the bit will not 
> > be set in the status word.
> 
> Are you sure?  

I tested it on my AMD K6-2. I will append some source.

> The Intel manual seems to say otherwise: it says that
> any invalid operation sets the appropriate bit in the status word.

Loading a NaN into a FPU register seems not to be an invalid 
operation (which makes sense to me).

> > A NaN can dissapear at least by the following functions: copysign, 
> > signbit, pow, fmax, fmin .... (This behaviour is demanded by C99.) 
> 
> IMHO these are not important cases

This may be a question of personal opinion. I personally find them 
important. I find it rediculous, that a portable program must be 
aware of pow(NaN, 0) == 1.0.

> > Also, code that only compares a produced NaN, will loose it. Or 
> > something like:
> > 
> >   double x, y, z;
> >   /* assume x is NaN */
> >   z = (x < y) ? x : y;
> 
> How does this lose? z is not a NaN, but y (or x) still is, and you can
> find it.

I thought, this was obvious. Change z to x. Such code is not that 
unlikely at all (perhaps hided in a macro x = FMAX(x,y);

I think your argument against exceptions boils down to: Protect the 
clueless user, that pastes some FPU-CW changing code into his 
program, from that horrible trace-back messages.

My argument boils down to, give anybody who actually knows what he 
does the choice. (And not that important, the poor user should be 
happy, that he found a bug.)

For any portable program (that usually has no access to the FPU-CW), 
I cannot see any differences.

> > > The main motivation for avoiding exceptions was the ANSI Standard, as
> > > I already said.  
> > 
> > I cannot find this in my draft to the C 99.
> 
> I meant C89.  C99 was not yet finalized when Eric started his work.

I think, it is clear, that I don't want to critizise Eric at all.
 
> > F.9.3.7 The log functions
> > log(±0) returns -Infinity and raises the divide-by-zero exception.
> 
> I believe that you interpret the word ``exception'' too literally
> here.  The C99 standard does not specify what does ``exception''
> mean. 

This perhaps is a quality of implementation issue. In Annex H 
(informative) I read:

C does require that SIGFPE be the signal corresponding to arithmetic 
exceptions, if there is any signal raised for them.

C supports signal handlers for SIGFPE and allows trapping of 
arithmetic exceptions. When arithmetic exceptions do trap, C’s signal-
handler mechanism allows trap-and-terminate (either default 
implementation behavior or user replacement for it) or trap-and-
resume, at the programmer’s option.

Again, I don't think, that exceptions should be the default. Just 
give people, who wants them, the choice.

> > "Swallowing" these exceptions, would mean, that there is no interest 
> > in beeing IEEE compatible, as suggested by the C standard.
> 
> Since the status word has an appropriate bit set, we don't swallow any
> exceptions.  We simply don't generate a signal out of them, but that's
> another matter.

Maybe, you can run my appended test program. It would be important to 
know, whether this is reproducable.

> > I just looked. I.e. the inline log function IHMO has exactly the 
> > problem I described above.
> 
> What problem is that?  If you mean how to tell GCC that inline
> assembly uses several slots in the FP stack, perhaps there is no
> problem if glibc doesn't care to do anything.  

Yes, this is, what I meant. It is not that easy, to find an actual 
test case, where this fails, because you need some source, that needs 
already 7 entries in the FP stack. When using some more stack 
entries, it would be easy to find a case, where you get stack 
overflow.

Rethinking your suggestion about the inline versions, I came to the 
different conclusion, that it may work with many functions. (I think, 
I also know a workaround for the stack problem in the above case, 
that just needs one fld more)

Writing all the inline assembly leaves an uncomfortable feeling, though. x87 inline assembly is almost not documented. And I have been bitten by the famous "fixed register spilled" already. (Actually for testing the math functions with Moshier's qfloat, that I had rewritten in inline assembly for 
an order of magnitude speed increase.)

> If you think there
> might be a problem, perhaps a message posted to glibc and/or gcc
> forum(s) might be a good idea.

Done.

Regards,
Dieter Buerssner

With the following program I get the output:

after silent inf   x = Inf, sw = 0x00
after trapping inf x = Inf, sw = 0x04
after silent NaN   x = NaN, sw = 0x00
after trapping NaN x = NaN, sw = 0x01

#include <stdio.h>

#define CLEAR_SW() __asm__ volatile("fclex")
#define GET_SW(sw) __asm__ volatile("fstsw %0" : "=m" (sw)); sw &= 0xff

unsigned long INF=0x7F800000;
unsigned long NAN=0xFFC00000;
#define DOUBLE(L) (*(float *)(&(L)))

double one=1.0, zero1=0.0, zero2=0.0;

double si(void) {return DOUBLE(INF);}
double ti(void) {return one/zero1;}
double sn(void) {return DOUBLE(NAN);}
double tn(void) {return zero1/zero2;}

int main(void)
{
  unsigned short sw;
  double x;
  CLEAR_SW();
  x = si();
  GET_SW(sw);
  printf("after silent inf   x = %f, sw = 0x%02hx\n", x, sw);
  CLEAR_SW();
  x=ti();
  GET_SW(sw);
  printf("after trapping inf x = %f, sw = 0x%02hx\n", x, sw);
  CLEAR_SW();
  x = sn();
  GET_SW(sw);
  printf("after silent NaN   x = %f, sw = 0x%02hx\n", x, sw);
  CLEAR_SW();
  x=tn();
  GET_SW(sw);
  printf("after trapping NaN x = %f, sw = 0x%02hx\n", x, sw);
  return 0;
}

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019