NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/49240: Some corner cases for pow() fail to work correctly



>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
>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@localhost:/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.



Home | Main Index | Thread Index | Old Index