summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2013-06-17 21:17:40 +0200
committerNoel Power <noel.power@suse.com>2013-06-18 10:23:15 +0000
commit2b81b9967f8269e4fe7cef8e6ede490ae3b66d94 (patch)
treebb955341e5310701d6e650604ff2e05725e209c7
parent0aece9e50bd06c16cf15b4cb7d45dd1f98d1800c (diff)
resolved fdo#65082 RATE function should not find roots <= -1
Limit RATE to roots > -1.0, which is what also Excel and now Gnumeric do. If the Newton goal seek fails for the default guess value 0.1 try other values. This now also calculates the few remaining error cases of i#15090 attachment https://issues.apache.org/ooo/attachment.cgi?id=6528 correctly. Change-Id: Ic62cc807626b4715035c3076f58f303b9020db5a (cherry picked from commit 9ee7be4efb494351c4be096ffa04cdbd85cdc3d4) Reviewed-on: https://gerrit.libreoffice.org/4328 Reviewed-by: Noel Power <noel.power@suse.com> Tested-by: Noel Power <noel.power@suse.com>
-rw-r--r--sc/source/core/tool/interpr2.cxx59
1 files changed, 46 insertions, 13 deletions
diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx
index dc843591e9ba..29845cd728e1 100644
--- a/sc/source/core/tool/interpr2.cxx
+++ b/sc/source/core/tool/interpr2.cxx
@@ -1430,16 +1430,19 @@ bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv,
fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope
else
fXnew = fX - fTerm / fTermDerivation;
- nCount++;
- // more accuracy not possible in oscillating cases
- bFound = (fabs(fXnew - fX) < SCdEpsilon);
- fX = fXnew;
+ nCount++;
+ // more accuracy not possible in oscillating cases
+ bFound = (fabs(fXnew - fX) < SCdEpsilon);
+ fX = fXnew;
}
}
// Gnumeric returns roots < -1, Excel gives an error in that cases,
// ODFF says nothing about it. Enable the statement, if you want Excel's
- // behavior
+ // behavior.
//bValid =(fX >=-1.0);
+ // Update 2013-06-17: Gnumeric (v1.12.2) doesn't return roots <= -1
+ // anymore.
+ bValid = (fX > -1.0);
}
else
{ // Nper is not an integer value.
@@ -1466,11 +1469,11 @@ bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv,
fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope
else
fXnew = fX - fTerm / fTermDerivation;
- nCount++;
- // more accuracy not possible in oscillating cases
- bFound = (fabs(fXnew - fX) < SCdEpsilon);
- fX = fXnew;
- bValid = (fX >= -1.0); // otherwise pow(1.0+fX,fNper) will fail
+ nCount++;
+ // more accuracy not possible in oscillating cases
+ bFound = (fabs(fXnew - fX) < SCdEpsilon);
+ fX = fXnew;
+ bValid = (fX >= -1.0); // otherwise pow(1.0+fX,fNper) will fail
}
}
}
@@ -1484,14 +1487,18 @@ void ScInterpreter::ScZins()
RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZins" );
double fPv, fPayment, fNper;
// defaults for missing arguments, see ODFF spec
- double fFv = 0, fPayType = 0, fGuess = 0.1;
+ double fFv = 0, fPayType = 0, fGuess = 0.1, fOrigGuess = 0.1;
bool bValid = true;
+ bool bDefaultGuess = true;
nFuncFmtType = NUMBERFORMAT_PERCENT;
sal_uInt8 nParamCount = GetByte();
if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
return;
if (nParamCount == 6)
- fGuess = GetDouble();
+ {
+ fOrigGuess = fGuess = GetDouble();
+ bDefaultGuess = false;
+ }
if (nParamCount >= 5)
fPayType = GetDouble();
if (nParamCount >= 4)
@@ -1509,7 +1516,33 @@ void ScInterpreter::ScZins()
//if (fPayType != 0.0) fPayType = 1.0;
bValid = RateIteration(fNper, fPayment, fPv, fFv, fPayType, fGuess);
if (!bValid)
- SetError(errNoConvergence);
+ {
+ /* TODO: try also for specified guess values, not only default? As is,
+ * a specified 0.1 guess may be error result but a default 0.1 guess
+ * may succeed. On the other hand, using a different guess value than
+ * the specified one may not be desired, even if that didn't match. */
+ if (bDefaultGuess)
+ {
+ /* TODO: this is rather ugly, instead of looping over different
+ * guess values and doing a Newton goal seek for each we could
+ * first insert the values into the RATE equation to obtain a set
+ * of y values and then do a bisecting goal seek, possibly using
+ * different algorithms. */
+ double fX = fOrigGuess;
+ for (int nStep = 2; nStep <= 10 && !bValid; ++nStep)
+ {
+ fGuess = fX * nStep;
+ bValid = RateIteration( fNper, fPayment, fPv, fFv, fPayType, fGuess);
+ if (!bValid)
+ {
+ fGuess = fX / nStep;
+ bValid = RateIteration( fNper, fPayment, fPv, fFv, fPayType, fGuess);
+ }
+ }
+ }
+ if (!bValid)
+ SetError(errNoConvergence);
+ }
PushDouble(fGuess);
}