From nobody@FreeBSD.org  Wed Jan  5 18:01:34 2005
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 8438B16A4CE
	for <freebsd-gnats-submit@FreeBSD.org>; Wed,  5 Jan 2005 18:01:34 +0000 (GMT)
Received: from www.freebsd.org (www.freebsd.org [216.136.204.117])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 552E543D66
	for <freebsd-gnats-submit@FreeBSD.org>; Wed,  5 Jan 2005 18:01:34 +0000 (GMT)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.13.1/8.13.1) with ESMTP id j05I1YG7040939
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 5 Jan 2005 18:01:34 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.13.1/8.13.1/Submit) id j05I1Xtc040919;
	Wed, 5 Jan 2005 18:01:33 GMT
	(envelope-from nobody)
Message-Id: <200501051801.j05I1Xtc040919@www.freebsd.org>
Date: Wed, 5 Jan 2005 18:01:33 GMT
From: "Nelson H. F. Beebe" <beebe@math.utah.edu>
To: freebsd-gnats-submit@FreeBSD.org
Subject: fpsetsticky() incorrectly clears, instead of sets, floating-point exception flags on IA-32
X-Send-Pr-Version: www-2.3
X-GNATS-Notify: vs

>Number:         75862
>Category:       i386
>Synopsis:       fpsetsticky() incorrectly clears, instead of sets, floating-point exception flags on IA-32
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    vs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jan 05 18:10:41 GMT 2005
>Closed-Date:    Tue Apr 05 15:28:26 GMT 2005
>Last-Modified:  Tue Apr 05 15:28:26 GMT 2005
>Originator:     Nelson H. F. Beebe
>Release:        4.4 and 5.0
>Organization:
University of Utah/Mathematics
>Environment:
FreeBSD XXXXXX.utah.edu 4.4-RELEASE FreeBSD 4.4-RELEASE #0: Tue Oct i386 i386 Pentium III/Pentium III Xeon/Celeron FreeBSD
FreeBSD XXXXXX.utah.edu 5.0-RELEASE FreeBSD 5.0-RELEASE #0: Thu Jan 16 22:16:53 GMT 2003     root@hollin.btc.adaptec.com:/usr/obj/usr/src/sys/GENERIC i386 i386 Pentium III/Pentium III Xeon/Celeron FreeBSD


>Description:
The fpsetsticky() macro in <flloatingpoint.h>, e.g.,
http://minnie.tuhs.org/FreeBSD-srctree/newsrc/i386/include/floatingpoint.h.html#fpgetsticky
is defined like this:
#define fpresetsticky(m)      ((fp_except_t) __fpsetreg(0, FP_STKY_REG, (m), FP_STKY_OFF))
#define fpsetsticky(m)	fpresetsticky(m)

That is wrong: fpresetsticky() CLEARS exception flags, and
fpsetsticky() SETS them.  The same API is present on HP-UX 10,
OpenBSD 3.2 and NetBSD 1.6, and works correctly on all of them.
It appears from the above URL that the error is long standing since
it is still the current 5.3 sources.

There is a secondary issue as well: "man fpgetsticky" does not
document fpsetsticky(), even though the latter is a critical
routine: one must be able to both set and test the floating-point
exception flags.


>How-To-Repeat:
The attached program produces this correct output on NetBSD, and OpenBSD:

cc test-invalid-bsd.c -lm && ./a.out 
After clear,         fpgetsticky() returns 0x00
After 1 / 0,         fpgetsticky() returns 0x04
z = inf
After clear,         fpgetsticky() returns 0x00
After nan * nan,     fpgetsticky() returns 0x00
z = nan
After clear,         fpgetsticky() returns 0x00
After inf - inf,     fpgetsticky() returns 0x01
z = nan
After clear,         fpgetsticky() returns 0x00
After big * big,     fpgetsticky() returns 0x28
z = inf
After clear,         fpgetsticky() returns 0x00
After small * small, fpgetsticky() returns 0x30
z = 0
After clear,         fpgetsticky() returns 0x00
After 1/3,           fpgetsticky() returns 0x20
z = 0.33333333333333331

For use on HP-UX, remove the include of <ieeefp.h> and #define
FP_X_DNML to 0; the output numbers are different (PA-RISC versus
IA-32), but still correct.

