mirror of https://github.com/Gnucash/gnucash
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
3.3 KiB
165 lines
3.3 KiB
/*
|
|
* go-math.c: Mathematical functions.
|
|
*
|
|
* Authors:
|
|
* Morten Welinder <terra@gnome.org>
|
|
*/
|
|
|
|
#include <goffice/goffice-config.h>
|
|
#include "go-math.h"
|
|
#include <glib/gmessages.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <locale.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
|
|
#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);
|
|
}
|