#define _GNU_SOURCE
#include <fenv.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef __STDC_IEC_559__
#define HAVE_HW_DOUBLE
#endif
#ifdef __sh__
#if !(defined(__SH4__) || defined(__SH4_SINGLE__) || defined(__SH4_SINGLE_ONLY__))
#define GDB_SIMULATOR
#endif
#endif
#ifdef HAVE_HW_DOUBLE
typedef double test_t;
#define test_sqrt(x) sqrt(x)
#define test_pow(x, y) pow(x, y)
#define test_remainder(x, y) remainder(x, y)
#define test_log(x) log(x)
#define tiny_val 1e-300
#define huge_val 1e300;
#else
typedef float test_t;
#define test_sqrt(x) sqrtf(x)
#define test_pow(x, y) powf(x, y)
#define test_remainder(x, y) remainderf(x, y)
#define test_log(x) logf(x)
#define tiny_val 1e-30f
#define huge_val 1e30f
#endif
volatile test_t one = 1.0;
volatile test_t zero = 0.0;
volatile test_t two = 2.0;
volatile test_t huge = huge_val;
volatile test_t tiny = tiny_val;
volatile test_t inf = (test_t)INFINITY;
#define lowbit(x) ((x) & -(x))
#define ispoweroftwo(x) (((x) & ((x) - 1)) == 0)
#ifdef FE_DIVBYZERO
#define my_divbyzero FE_DIVBYZERO
#else
#define my_divbyzero 0
#endif
#ifdef FE_OVERFLOW
#define my_overflow FE_OVERFLOW
#else
#define my_overflow 0
#endif
#ifdef FE_UNDERFLOW
#define my_underflow FE_UNDERFLOW
#else
#define my_underflow 0
#endif
#ifdef FE_INEXACT
#define my_inexact FE_INEXACT
#else
#define my_inexact 0
#endif
#ifdef FE_INVALID
#define my_invalid FE_INVALID
#else
#define my_invalid 0
#endif
static const char *
e_to_str(int e)
{
if (e == 0)
return "NONE";
#ifdef FE_DIVBYZERO
if (e == FE_DIVBYZERO)
return "FE_DIVBYZERO";
#endif
#ifdef FE_OVERFLOW
if (e == FE_OVERFLOW)
return "FE_OVERFLOW";
#endif
#ifdef FE_UNDERFLOW
if (e == FE_UNDERFLOW)
return "FE_UNDERFLOW";
#endif
#ifdef FE_INEXACT
if (e == FE_INEXACT)
return "FE_INEXACT";
#endif
#ifdef FE_INVALID
if (e == FE_INVALID)
return "FE_INVALID";
#endif
#if defined(FE_OVERFLOW) && defined(FE_INEXACT)
if (e == (FE_OVERFLOW | FE_INEXACT))
return "FE_OVERFLOW|FE_INEXACT";
#endif
#if defined(FE_UNDERFLOW) && defined(FE_INEXACT)
if (e == (FE_UNDERFLOW | FE_INEXACT))
return "FE_UNDERFLOW|FE_INEXACT";
#endif
static char buf[50];
sprintf(buf, "Invalid 0x%x", e);
return buf;
}
#define s(e) #e
static int
report(char *expr, test_t v, int e, int exception, int oexception)
{
e &= (my_inexact | my_divbyzero | my_underflow | my_overflow | my_invalid);
printf("%-20.20s: ", expr);
printf("%8g ", (double)v);
printf("expect %s", e_to_str(exception));
if (oexception)
printf(" or %s", e_to_str(oexception));
printf(" got %s\n", e_to_str(e));
if (e == (exception) || (oexception && e == (oexception))) {
return 0;
}
printf("\tFAILURE\n");
return 1;
}
#define TEST_CASE2(expr, exception, oexception) \
do { \
int e; \
volatile test_t v; \
feclearexcept(FE_ALL_EXCEPT); \
v = expr; \
e = fetestexcept(FE_ALL_EXCEPT); \
result += report(s(expr), v, e, exception, oexception); \
} while (0)
#define TEST_CASE(expr, exception) \
do { \
if ((exception & (my_overflow | my_underflow)) && my_inexact != 0) \
TEST_CASE2(expr, exception, exception | my_inexact); \
else \
TEST_CASE2(expr, exception, 0); \
} while (0)
static const struct {
const char *name;
int value;
} excepts[] = {
{ .name = "None", .value = 0 },
#if FE_DIVBYZERO
{ .name = "Divide by zero", .value = FE_DIVBYZERO },
#endif
#if FE_OVERFLOW
{ .name = "Overflow", .value = FE_OVERFLOW },
#endif
#if FE_UNDERFLOW
{ .name = "Underflow", .value = FE_UNDERFLOW },
#endif
#if FE_INVALID
{ .name = "Invalid", .value = FE_INVALID },
#endif
};
#define NUM_EXCEPTS (sizeof(excepts) / sizeof(excepts[0]))
int
main(void)
{
int result = 0;
int ret;
unsigned i;
#ifdef GDB_SIMULATOR
printf("GDB simulator doesn't support fenv. Skipping\n");
return 77;
#endif
(void)report;
(void)e_to_str;
if (math_errhandling & MATH_ERREXCEPT) {
#if FE_DIVBYZERO
TEST_CASE(one / zero, FE_DIVBYZERO);
TEST_CASE(test_log(zero), FE_DIVBYZERO);
#endif
#if FE_OVERFLOW
TEST_CASE(huge * huge, FE_OVERFLOW);
TEST_CASE(test_pow(two, huge), FE_OVERFLOW);
#endif
#if FE_UNDERFLOW
TEST_CASE(tiny * tiny, FE_UNDERFLOW);
TEST_CASE(test_pow(two, -huge), FE_UNDERFLOW);
#endif
#if FE_INVALID
TEST_CASE(zero * inf, FE_INVALID);
TEST_CASE(inf * zero, FE_INVALID);
TEST_CASE(inf + -inf, FE_INVALID);
TEST_CASE(inf - inf, FE_INVALID);
TEST_CASE(zero / zero, FE_INVALID);
TEST_CASE(inf / inf, FE_INVALID);
TEST_CASE(test_remainder(one, zero), FE_INVALID);
TEST_CASE(test_remainder(inf, two), FE_INVALID);
TEST_CASE(test_sqrt(-two), FE_INVALID);
#endif
}
feclearexcept(FE_ALL_EXCEPT);
for (i = 0; i < NUM_EXCEPTS; i++) {
ret = feenableexcept(excepts[i].value);
if (ret == 0) {
ret = fedisableexcept(excepts[i].value);
if (ret != excepts[i].value) {
printf("enable %s worked, disabled returned %d\n", excepts[i].name, ret);
result = 1;
}
} else {
if (excepts[i].value == 0) {
printf("enable %s returned %d", excepts[i].name, ret);
result = 1;
}
}
}
return result;
}