On FreeBSD, I get this:
cc test-invalid-bsd.c -lm && ./a.out 
After clear,         fpgetsticky() returns 0x05
After 1 / 0,         fpgetsticky() returns 0x05
z = inf
After clear,         fpgetsticky() returns 0x05
After nan * nan,     fpgetsticky() returns 0x05
z = nan
After clear,         fpgetsticky() returns 0x05
After inf - inf,     fpgetsticky() returns 0x05
z = nan
After clear,         fpgetsticky() returns 0x05
After big * big,     fpgetsticky() returns 0x2d
z = inf
After clear,         fpgetsticky() returns 0x2d
After small * small, fpgetsticky() returns 0x3d
z = 0
After clear,         fpgetsticky() returns 0x3d
After 1/3,           fpgetsticky() returns 0x3d
z = 0.33333333333333331

The returned flags from fpgetsticky() are nonsense, because
they were never set properly in the first place.

Here is the test program:

% cat test-invalid-bsd.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ieeefp.h>

#if !defined(FP_X_STK)
#define FP_X_STK	0x40
#endif

#define FP_ALL (FP_X_INV | FP_X_DNML | FP_X_DZ | FP_X_OFL | FP_X_UFL | FP_X_IMP | FP_X_STK)

int
main(int argc, char* argv[])
{
    double inf, nan, x, y, z;
    x = 1.0;
    y = x;
    z = y - x;
    inf = x / z;
    nan = z / (x - y);

    fpsetsticky(~FP_ALL);
    (void)printf("After clear,         fpgetsticky() returns 0x%02x\n", fpgetsticky());
    z = x / z;
    (void)printf("After 1 / 0,         fpgetsticky() returns 0x%02x\n", fpgetsticky());
    (void)printf("z = %.17lg\n", z);

    fpsetsticky(~FP_ALL);
    (void)printf("After clear,         fpgetsticky() returns 0x%02x\n", fpgetsticky());
    z = nan * nan;
    (void)printf("After nan * nan,     fpgetsticky() returns 0x%02x\n", fpgetsticky());
    (void)printf("z = %.17lg\n", z);

    fpsetsticky(~FP_ALL);
    (void)printf("After clear,         fpgetsticky() returns 0x%02x\n", fpgetsticky());
    z = inf - inf; 
    (void)printf("After inf - inf,     fpgetsticky() returns 0x%02x\n", fpgetsticky());
    (void)printf("z = %.17lg\n", z);

    z = 1.0e+200;
    fpsetsticky(~FP_ALL);
    (void)printf("After clear,         fpgetsticky() returns 0x%02x\n", fpgetsticky());
    z = z * z;
    (void)printf("After big * big,     fpgetsticky() returns 0x%02x\n", fpgetsticky());
    (void)printf("z = %.17lg\n", z);

    z = 1.0e-200;
    fpsetsticky(~FP_ALL);
    (void)printf("After clear,         fpgetsticky() returns 0x%02x\n", fpgetsticky());
    z = z * z;
    (void)printf("After small * small, fpgetsticky() returns 0x%02x\n", fpgetsticky());
    (void)printf("z = %.17lg\n", z);

    z = 1.0;
    fpsetsticky(~FP_ALL);
    (void)printf("After clear,         fpgetsticky() returns 0x%02x\n", fpgetsticky());
    z = z / 3.0;
    (void)printf("After 1/3,           fpgetsticky() returns 0x%02x\n", fpgetsticky());
    (void)printf("z = %.17lg\n", z);

    return (EXIT_SUCCESS);
}


>Fix:
I found that this redefinition of fpsetsticky() solves the problem:

#define fpsetsticky(e)		((fp_except)__fpsetreg(0x3f, FP_STKY_REG, (e), FP_STKY_OFF))

With that change, I can now have a C99-style interface (sadly
still lacking the BSD world) like this:
#define feclearexcept(e)	(void)fpsetsticky(~((fp_except)(e)))
#define feraiseexcept(e)	(void)fpsetsticky(fpgetsticky() | ((fp_except)(e)))
#define fetestexcept(e)		((int)(fpgetsticky() & (fp_except)(e)))

where the C99 flags are defined like this:

#define FE_DIVBYZERO		FP_X_DZ
#define FE_INEXACT		FP_X_IMP
#define FE_INVALID		FP_X_INV
#define FE_OVERFLOW		FP_X_OFL
#define FE_UNDERFLOW		FP_X_UFL


>Release-Note:
>Audit-Trail:

From: Bruce Evans <bde@zeta.org.au>
To: "Nelson H. F. Beebe" <beebe@math.utah.edu>
Cc: freebsd-gnats-submit@freebsd.org, freebsd-i386@freebsd.org
Subject: Re: i386/75862: fpsetsticky() incorrectly clears, instead of sets,
 floating-point exception flags on IA-32
