Commit 5992a3b8 authored by Morten Welinder's avatar Morten Welinder Committed by Morten Welinder

If all else fails, try to Newton seek from left edge of interval.

2005-06-27  Morten Welinder  <terra@gnome.org>

	* functions.c (gnumeric_irr): If all else fails, try to Newton
	seek from left edge of interval.
	(irr_npv, irr_npv_df): Don't be smart.  Fixes Debian 315625.
parent 31cf264a
......@@ -15,6 +15,7 @@ Morten:
* Fix stf export quoting problem. [#308109]
* Fix stf export format problem.
* Prevent sheets from being renamed to "".
* Fix IRR. [Debian #315625]
--------------------------------------------------------------------------
Gnumeric 1.5.2
......
2005-06-27 Morten Welinder <terra@gnome.org>
* functions.c (gnumeric_irr): If all else fails, try to Newton
seek from left edge of interval.
(irr_npv, irr_npv_df): Don't be smart. Fixes Debian #315625.
2005-06-13 Jody Goldberg <jody@gnome.org>
* Release 1.5.2
......
......@@ -1564,43 +1564,41 @@ typedef struct {
static GoalSeekStatus
irr_npv (gnm_float rate, gnm_float *y, void *user_data)
{
gnumeric_irr_t *p = user_data;
gnm_float *values, sum;
int i, n;
values = p->values;
n = p->n;
sum = 0;
for (i = 0; i < n; i++)
sum += values[i] * pow1p (rate, n - i);
const gnumeric_irr_t *p = user_data;
const gnm_float *values = p->values;
int n = p->n;
gnm_float sum = 0;
gnm_float f = 1;
gnm_float ff = 1 / (rate + 1);
int i;
/*
* I changed the formula above by multiplying all terms by (1+r)^n.
* Since we're looking for zeros, that should not matter. It does
* make the derivative below simpler, though. -- MW.
*/
for (i = 0; i < n; i++) {
sum += values[i] * f;
f *= ff;
}
*y = sum;
return GOAL_SEEK_OK;
return gnm_finite (sum) ? GOAL_SEEK_OK : GOAL_SEEK_ERROR;
}
static GoalSeekStatus
irr_npv_df (gnm_float rate, gnm_float *y, void *user_data)
{
gnumeric_irr_t *p = user_data;
gnm_float *values, sum;
int i, n;
values = p->values;
n = p->n;
sum = 0;
for (i = 0; i < n - 1; i++)
sum += values[i] * (n - i) * pow1p (rate, n - i - 1);
const gnumeric_irr_t *p = user_data;
const gnm_float *values = p->values;
int n = p->n;
gnm_float sum = 0;
gnm_float f = 1;
gnm_float ff = 1 / (rate + 1);
int i;
for (i = 1; i < n; i++) {
sum += values[i] * (-i) * f;
f *= ff;
}
*y = sum;
return GOAL_SEEK_OK;
return gnm_finite (sum) ? GOAL_SEEK_OK : GOAL_SEEK_ERROR;
}
static GnmValue *
......@@ -1625,21 +1623,31 @@ gnumeric_irr (FunctionEvalInfo *ei, GnmValue const * const *argv)
goal_seek_initialize (&data);
data.xmin = MAX (data.xmin,
-gnm_pow (DBL_MAX / 1e10, 1.0 / p.n) + 1);
data.xmin = -1;
data.xmax = MIN (data.xmax,
gnm_pow (DBL_MAX / 1e10, 1.0 / p.n) - 1);
status = goal_seek_newton (&irr_npv, &irr_npv_df, &data, &p, rate0);
if (status != GOAL_SEEK_OK) {
int factor;
int i;
gnm_float s;
/* Lay a net of test points around the guess. */
for (factor = 2; !(data.havexneg && data.havexpos) &&
factor < 100; factor *= 2) {
goal_seek_point (&irr_npv, &data, &p, rate0 * factor);
goal_seek_point (&irr_npv, &data, &p, rate0 / factor);
for (i = 0, s = 2; !(data.havexneg && data.havexpos) && i < 10; i++, s *= 2) {
goal_seek_point (&irr_npv, &data, &p, rate0 * s);
goal_seek_point (&irr_npv, &data, &p, rate0 / s);
}
/*
* If the root is negative and the guess is positive is
* is possible to get thrown out to the left of -100%
* by the Newton method.
*/
if (!(data.havexneg && data.havexpos))
goal_seek_newton (&irr_npv, &irr_npv_df, &data, &p, -0.99);
if (!(data.havexneg && data.havexpos))
goal_seek_point (&irr_npv, &data, &p, 1 - GNM_EPSILON);
/* Pray we got both sides of the root. */
status = goal_seek_bisection (&irr_npv, &data, &p);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment