fpspreadsheet: Implement fpn formula calculation of all math functions available in fps. Test cases passed.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3255 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-06-30 13:21:04 +00:00
parent 7b9110ba74
commit c2a5a2dd5d
5 changed files with 693 additions and 33 deletions

View File

@ -64,7 +64,33 @@ function fpsGreaterEqual(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsLess (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsLessEqual (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsNotEqual (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
{ Math }
function fpsABS (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsACOS (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsACOSH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsASIN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsASINH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsATAN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsATANH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsCOS (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsCOSH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsDEGREES (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsEXP (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsINT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsLN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsLOG (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsLOG10 (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsPI (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsRADIANS (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsRAND (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsROUND (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsSIGN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsSIN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsSINH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsSQRT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsTAN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsTANH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
{ Logic }
function fpsAND (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsFALSE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsIF (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
@ -527,7 +553,6 @@ begin
Result := CreateBool(false);
end;
function fpsNotEqual(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
arg1, arg2: TsArgument;
@ -544,6 +569,265 @@ begin
Result := CreateBool(false);
end;
{ Math functions }
function fpsABS(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(abs(data[0]));
end;
function fpsACOS(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if InRange(data[0], -1, +1) then
Result := CreateNumber(arccos(data[0]))
else
Result := CreateError(errOverflow); // #NUM!
end;
end;
function fpsACOSH(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if data[0] >= 1 then
Result := CreateNumber(arccosh(data[0]))
else
Result := CreateError(errOverflow); // #NUM!
end;
end;
function fpsASIN(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if InRange(data[0], -1, +1) then
Result := CreateNumber(arcsin(data[0]))
else
Result := CreateError(errOverflow); // #NUM!
end;
end;
function fpsASINH(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(arcsinh(data[0]));
end;
function fpsATAN(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(arctan(data[0]));
end;
function fpsATANH(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if (data[0] > -1) and (data[0] < +1) then
Result := CreateNumber(arctanh(data[0]))
else
Result := CreateError(errOverflow); // #NUM!
end;
end;
function fpsCOS(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(cos(data[0]));
end;
function fpsCOSH(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(cosh(data[0]));
end;
function fpsDEGREES(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(RadToDeg(data[0]));
end;
function fpsEXP(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(exp(data[0]));
end;
function fpsINT(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(floor(data[0]));
end;
function fpsLN(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if (data[0] > 0) then
Result := CreateNumber(ln(data[0]))
else
Result := CreateError(errOverflow); // #NUM!
end;
end;
function fpsLOG(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
arg_base, arg_number: TsArgument;
data: TFloatArray;
base: Double;
begin
base := 10;
if NumArgs = 2 then begin
arg_base := Args.Pop;
if not arg_base.IsMissing then begin
if arg_base.ArgumentType <> atNumber then begin
Result := CreateError(errWrongType);
exit;
end;
base := arg_base.NumberValue;
end;
end;
if base < 0 then begin
Result := CreateError(errOverflow);
exit;
end;
arg_number := Args.Pop;
if arg_number.ArgumentType <> atNumber then begin
Result := CreateError(errWrongType);
exit;
end;
if arg_number.NumberValue > 0 then
Result := CreateNumber(logn(base, arg_number.NumberValue))
else
Result := CreateError(errOverflow);
end;
function fpsLOG10(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if (data[0] > 0) then
Result := CreateNumber(log10(data[0]))
else
Result := CreateError(errOverflow); // #NUM!
end;
end;
function fpsPI(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
begin
Result := CreateNumber(pi);
end;
function fpsRADIANS(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(degtorad(data[0]))
end;
function fpsRAND(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
begin
Result := CreateNumber(random);
end;
function fpsROUND(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 2, data, Result) then
Result := CreateNumber(RoundTo(data[0], round(data[1])))
end;
function fpsSIGN(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(sign(data[0]))
end;
function fpsSIN(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(sin(data[0]))
end;
function fpsSINH(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(sinh(data[0]))
end;
function fpsSQRT(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if data[0] >= 0.0 then
Result := CreateNumber(sqrt(data[0]))
else
Result := CreateError(errOverflow);
end;
end;
function fpsTAN(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then begin
if frac(data[0] / (pi*0.5)) = 0 then
Result := CreateError(errOverflow)
else
Result := CreateNumber(tan(data[0]))
end;
end;
function fpsTANH(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TFloatArray;
begin
if PopFloatValues(Args, 1, data, Result) then
Result := CreateNumber(tanh(data[0]))
end;
{ Logical functions }
function fpsAND(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TBoolArray;

View File

@ -1123,31 +1123,31 @@ const
(Symbol:'<>'; MinParams:2; MaxParams:2; Func:fpsNotEqual), // fekNotEqual
(Symbol:''; MinParams:1; MaxParams:1; Func:nil), // fekParen
{ math }
(Symbol:'ABS'; MinParams:1; MaxParams:1; Func:nil), // fekABS
(Symbol:'ACOS'; MinParams:1; MaxParams:1; Func:nil), // fekACOS
(Symbol:'ACOSH'; MinParams:1; MaxParams:1; Func:nil), // fekACOSH
(Symbol:'ASIN'; MinParams:1; MaxParams:1; Func:nil), // fekASIN
(Symbol:'ASINH'; MinParams:1; MaxParams:1; Func:nil), // fekASINH
(Symbol:'ATAN'; MinParams:1; MaxParams:1; Func:nil), // fekATAN
(Symbol:'ATANH'; MinParams:1; MaxParams:1; Func:nil), // fekATANH,
(Symbol:'COS'; MinParams:1; MaxParams:1; Func:nil), // fekCOS
(Symbol:'COSH'; MinParams:1; MaxParams:1; Func:nil), // fekCOSH
(Symbol:'DEGREES'; MinParams:1; MaxParams:1; Func:nil), // fekDEGREES
(Symbol:'EXP'; MinParams:1; MaxParams:1; Func:nil), // fekEXP
(Symbol:'INT'; MinParams:1; MaxParams:1; Func:nil), // fekINT
(Symbol:'LN'; MinParams:1; MaxParams:1; Func:nil), // fekLN
(Symbol:'LOG'; MinParams:1; MaxParams:2; Func:nil), // fekLOG,
(Symbol:'LOG10'; MinParams:1; MaxParams:1; Func:nil), // fekLOG10
(Symbol:'PI'; MinParams:0; MaxParams:0; Func:nil), // fekPI
(Symbol:'RADIANS'; MinParams:1; MaxParams:1; Func:nil), // fekRADIANS
(Symbol:'RAND'; MinParams:0; MaxParams:0; Func:nil), // fekRAND
(Symbol:'ROUND'; MinParams:2; MaxParams:2; Func:nil), // fekROUND,
(Symbol:'SIGN'; MinParams:1; MaxParams:1; Func:nil), // fekSIGN
(Symbol:'SIN'; MinParams:1; MaxParams:1; Func:nil), // fekSIN
(Symbol:'SINH'; MinParams:1; MaxParams:1; Func:nil), // fekSINH
(Symbol:'SQRT'; MinParams:1; MaxParams:1; Func:nil), // fekSQRT,
(Symbol:'TAN'; MinParams:1; MaxParams:1; Func:nil), // fekTAN
(Symbol:'TANH'; MinParams:1; MaxParams:1; Func:nil), // fekTANH,
(Symbol:'ABS'; MinParams:1; MaxParams:1; Func:fpsABS), // fekABS
(Symbol:'ACOS'; MinParams:1; MaxParams:1; Func:fpsACOS), // fekACOS
(Symbol:'ACOSH'; MinParams:1; MaxParams:1; Func:fpsACOSH), // fekACOSH
(Symbol:'ASIN'; MinParams:1; MaxParams:1; Func:fpsASIN), // fekASIN
(Symbol:'ASINH'; MinParams:1; MaxParams:1; Func:fpsASINH), // fekASINH
(Symbol:'ATAN'; MinParams:1; MaxParams:1; Func:fpsATAN), // fekATAN
(Symbol:'ATANH'; MinParams:1; MaxParams:1; Func:fpsATANH), // fekATANH,
(Symbol:'COS'; MinParams:1; MaxParams:1; Func:fpsCOS), // fekCOS
(Symbol:'COSH'; MinParams:1; MaxParams:1; Func:fpsCOSH), // fekCOSH
(Symbol:'DEGREES'; MinParams:1; MaxParams:1; Func:fpsDEGREES), // fekDEGREES
(Symbol:'EXP'; MinParams:1; MaxParams:1; Func:fpsEXP), // fekEXP
(Symbol:'INT'; MinParams:1; MaxParams:1; Func:fpsINT), // fekINT
(Symbol:'LN'; MinParams:1; MaxParams:1; Func:fpsLN), // fekLN
(Symbol:'LOG'; MinParams:1; MaxParams:2; Func:fpsLOG), // fekLOG,
(Symbol:'LOG10'; MinParams:1; MaxParams:1; Func:fpsLOG10), // fekLOG10
(Symbol:'PI'; MinParams:0; MaxParams:0; Func:fpsPI), // fekPI
(Symbol:'RADIANS'; MinParams:1; MaxParams:1; Func:fpsRADIANS), // fekRADIANS
(Symbol:'RAND'; MinParams:0; MaxParams:0; Func:fpsRAND), // fekRAND
(Symbol:'ROUND'; MinParams:2; MaxParams:2; Func:fpsROUND), // fekROUND,
(Symbol:'SIGN'; MinParams:1; MaxParams:1; Func:fpsSIGN), // fekSIGN
(Symbol:'SIN'; MinParams:1; MaxParams:1; Func:fpsSIN), // fekSIN
(Symbol:'SINH'; MinParams:1; MaxParams:1; Func:fpsSINH), // fekSINH
(Symbol:'SQRT'; MinParams:1; MaxParams:1; Func:fpsSQRT), // fekSQRT,
(Symbol:'TAN'; MinParams:1; MaxParams:1; Func:fpsTAN), // fekTAN
(Symbol:'TANH'; MinParams:1; MaxParams:1; Func:fpsTANH), // fekTANH,
{ date/time }
(Symbol:'DATE'; MinParams:3; MaxParams:3; Func:nil), // fekDATE
(Symbol:'DATEDIF'; MinParams:3; MaxParams:3; Func:nil), // fekDATEDIF

View File

@ -142,7 +142,7 @@ var
expected: TsArgument;
cell: PCell;
sollValues: array of TsArgument;
a, b: Double;
formula: String;
begin
TempFile := GetTempFileName;
@ -174,6 +174,7 @@ begin
fail('Error in test code. Failed to get named worksheet');
for Row := 0 to MyWorksheet.GetLastRowIndex do begin
formula := MyWorksheet.ReadAsUTF8Text(Row, 0);
cell := MyWorksheet.FindCell(Row, 1);
if (cell = nil) then
fail('Error in test code: Failed to get cell ' + CellNotation(MyWorksheet, Row, 1));
@ -186,22 +187,27 @@ begin
end;
expected := SollValues[row];
CheckEquals(ord(expected.ArgumentType), ord(actual.ArgumentType),
'Test read calculated formula data type mismatch, cell '+CellNotation(MyWorkSheet,Row,1));
'Test read calculated formula data type mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
case actual.ArgumentType of
atBool:
CheckEquals(BoolToStr(expected.BoolValue), BoolToStr(actual.BoolValue),
'Test read calculated formula result mismatch, cell '+CellNotation(MyWorkSheet,Row,1));
'Test read calculated formula result mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
atNumber:
CheckEquals(expected.NumberValue, actual.NumberValue,
'Test read calculated formula result mismatch, cell '+CellNotation(MyWorkSheet,Row,1));
'Test read calculated formula result mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
atString:
CheckEquals(expected.StringValue, actual.StringValue,
'Test read calculated formula result mismatch, cell '+CellNotation(MyWorkSheet,Row,1));
'Test read calculated formula result mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
atError:
CheckEquals(
GetEnumName(TypeInfo(TsErrorValue), ord(expected.ErrorValue)),
GetEnumname(TypeInfo(TsErrorValue), ord(actual.ErrorValue)),
'Test read calculated formula error value mismatch, cell '+CellNotation(MyWorkSheet,Row,1));
'Test read calculated formula error value mismatch, formula ' + formula +
', cell '+CellNotation(MyWorkSheet,Row,1));
end;
end;

View File

@ -47,6 +47,9 @@
<UseExternalDbgSyms Value="True"/>
</Debugging>
</Linking>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
</Item2>
</BuildModes>
@ -156,6 +159,9 @@
<OptimizationLevel Value="0"/>
</Optimizations>
</CodeGeneration>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="6">

View File

@ -321,6 +321,369 @@
SetLength(sollValues, Row+1);
sollValues[Row] := CreateBool(1<>1);
{------------------------------------------------------------------------------}
{ Math }
{------------------------------------------------------------------------------}
// ABS
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=abs(-1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-1,
RPNFunc(fekABS, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(abs(-1));
// ARCCOS - valid result
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=acos(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekACOS, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(arccos(0.1));
// ACOS - error result (arccos is not defined outside the interval [-1..1]
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=acos(-2)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-2,
RPNFunc(fekACOS, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverFlow);
// ARCCOSH - valid result
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=acosh(1.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(1.1,
RPNFunc(fekACOSH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(arccosh(1.1));
// ACOSH - error result (arccos is not defined below 1
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=acosh(0.9)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.9,
RPNFunc(fekACOSH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverFlow);
// ASIN
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=asin(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekASIN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(arcsin(0.1));
// ASIN - error result (arcsin is not defined outside the interval [-1..1]
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=asin(-2)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-2,
RPNFunc(fekASIN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverFlow);
// ARCSINH
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=asinh(1.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(1.1,
RPNFunc(fekASINH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(arcsinh(1.1));
// ATAN
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=atan(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekATAN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(arctan(0.1));
// ATANH - valid result
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=atanh(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekATANH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(arctanh(0.1));
// ATANH - error result (arctan is only defined within ]-1,1[
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=atanh(1.0)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(1.0,
RPNFunc(fekATANH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverFlow);
// COS
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=cos(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekCOS, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(cos(0.1));
// COSH
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=cosh(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekCOSH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(cosh(0.1));
// DEGREES
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=degrees(1.0)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(1.0,
RPNFunc(fekDEGREES, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(radToDeg(1.0));
// EXP
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=exp(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekEXP, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(exp(0.1));
// INT (positive argument)
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=int(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekINT, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(floor(0.1));
// INT (negative argument)
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=int(-0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-0.1,
RPNFunc(fekINT, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(floor(-0.1));
// LN - valid result
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=ln(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekLN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(ln(0.1));
// LN - error due to argument = 0
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=ln(0.0)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.0,
RPNFunc(fekLN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverflow);
// LN - error due to argument < 0
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=ln(-0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-0.1,
RPNFunc(fekLN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverflow);
// LOG10 - valid result
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log10(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekLOG10, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(log10(0.1));
// LOG10 - error due to argument = 0
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log10(0.0)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.0,
RPNFunc(fekLOG10, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverflow);
// LOG10 - error due to argument < 0
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log10(-0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-0.1,
RPNFunc(fekLOG10, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverflow);
// LOG - valid result (2 arguments)
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, 2)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNNumber(2,
RPNFunc(fekLOG, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(logn(2, 0.1));
// LOG - valid result (2 arguments, base missing)
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, )');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNMissingArg(
RPNFunc(fekLOG, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(logn(10, 0.1));
// LOG - valid result (1 argument)
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekLOG, 1, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(logn(10, 0.1));
// LOG - negative base
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, -2)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNNumber(-2,
RPNFunc(fekLOG, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverflow);
// LOG - negative value
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=log(-0.1, 2)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-0.1,
RPNNumber(2.0,
RPNFunc(fekLOG, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverflow);
// PI
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=pi()');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNFunc(fekPI, nil)));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(pi);
// RADIANS
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=radians(60)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(60,
RPNFunc(fekRADIANS, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(degtorad(60));
// RAND
// Test omitted because we cannot enforce getting the same random number back
// when we are reading the file.
// ROUND
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=ROUND(pi(), 2)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNFunc(fekPI,
RPNNumber(2,
RPNFunc(fekROUND, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(RoundTo(pi, 2));
// SIGN
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=sign(-0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-0.1,
RPNFunc(fekSIGN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(sign(-0.1));
// SIN
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=sin(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekSIN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(sin(0.1));
// SINH
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=sinh(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekSINH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(sinh(0.1));
// SQRT - valid result
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=sqrt(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekSQRT, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(sqrt(0.1));
// SQRT - error (negative argument)
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=sqrt(-0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(-0.1,
RPNFunc(fekSQRT, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateError(errOverflow);
// TAN - valid result
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=tan(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekTAN, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(tan(0.1));
// TAN - error (argument = pi/2)
// This test is omitted because it is difficult to exactly hit pi/2.
// TANH
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=tanh(0.1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0.1,
RPNFunc(fekTANH, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(tanh(0.1));
{------------------------------------------------------------------------------}
{ Logical functions }
{------------------------------------------------------------------------------}
// AND of one values (bool)
inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=AND(true)');
@ -490,3 +853,4 @@
SetLength(sollValues, Row+1);
sollValues[Row] := CreateString('A');