Date: Mon, 10 Jan 2005 09:50:12 +1100 (EST)

 On Wed, 5 Jan 2005, Nelson H. F. Beebe wrote:
 
 > >Description:
 > The fpsetsticky() macro in <flloatingpoint.h>, e.g.,
 > http://minnie.tuhs.org/FreeBSD-srctree/newsrc/i386/include/floatingpoint.h.html#fpgetsticky
 > is defined like this:
 > #define fpresetsticky(m)      ((fp_except_t) __fpsetreg(0, FP_STKY_REG, (m), FP_STKY_OFF))
 > #define fpsetsticky(m)	fpresetsticky(m)
 >
 > That is wrong: fpresetsticky() CLEARS exception flags, and
 > fpsetsticky() SETS them.  The same API is present on HP-UX 10,
 > OpenBSD 3.2 and NetBSD 1.6, and works correctly on all of them.
 > It appears from the above URL that the error is long standing since
 > it is still the current 5.3 sources.
 
 Yes, it was wrong when first committed in FreeBSD-1 in 1993 and has never
 been fixed.  A thread in freebsd-standards has some relevant details:
 
 %%%
 On Tue, 20 Jan 2004, Bruce Evans wrote:
 
 > On Mon, 19 Jan 2004, David Schultz wrote:
 > > ...
 > > By the way, the man pages refer to fpresetsticky(3), but not
 > > fpsetsticky(3).  The MI ieee.h header declares only fpsetsticky(),
 > > but some architectures override these with equivalent macros for
 > > both.  If you have a good idea of how this is supposed to be, it
 > > would be great if you could fix it.
 >
 > I once thought that fpresetsticky() was standard and fpsetsticky() was
 > intentionally undocumented because it is nonstandard, but this seems
 > to be backwards -- it is fpresetsticky() that is the mistake.  Look
 > them up on the web.  There are more hits for fpsetsticky, and google
 > even suggests I wanted fpsetsticky when I seach for fpresetsticky.
 >
 > The C99 interfaces should make these moot.  It has equivalents to
 > both, and also feraiseexcept().
 >
 > > Another option is to just implement the relevant parts of fenv.h.
 > > They are fairly straightforward, and there's no sense in hiding
 > > behind the brokenness of gcc's i386 backend forever.
 %%%
 
 David committed an implementation of fenv.h last June.
 
 There is also a lot of bogusness in the arrangement of <floatingpoint.h>
 and <ieeefp.h>.  IIRC, the arrangement was sort of backwards
 <floatingpoint.h> essentially now just includes <ieeefp.h>.  Check
 google or a Sun system for details of where things should be.
 
 > >Fix:
 > I found that this redefinition of fpsetsticky() solves the problem:
 >
 > #define fpsetsticky(e)		((fp_except)__fpsetreg(0x3f, FP_STKY_REG, (e), FP_STKY_OFF))
 >
 > With that change, I can now have a C99-style interface (sadly
 > still lacking the BSD world) like this:
 > #define feclearexcept(e)	(void)fpsetsticky(~((fp_except)(e)))
 > #define feraiseexcept(e)	(void)fpsetsticky(fpgetsticky() | ((fp_except)(e)))
 > #define fetestexcept(e)		((int)(fpgetsticky() & (fp_except)(e)))
 
 But these have not been lacking in FreeBSD since last June.  They are in
 FreeBSD-5.3.
 
 Bruce
State-Changed-From-To: open->feedback 
State-Changed-By: vs 
State-Changed-When: Sun Mar 13 16:23:07 GMT 2005 
State-Changed-Why:  


http://www.freebsd.org/cgi/query-pr.cgi?pr=75862 
Responsible-Changed-From-To: freebsd-i386->vs 
Responsible-Changed-By: vs 
Responsible-Changed-When: Sun Mar 13 16:31:55 GMT 2005 
Responsible-Changed-Why:  
Yikes, darn edit-pr. What I wanted to know is if you could please verify that 
the issue has indeed been fixed. 

Regards and sorry for the line-noise, 
Volker 

http://www.freebsd.org/cgi/query-pr.cgi?pr=75862 
State-Changed-From-To: feedback->closed 
State-Changed-By: vs 
State-Changed-When: Tue Apr 5 15:26:38 GMT 2005 
State-Changed-Why:  
From commit-log: 
Remove fpsetsticky().  This was added for SysV compatibility, but due 
to mistakes from day 1, it has always had semantics inconsistent with 
SVR4 and its successors. [...] 
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/amd64/include/ieeefp.h?rev=1.13&content-type=text/x-cvsweb-markup 

Thanks for your submission! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=75862 
>Unformatted:
