NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: lib/49240: Some corner cases for pow() fail to work correctly
The following reply was made to PR lib/49240; it has been noted by GNATS.
From: Havard Eidnes <he%NetBSD.org@localhost>
To: gnats-bugs%NetBSD.org@localhost
Cc:
Subject: Re: lib/49240: Some corner cases for pow() fail to work correctly
Date: Wed, 01 Oct 2014 12:26:54 +0200 (CEST)
> I'll work out a minimal diff for our e_pow.c.
This follows below. With that and the test correction for C99
behaviour, the tests now all pass when I test locally.
The changes rolled into this are:
* Change yy1 to y1; no need for yy1.
* Add a case for 1**y = 1, even if y is NaN (or +-Inf)
* Use (x+0.0)+(y+0.0) instead of x+y when mixing NaNs. There's
a long explanation of why this is done in FreeBSD's commit log
for e_pow.c at
http://svnweb.freebsd.org/base/head/lib/msun/src/e_pow.c?view=log
* Add a case for -1**+-Inf, since C99 and IEEE 754 revised 2008(?)
reportedly state that this should be 1
* Add a fix to get correct sign for some over- and underflow
cases. This seems to come from fdlib 5.3, FreeBSD revision
129956. (Ref. above SVN log.)
* Add a fix to get well-defined behaviour when right-shifting a
possibly negative value by adding a cast.
* Fix comment detailing the expected result for the special cases
--- /usr/src/lib/libm/src/e_pow.c 2013-03-27 19:12:07.000000000 +0100
+++ e_pow.c 2014-10-01 11:41:10.000000000 +0200
@@ -29,13 +29,13 @@
* Special cases:
* 1. (anything) ** 0 is 1
* 2. (anything) ** 1 is itself
- * 3. (anything) ** NAN is NAN
+ * 3. (anything) ** NAN is NAN except 1 ** NAN = 1
* 4. NAN ** (anything except 0) is NAN
* 5. +-(|x| > 1) ** +INF is +INF
* 6. +-(|x| > 1) ** -INF is +0
* 7. +-(|x| < 1) ** +INF is +0
* 8. +-(|x| < 1) ** -INF is +INF
- * 9. +-1 ** +-INF is NAN
+ * 9. +-1 ** +-INF is 1
* 10. +0 ** (+anything except 0, NAN) is +0
* 11. -0 ** (+anything except 0, NAN, odd integer) is +0
* 12. +0 ** (-anything except 0, NAN) is +INF
@@ -98,10 +98,10 @@
ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/
double
-__ieee754_pow(double x, double y)
+pow(double x, double y)
{
double z,ax,z_h,z_l,p_h,p_l;
- double yy1,t1,t2,r,s,t,u,v,w;
+ double y1,t1,t2,r,s,t,u,v,w;
int32_t i,j,k,yisint,n;
int32_t hx,hy,ix,iy;
u_int32_t lx,ly;
@@ -113,10 +113,13 @@
/* y==zero: x**0 = 1 */
if((iy|ly)==0) return one;
- /* +-NaN return x+y */
+ /* x==1: 1**y = 1, even if y is NaN */
+ if (hx==0x3ff00000 && lx == 0) return one;
+
+ /* y!=zero: result is NaN if either arg is NaN */
if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) ||
iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0)))
- return x+y;
+ return (x+0.0)+(y+0.0);
/* determine if y is an odd int when x < 0
* yisint = 0 ... y is not an integer
@@ -142,7 +145,7 @@
if(ly==0) {
if (iy==0x7ff00000) { /* y is +-inf */
if(((ix-0x3ff00000)|lx)==0)
- return y - y; /* inf**+-1 is NaN */
+ return one; /* (-1)**+-inf is 1 */
else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */
return (hy>=0)? y: zero;
else /* (|x|<1)**-,+inf = inf,0 */
@@ -174,7 +177,11 @@
}
}
+ /* CYGNUS LOCAL + fdlibm-5.3 fix: This used to be
n = (hx>>31)+1;
+ but ANSI C says a right shift of a signed negative quantity is
+ implementation defined. */
+ n = ((u_int32_t)hx>>31)-1;
/* (x<0)**(non-int) is NaN */
if((n|yisint)==0) return (x-x)/(x-x);
@@ -250,11 +257,11 @@
t2 = z_l-(((t1-t)-dp_h[k])-z_h);
}
- /* split up y into yy1+y2 and compute (yy1+y2)*(t1+t2) */
- yy1 = y;
- SET_LOW_WORD(yy1,0);
- p_l = (y-yy1)*t1+y*t2;
- p_h = yy1*t1;
+ /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */
+ y1 = y;
+ SET_LOW_WORD(y1,0);
+ p_l = (y-y1)*t1+y*t2;
+ p_h = y1*t1;
z = p_l+p_h;
EXTRACT_WORDS(j,i,z);
if (j>=0x40900000) { /* z >= 1024 */
Home |
Main Index |
Thread Index |
Old Index