blob: 2fa865551378a3a532311f760aa8af902dcc3a7e [file] [edit]
/*
* Copyright 2010 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* Test parts of math.h and floating point ops for compliance against ieee754.
*/
#include <math.h>
#include <stdio.h>
#include <errno.h>
/* #include <fenv.h>
* TODO(jvoung) Newlib doesn't have this on any machine, except the Cell SPU.
* Would like to test the exception flagging (see below).
*/
#define CHECK_EQ_DOUBLE(a, b) \
(a == b) ? 0 : (printf("ERROR %f != %f at %d\n", a, b, __LINE__), 1)
#define CHECK_NAN(_x_) \
isnan(_x_) ? 0 : (printf("ERROR %s(%f) != NaN at %d\n", \
#_x_, (double)_x_, __LINE__), 1)
#define CHECK_INF(a) \
isinf(a) ? 0 : (printf("ERROR %f != Inf at %d\n", (double)a, __LINE__), 1)
#define ASSERT_TRUE(b) \
(b) ? 0 : (printf("ASSERT true failed! %d at %d\n", b, __LINE__), 1)
#define ASSERT_FALSE(b) \
(0 == (b)) ? 0 : (printf("ASSERT false failed! %d at %d\n", b, __LINE__), 1)
/************************************************************/
int test_constants(void) {
int errs = 0;
/* Attempt to prevent constant folding */
volatile double x = 1.0;
volatile double y = 0.0;
errs += CHECK_NAN(NAN);
printf("Print out of NaN: %f\n", NAN);
errs += CHECK_INF(INFINITY);
printf("Print out of Infinity: %f\n", INFINITY);
errs += CHECK_INF(x/y);
return errs;
}
int test_compares(void) {
int errs = 0;
/* Attempt to prevent constant folding */
volatile double x;
volatile double y;
printf("Comparing float constants\n");
x = NAN;
errs += ASSERT_TRUE(x != x);
errs += ASSERT_TRUE(isunordered(x, x));
errs += CHECK_NAN(x + 3.0f);
errs += CHECK_NAN(x + x);
errs += CHECK_NAN(x - x);
errs += CHECK_NAN(x / x);
errs += CHECK_NAN(0.0 / x);
errs += CHECK_NAN(0.0 * x);
errs += ASSERT_FALSE(x == x);
x = INFINITY;
errs += ASSERT_TRUE(x == x);
errs += ASSERT_FALSE(x == -x);
errs += ASSERT_TRUE(-x == -x);
errs += ASSERT_TRUE(x + 100.0 == x);
errs += ASSERT_TRUE(x - 100.0 == x);
errs += ASSERT_TRUE(-x - 100.0 == -x);
errs += ASSERT_TRUE(-x + 100.0 == -x);
errs += ASSERT_TRUE(-x < x);
errs += ASSERT_FALSE(-x > x);
y = 0.0;
errs += CHECK_NAN(y * x);
errs += CHECK_NAN(y / y);
errs += CHECK_NAN(x / x);
errs += CHECK_NAN(x - x);
y = NAN;
errs += CHECK_NAN(x * y);
x = INFINITY;
errs += CHECK_INF(x + x);
x = 1.0; y = 0.0;
errs += CHECK_INF(x / y);
x = INFINITY;
errs += ASSERT_FALSE(isfinite(x));
x = NAN;
errs += ASSERT_FALSE(isfinite(x));
return errs;
}
/* Test non-NaN-resulting library calls. */
int test_defined(void) {
int errs = 0;
/*
* Attempt to prevent constant folding and optimization of library
* function bodies (when statically linked).
*/
volatile double x;
volatile double y;
volatile double z;
printf("Checking lib calls that take NaN, etc, but return non-NaN.\n");
x = 0.0; y = 1.0;
errs += CHECK_EQ_DOUBLE(pow(x, x), y);
z = NAN;
errs += CHECK_EQ_DOUBLE(pow(z, x), y);
z = INFINITY;
errs += CHECK_EQ_DOUBLE(pow(z, x), y);
errs += CHECK_INF(sqrt(z));
x = -0.0;
errs += CHECK_EQ_DOUBLE(sqrt(x), x);
x = INFINITY; y = 2.0;
errs += CHECK_INF(pow(x, y));
x = 0.0; y = -INFINITY;
errs += ASSERT_TRUE(log(x) == y);
return errs;
}
/* Test NaN-resulting library calls. */
int test_errs(void) {
int errs = 0;
/*
* Attempt to prevent constant folding and optimization of library
* function bodies (when statically linked).
*/
volatile double x;
volatile double y;
printf("Checking well-defined library errors\n");
x = -3.0; y = 4.4;
errs += CHECK_NAN(pow(x, y));
errs += CHECK_NAN(log(x));
x = -0.001;
errs += CHECK_NAN(sqrt(x));
x = 1.0001;
errs += CHECK_NAN(asin(x));
x = INFINITY;
errs += CHECK_NAN(sin(x));
errs += CHECK_NAN(cos(x));
x = 0.999;
errs += CHECK_NAN(acosh(x));
x = 3.3; y = 0.0;
errs += CHECK_NAN(remainder(x, y));
y = INFINITY;
errs += CHECK_NAN(remainder(y, x));
return errs;
}
/*
TODO(jvoung) Check if exceptions are flagged in status word
(once fenv.h is in newlib, or we move to using glibc)?
test FE_INVALID
test FE_DIVBYZERO
test FE_INEXACT
test FE_OVERFLOW
test FE_UNDERFLOW
E.g., Ordered comparisons of NaN should raise INVALID
(NAN < NAN);
(NAN >= NAN);
(1.0 >= NAN);
as, should all the calls in test_errs()
Some notes:
http://www.kernel.org/doc/man-pages/online/pages/man7/math_error.7.html
https://www.securecoding.cert.org/confluence/display/seccode/ \
FLP03-C.+Detect+and+handle+floating+point+errors
*/
/* Check exceptions communicated by the "old" errno mechanism */
#define CHECK_TRIPPED_ERRNO(expr) \
(errno = 0, \
(void)expr, \
(0 != errno ? 0 : (printf("%s - errno %d not set at %d\n", \
#expr, errno, __LINE__), 1)))
int test_exception(void) {
int errs = 0;
/*
* Attempt to prevent constant folding and optimization of library
* function bodies (when statically linked).
*/
volatile double x;
volatile double y;
printf("Checking that exceptional lib calls set errno\n");
x = -3.0; y = 4.4;
errs += CHECK_TRIPPED_ERRNO(pow(x, y));
errs += CHECK_TRIPPED_ERRNO(log(x));
x = -0.001;
errs += CHECK_TRIPPED_ERRNO(sqrt(x));
x = 1.0001;
errs += CHECK_TRIPPED_ERRNO(asin(x));
/* Some versions of libc don't set errno =(
* http://sourceware.org/bugzilla/show_bug.cgi?id=6780
* http://sourceware.org/bugzilla/show_bug.cgi?id=6781
x = INFINITY;
errs += CHECK_TRIPPED_ERRNO(sin(x));
errs += CHECK_TRIPPED_ERRNO(cos(x));
*/
x = 0.999;
errs += CHECK_TRIPPED_ERRNO(acosh(x));
x = 3.3; y = 0.0;
errs += CHECK_TRIPPED_ERRNO(remainder(x, y));
/* Some versions of libc don't set errno =(
* http://sourceware.org/bugzilla/show_bug.cgi?id=6783
x = INFINITY; y = 3.3;
errs += CHECK_TRIPPED_ERRNO(remainder(x, y));
*/
return errs;
}
int main(int ac, char* av[]) {
int errs = 0;
errs += test_constants();
errs += test_compares();
errs += test_defined();
errs += test_errs();
#if !defined(NO_ERRNO_CHECK)
errs += test_exception();
#endif
printf("%d errs!\n", errs);
return errs + 55;
}