/* * go-math.c: Mathematical functions. * * Authors: * Morten Welinder */ #include #include "go-math.h" #include #include #include #include #include #include #if defined (HAVE_IEEEFP_H) || defined (HAVE_IEEE754_H) /* Make sure we have this symbol defined, since the existance of either header file implies it. */ #ifndef IEEE_754 #define IEEE_754 #endif #endif #define ML_UNDERFLOW (GO_EPSILON * GO_EPSILON) double go_nan; double go_pinf; double go_ninf; void go_math_init (void) { const char *bug_url = "http://bugzilla.gnome.org/enter_bug.cgi?product=gnumeric"; char *old_locale; double d; #ifdef SIGFPE void (*signal_handler)(int) = (void (*)(int))signal (SIGFPE, SIG_IGN); #endif go_pinf = HUGE_VAL; if (go_pinf > 0 && !go_finite (go_pinf)) goto have_pinf; #if defined(INFINITY) && defined(__STDC_IEC_559__) go_pinf = INFINITY; if (go_pinf > 0 && !go_finite (go_pinf)) goto have_pinf; #endif /* Try sscanf with fixed strings. */ old_locale = setlocale (LC_ALL, "C"); if (sscanf ("Inf", "%lf", &d) != 1 && sscanf ("+Inf", "%lf", &d) != 1) d = 0; setlocale (LC_ALL, old_locale); go_pinf = d; if (go_pinf > 0 && !go_finite (go_pinf)) goto have_pinf; /* Try overflow. */ go_pinf = (HUGE_VAL * HUGE_VAL); if (go_pinf > 0 && !go_finite (go_pinf)) goto have_pinf; g_error ("Failed to generate +Inf. Please report at %s", bug_url); abort (); have_pinf: /* ---------------------------------------- */ go_ninf = -go_pinf; if (go_ninf < 0 && !go_finite (go_ninf)) goto have_ninf; g_error ("Failed to generate -Inf. Please report at %s", bug_url); abort (); have_ninf: /* ---------------------------------------- */ go_nan = go_pinf * 0.0; if (isnan (go_nan)) goto have_nan; /* Try sscanf with fixed strings. */ old_locale = setlocale (LC_ALL, "C"); if (sscanf ("NaN", "%lf", &d) != 1 && sscanf ("NAN", "%lf", &d) != 1 && sscanf ("+NaN", "%lf", &d) != 1 && sscanf ("+NAN", "%lf", &d) != 1) d = 0; setlocale (LC_ALL, old_locale); go_nan = d; if (isnan (go_nan)) goto have_nan; go_nan = go_pinf / go_pinf; if (isnan (go_nan)) goto have_nan; g_error ("Failed to generate NaN. Please report at %s", bug_url); abort (); have_nan: #ifdef SIGFPE signal (SIGFPE, signal_handler); #endif return; } /* * In preparation for truncation, make the value a tiny bit larger (seen * absolutely). This makes ROUND (etc.) behave a little closer to what * people want, even if it is a bit bogus. */ double go_add_epsilon (double x) { if (!go_finite (x) || x == 0) return x; else { int exp; double mant = frexp (fabs (x), &exp); double absres = ldexp (mant + DBL_EPSILON, exp); return (x < 0) ? -absres : absres; } } double go_sub_epsilon (double x) { if (!go_finite (x) || x == 0) return x; else { int exp; double mant = frexp (fabs (x), &exp); double absres = ldexp (mant - DBL_EPSILON, exp); return (x < 0) ? -absres : absres; } } double go_fake_floor (double x) { return floor (go_add_epsilon (x)); } double go_fake_ceil (double x) { return ceil (go_sub_epsilon (x)); } double go_fake_trunc (double x) { return (x >= 0) ? go_fake_floor (x) : -go_fake_floor (-x); }