Message-Id: <200005161320.JAA11285@delorie.com> From: "Dieter Buerssner" To: djgpp-workers AT delorie DOT com Date: Tue, 16 May 2000 15:26:20 +0200 MIME-Version: 1.0 Content-type: text/plain; charset=ISO-8859-1 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: X-mailer: Pegasus Mail for Win32 (v3.12b) Content-Transfer-Encoding: 8bit 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 #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; }