NetBSD Problem Report #49240
From he@smistad.uninett.no Sun Sep 28 16:24:23 2014
Return-Path: <he@smistad.uninett.no>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
by mollari.NetBSD.org (Postfix) with ESMTPS id 31E01A6554
for <gnats-bugs@gnats.NetBSD.org>; Sun, 28 Sep 2014 16:24:23 +0000 (UTC)
Message-Id: <20140928162416.DC18C3D0B8@smistad.uninett.no>
Date: Sun, 28 Sep 2014 18:24:16 +0200 (CEST)
From: he@NetBSD.org
Reply-To: he@NetBSD.org
To: gnats-bugs@gnats.NetBSD.org
Subject: Some corner cases for pow() fail to work correctly
X-Send-Pr-Version: 3.95
>Number: 49240
>Category: lib
>Synopsis: Some corner cases for pow() fail to work correctly
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Sep 28 16:25:01 +0000 2014
>Last-Modified: Sat Oct 04 14:25:00 +0000 2014
>Originator: Havard Eidnes
>Release: NetBSD 6.1_STABLE
>Organization:
>Environment:
System: NetBSD smistad.uninett.no 6.1_STABLE NetBSD 6.1_STABLE (MAANEN) #0: Mon Sep 1 14:42:18 CEST 2014 root@smistad.uninett.no:/usr/obj/sys/arch/i386/compile/MAANEN i386
Architecture: i386
Machine: i386
>Description:
While working on pkgsrc-wip/v8's test suite, I found one of
them which failed which excercise the pow() function, by
first doing some simple sanity checking and then trying out
various corner cases.
The v8 test bails out on the first test failure. I've
translated the tests into C, which runs through all the tests
counting up the successes and failures, displaying the result
of each test, with details for the failing tests.
Mostly, the failing tests on NetBSD appear also to contradict
the comments about the "Special cases" near the top of our
lib/libm/src/e_pow.c. The test failures and the special case
confirming the test is OK and our code is not are:
Test failure: NaN == pow(1, Infinity)
a == nan
b == 1.000000
Test failure: NaN == pow(1, -Infinity)
a == nan
b == 1.000000
* 9. +-1 ** +-INF is NAN
Test failure: Infinity == pow(+0.0, -1.1)
a == inf
b == -inf
Test failure: Infinity == pow(+0.0, -2)
a == inf
b == -inf
* 12. +0 ** (-anything except 0, NAN) is +INF
Test failure: Infinity == pow(-0.0, -3.1)
a == inf
b == -inf
Test failure: Infinity == pow(-0.0, -2)
a == inf
b == -inf
Test failure: +Infinity == pow(-0.0, -0.5)
a == inf
b == -inf
Test failure: +Infinity == pow(-0.0, -0.6)
a == inf
b == -inf
* 13. -0 ** (-anything except 0, NAN, odd integer) is +INF
>How-To-Repeat:
Compile and run this program, and watch it fail 8 of our tests
on netbsd-6 and netbsd-7.
(The meat of the code is (c) Google with a 3-clause BSD-like
license, but this is a translation into C by myself.)
------------------------------ snip
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define Infinity INFINITY
#define NaN NAN
#define assertEquals(a, b) \
do { \
if ((a) != (b)) { \
char s1[64], s2[64]; \
sprintf(s1, "%f", (double)(a)); \
sprintf(s2, "%f", (double)(b)); \
if ((strcmp(s1, "nan") == 0) || \
(strcmp(s2, "nan") == 0)) { \
if (strcmp(s1, s2) != 0) { \
printf("Test failure: %s == %s\n", #a, #b); \
printf(" a == %s\n", s1); \
printf(" b == %s\n", s2); \
failures++; \
} else { \
printf("Test OK : %s == %s\n", #a, #b); \
ok++; \
} \
} else { \
printf("Test failure: %s == %s\n", #a, #b); \
printf(" a == %f\n", (double)(a)); \
printf(" b == %f\n", (double)(b)); \
failures++; \
} \
} else { \
printf("Test OK : %s == %s\n", #a, #b); \
ok++; \
} \
} while(0)
int
main(int argc, char **argv)
{
int failures = 0;
int ok = 0;
// These two need to work for the tests to work
// Verify the "nan" dance in the assertEquals macro...
assertEquals(NaN, NaN);
assertEquals(Infinity, Infinity);
// Spec tests
assertEquals(NaN, pow(2, NaN));
assertEquals(NaN, pow(+0.0, NaN));
assertEquals(NaN, pow(-0.0, NaN));
assertEquals(NaN, pow(Infinity, NaN));
assertEquals(NaN, pow(-Infinity, NaN));
assertEquals(1, pow(NaN, +0.0));
assertEquals(1, pow(NaN, -0.0));
assertEquals(NaN, pow(NaN, NaN));
assertEquals(NaN, pow(NaN, 2.2));
assertEquals(NaN, pow(NaN, 1));
assertEquals(NaN, pow(NaN, -1));
assertEquals(NaN, pow(NaN, -2.2));
assertEquals(NaN, pow(NaN, Infinity));
assertEquals(NaN, pow(NaN, -Infinity));
assertEquals(Infinity, pow(1.1, Infinity));
assertEquals(Infinity, pow(-1.1, Infinity));
assertEquals(Infinity, pow(2, Infinity));
assertEquals(Infinity, pow(-2, Infinity));
// Because +0 == -0, we need to compare 1/{+,-}0 to {+,-}Infinity
assertEquals(+Infinity, 1/pow(1.1, -Infinity));
assertEquals(+Infinity, 1/pow(-1.1, -Infinity));
assertEquals(+Infinity, 1/pow(2, -Infinity));
assertEquals(+Infinity, 1/pow(-2, -Infinity));
assertEquals(NaN, pow(1, Infinity));
assertEquals(NaN, pow(1, -Infinity));
assertEquals(NaN, pow(-1, Infinity));
assertEquals(NaN, pow(-1, -Infinity));
assertEquals(+0.0, pow(0.1, Infinity));
assertEquals(+0.0, pow(-0.1, Infinity));
assertEquals(+0.0, pow(0.999, Infinity));
assertEquals(+0.0, pow(-0.999, Infinity));
assertEquals(Infinity, pow(0.1, -Infinity));
assertEquals(Infinity, pow(-0.1, -Infinity));
assertEquals(Infinity, pow(0.999, -Infinity));
assertEquals(Infinity, pow(-0.999, -Infinity));
assertEquals(Infinity, pow(Infinity, 0.1));
assertEquals(Infinity, pow(Infinity, 2));
assertEquals(+Infinity, 1/pow(Infinity, -0.1));
assertEquals(+Infinity, 1/pow(Infinity, -2));
assertEquals(-Infinity, pow(-Infinity, 3));
assertEquals(-Infinity, pow(-Infinity, 13));
assertEquals(Infinity, pow(-Infinity, 3.1));
assertEquals(Infinity, pow(-Infinity, 2));
assertEquals(-Infinity, 1/pow(-Infinity, -3));
assertEquals(-Infinity, 1/pow(-Infinity, -13));
assertEquals(+Infinity, 1/pow(-Infinity, -3.1));
assertEquals(+Infinity, 1/pow(-Infinity, -2));
assertEquals(+Infinity, 1/pow(+0.0, 1.1));
assertEquals(+Infinity, 1/pow(+0.0, 2));
assertEquals(Infinity, pow(+0.0, -1.1));
assertEquals(Infinity, pow(+0.0, -2));
assertEquals(-Infinity, 1/pow(-0.0, 3));
assertEquals(-Infinity, 1/pow(-0.0, 13));
assertEquals(+Infinity, 1/pow(-0.0, 3.1));
assertEquals(+Infinity, 1/pow(-0.0, 2));
assertEquals(-Infinity, pow(-0.0, -3));
assertEquals(-Infinity, pow(-0.0, -13));
assertEquals(Infinity, pow(-0.0, -3.1));
assertEquals(Infinity, pow(-0.0, -2));
assertEquals(NaN, pow(-0.00001, 1.1));
assertEquals(NaN, pow(-0.00001, -1.1));
assertEquals(NaN, pow(-1.1, 1.1));
assertEquals(NaN, pow(-1.1, -1.1));
assertEquals(NaN, pow(-2, 1.1));
assertEquals(NaN, pow(-2, -1.1));
assertEquals(NaN, pow(-1000, 1.1));
assertEquals(NaN, pow(-1000, -1.1));
assertEquals(+Infinity, 1/pow(-0.0, 0.5));
assertEquals(+Infinity, 1/pow(-0.0, 0.6));
assertEquals(-Infinity, 1/pow(-0.0, 1));
assertEquals(-Infinity, 1/pow(-0.0, 10000000001.0));
assertEquals(+Infinity, pow(-0.0, -0.5));
assertEquals(+Infinity, pow(-0.0, -0.6));
assertEquals(-Infinity, pow(-0.0, -1));
assertEquals(-Infinity, pow(-0.0, -10000000001.0));
assertEquals(4, pow(16, 0.5));
assertEquals(NaN, pow(-16, 0.5));
assertEquals(0.25, pow(16, -0.5));
assertEquals(NaN, pow(-16, -0.5));
// Test detecting and converting integer value as double.
assertEquals(8, pow(2, sqrt(9)));
// Tests from Mozilla 15.8.2.13.
assertEquals(Infinity, pow(-Infinity, Infinity));
assertEquals(0, pow(-Infinity, -Infinity));
assertEquals(1, pow(0, 0));
assertEquals(0, pow(0, Infinity));
assertEquals(NaN, pow(NaN, 0.5));
assertEquals(NaN, pow(NaN, -0.5));
// Tests from Sputnik S8.5_A13_T1.
assertEquals(
(1*((pow(2,53))-1)*(pow(2,-1074))),
4.4501477170144023e-308);
assertEquals(
(1*(pow(2,52))*(pow(2,-1074))),
2.2250738585072014e-308);
assertEquals(
(-1*(pow(2,52))*(pow(2,-1074))),
-2.2250738585072014e-308);
printf("\n");
printf("Failed tests: %d\n", failures);
printf("Successful tests: %d\n", ok);
exit(failures != 0);
}
------------------------------ snip
>Fix:
Sorry, no fix proposed.
The pcc maintainer tested this with pcc on netbsd-6, and
interestingly these two tests succeed with pcc but fail with
gcc (with the same libm!):
Test OK : NaN == pow(1, Infinity)
Test OK : NaN == pow(1, -Infinity)
Also of interest is that there's been a regression between
netbsd-5 and netbsd-6 on the number of test failures: these
two succeed on netbsd-5:
Test OK : Infinity == pow(-0.0, -2)
Test OK : Infinity == pow(+0.0, -2)
but fail on netbsd-6 or netbsd-7:
Test failure: Infinity == pow(+0.0, -2)
a == inf
b == -inf
Test failure: Infinity == pow(-0.0, -2)
a == inf
b == -inf
This is seen both for macppc and amd64.
>Audit-Trail:
From: Havard Eidnes <he@NetBSD.org>
To: gnats-bugs@NetBSD.org
Cc:
Subject: Re: lib/49240: Some corner cases for pow() fail to work correctly
Date: Tue, 30 Sep 2014 14:20:55 +0200 (CEST)
Hi,
I've dug some more, and it looks like the following four tests:
assertEquals(NaN, pow(1, Infinity));
assertEquals(NaN, pow(1, -Infinity));
assertEquals(NaN, pow(-1, Infinity));
assertEquals(NaN, pow(-1, -Infinity));
are in fact erroneous, at least reportedly if one is to follow
C99, and some (FreeBSD commit messages) claim the revised IEEE
754 of 2008. These are now all defined to give +1.0 as result.
I'll follow that up with Google's V8 project.
It seems our libm is based on fdlibm, but we have no entry in
doc/3RDPARTY documentind that, so "which version" is unknown to
me so far.
Interestingly, the test when run on FreeBSD 10/amd64 failed only
the four above tests, with results which were according to C99,
apparently.
Some pointers:
FreeBSD diff for the C99 update:
http://svnweb.freebsd.org/base/head/lib/msun/src/e_pow.c?r1=176276&r2=268597
A pointer to fdlibm 5.3 and an IEEE test suite from UCB (I've not
looked at that yet -- will do time permitting) can be found at
http://www.validlab.com/software/
There appears to be another bug in fdlibm 5.3's pow function,
talked about here:
https://blogs.oracle.com/darcy/entry/finding_a_bug_in_fdlibm
but unfortunately the URL pointer to the fix is dead. That
document also says that the 4 above tests should give +1.0 in C99.
A minor tweak and copying of our math-private.h makes the FreeBSD
version build outside of the NetBSD source tree, and by using the
result and correcting the test cases for the above 4, I now have
a "0 fails" testrun on NetBSD.
I'll work out a minimal diff for our e_pow.c.
Regards,
- Havard
From: Havard Eidnes <he@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/49240: Some corner cases for pow() fail to work correctly
Date: Tue, 30 Sep 2014 17:23:27 +0200 (CEST)
> I've dug some more, and it looks like the following four tests:
>
> assertEquals(NaN, pow(1, Infinity));
> assertEquals(NaN, pow(1, -Infinity));
> assertEquals(NaN, pow(-1, Infinity));
> assertEquals(NaN, pow(-1, -Infinity));
>
> are in fact erroneous, at least reportedly if one is to follow
> C99, and some (FreeBSD commit messages) claim the revised IEEE
> 754 of 2008. These are now all defined to give +1.0 as result.
> I'll follow that up with Google's V8 project.
The feedback I received there is that ECMAscript is specific
about mandating the old IEEE 754 rules in these 4 test cases.
I'm currently testing a wrapper patch for this issue in v8, so
that C99 systems also can do this "correctly".
Regards,
- Havard
From: Havard Eidnes <he@NetBSD.org>
To: gnats-bugs@NetBSD.org
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 */
From: christos@zoulas.com (Christos Zoulas)
To: gnats-bugs@NetBSD.org, lib-bug-people@netbsd.org,
gnats-admin@netbsd.org, netbsd-bugs@netbsd.org, he@NetBSD.org
Cc:
Subject: Re: lib/49240: Some corner cases for pow() fail to work correctly
Date: Wed, 1 Oct 2014 08:39:16 -0400
On Oct 1, 10:30am, he@NetBSD.org (Havard Eidnes) wrote:
-- Subject: Re: lib/49240: Some corner cases for pow() fail to work correctly
| The changes rolled into this are:
|
| * Change yy1 to y1; no need for yy1.
Please change that back. Compilers warn:
/usr/include/math.h:double y1(double);
christos
From: Havard Eidnes <he@NetBSD.org>
To: gnats-bugs@NetBSD.org
Cc:
Subject: Re: lib/49240: Some corner cases for pow() fail to work correctly
Date: Sat, 04 Oct 2014 16:24:18 +0200 (CEST)
Hmm.
It appears that we build a "multi-standard fdlibm as libm", and that
we by default implement _POSIX_MODE (ref. src/lib/lim/Makefile).
So ... the test program, if it should test the IEEE compliance for the
corner cases, needs to tell libm to do the IEEE operations instead of
the POSIX operations via
_LIB_VERSION =3D _IEEE_;
(is that the officially sanctioned method, BTW?)
and the only failures flagged by the test program I posted earlier
which our original libm then fail with is
Test failure: 1.0 =3D=3D pow(-1, Infinity)
a =3D=3D 1.000000
b =3D=3D nan
Test failure: 1.0 =3D=3D pow(-1, -Infinity)
a =3D=3D 1.000000
b =3D=3D nan
which correspond to this change:
* Add a case for -1**+-Inf, since C99 and IEEE 754 revised 2008(?)
reportedly state that this should be 1
Further, it looks like the reason for the "yy1" variable name is that
a use of "y1" appears to mask a global "y1", at least on netbsd-6.
Test cases to justify the other changes should be found.
Regards,
- H=E5vard
(Contact us)
$NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.