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
This commit is contained in:
@@ -1433,8 +1433,11 @@ bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv,
|
|||||||
}
|
}
|
||||||
// Gnumeric returns roots < -1, Excel gives an error in that cases,
|
// Gnumeric returns roots < -1, Excel gives an error in that cases,
|
||||||
// ODFF says nothing about it. Enable the statement, if you want Excel's
|
// ODFF says nothing about it. Enable the statement, if you want Excel's
|
||||||
// behavior
|
// behavior.
|
||||||
//bValid =(fX >=-1.0);
|
//bValid =(fX >=-1.0);
|
||||||
|
// Update 2013-06-17: Gnumeric (v1.12.2) doesn't return roots <= -1
|
||||||
|
// anymore.
|
||||||
|
bValid = (fX > -1.0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ // Nper is not an integer value.
|
{ // Nper is not an integer value.
|
||||||
@@ -1479,14 +1482,18 @@ void ScInterpreter::ScZins()
|
|||||||
RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZins" );
|
RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZins" );
|
||||||
double fPv, fPayment, fNper;
|
double fPv, fPayment, fNper;
|
||||||
// defaults for missing arguments, see ODFF spec
|
// 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 bValid = true;
|
||||||
|
bool bDefaultGuess = true;
|
||||||
nFuncFmtType = NUMBERFORMAT_PERCENT;
|
nFuncFmtType = NUMBERFORMAT_PERCENT;
|
||||||
sal_uInt8 nParamCount = GetByte();
|
sal_uInt8 nParamCount = GetByte();
|
||||||
if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
|
if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
|
||||||
return;
|
return;
|
||||||
if (nParamCount == 6)
|
if (nParamCount == 6)
|
||||||
fGuess = GetDouble();
|
{
|
||||||
|
fOrigGuess = fGuess = GetDouble();
|
||||||
|
bDefaultGuess = false;
|
||||||
|
}
|
||||||
if (nParamCount >= 5)
|
if (nParamCount >= 5)
|
||||||
fPayType = GetDouble();
|
fPayType = GetDouble();
|
||||||
if (nParamCount >= 4)
|
if (nParamCount >= 4)
|
||||||
@@ -1503,8 +1510,34 @@ void ScInterpreter::ScZins()
|
|||||||
// ODFF spec is not clear yet, enable statement if you want only 0 and 1
|
// ODFF spec is not clear yet, enable statement if you want only 0 and 1
|
||||||
//if (fPayType != 0.0) fPayType = 1.0;
|
//if (fPayType != 0.0) fPayType = 1.0;
|
||||||
bValid = RateIteration(fNper, fPayment, fPv, fFv, fPayType, fGuess);
|
bValid = RateIteration(fNper, fPayment, fPv, fFv, fPayType, fGuess);
|
||||||
|
if (!bValid)
|
||||||
|
{
|
||||||
|
/* 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)
|
if (!bValid)
|
||||||
SetError(errNoConvergence);
|
SetError(errNoConvergence);
|
||||||
|
}
|
||||||
PushDouble(fGuess);
|
PushDouble(fGuess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user