diff --git a/components/fpspreadsheet/examples/other/demo_expression_parser.pas b/components/fpspreadsheet/examples/other/demo_expression_parser.pas index 54e2c9ae7..8f3db5e44 100644 --- a/components/fpspreadsheet/examples/other/demo_expression_parser.pas +++ b/components/fpspreadsheet/examples/other/demo_expression_parser.pas @@ -41,7 +41,7 @@ begin //cell := Worksheet.WriteFormula(1, 0, 'Day(Date(2014, 1, 12))'); //cell := Worksheet.WriteFormula(1, 0, 'SUM(1,2,3)'); //cell := Worksheet.WriteFormula(1, 0, 'CELL("address",A1)'); - cell := Worksheet.WriteFormula(1, 0, 'SUM(A1, 1.2, 1.3)'); + cell := Worksheet.WriteFormula(1, 0, 'REPT("Hallo", 3)'); WriteLn('A1: ', worksheet.ReadAsUTF8Text(0, 0)); WriteLn('B1: ', worksheet.ReadAsUTF8Text(0, 1)); diff --git a/components/fpspreadsheet/fpsfunc.pas b/components/fpspreadsheet/fpsfunc.pas index c052c1b06..e510f9e7b 100644 --- a/components/fpspreadsheet/fpsfunc.pas +++ b/components/fpspreadsheet/fpsfunc.pas @@ -16,7 +16,7 @@ procedure RegisterStdBuiltins(AManager : TsBuiltInExpressionManager); implementation uses - Math, lazutf8, DateUtils, xlsconst, fpsUtils; + Math, lazutf8, StrUtils, DateUtils, xlsconst, fpsUtils; {------------------------------------------------------------------------------} @@ -82,6 +82,20 @@ begin Result := ErrorResult(errOverflow); // #NUM! end; +procedure fpsCEILING(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// CEILING( number, significance ) +// returns a number rounded up to a multiple of significance +var + num, sig: TsExprFloat; +begin + num := ArgToFloat(Args[0]); + sig := ArgToFloat(Args[1]); + if sig = 0 then + Result := ErrorResult(errDivideByZero) + else + Result := FloatResult(ceil(num/sig)*sig); +end; + procedure fpsCOS(var Result: TsExpressionResult; const Args: TsExprParameterArray); begin Result := FloatResult(cos(ArgToFloat(Args[0]))); @@ -97,11 +111,77 @@ begin Result := FloatResult(RadToDeg(ArgToFloat(Args[0]))); end; +procedure fpsEVEN(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// EVEN( number ) +// rounds a number up to the nearest even integer. +// If the number is negative, the number is rounded away from zero. +var + x: TsExprFloat; + n: Integer; +begin + if Args[0].ResultType in [rtInteger, rtFloat, rtDateTime, rtCell, rtEmpty] then begin + x := ArgToFloat(Args[0]); + if x > 0 then + begin + n := Trunc(x) + 1; + if odd(n) then inc(n); + end else + if x < 0 then + begin + n := Trunc(x) - 1; + if odd(n) then dec(n); + end else + n := 0; + Result := IntegerResult(n); + end + else + Result := ErrorResult(errWrongType); +end; + procedure fpsEXP(var Result: TsExpressionResult; const Args: TsExprParameterArray); begin Result := FloatResult(exp(ArgToFloat(Args[0]))); end; +procedure fpsFACT(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// FACT( number ) +// returns the factorial of a number. +var + res: TsExprFloat; + i, n: Integer; +begin + if Args[0].ResultType in [rtInteger, rtFloat, rtEmpty, rtDateTime] then + begin + res := 1.0; + n := ArgToInt(Args[0]); + if n < 0 then + Result := ErrorResult(errOverflow) + else + try + for i:=1 to n do + res := res * i; + Result := FloatResult(res); + except on E:Exception do + Result := ErrorResult(errOverflow); + end; + end else + Result := ErrorResult(errWrongType); +end; + +procedure fpsFLOOR(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// FLOOR( number, significance ) +// returns a number rounded down to a multiple of significance +var + num, sig: TsExprFloat; +begin + num := ArgToFloat(Args[0]); + sig := ArgToFloat(Args[1]); + if sig = 0 then + Result := ErrorResult(errDivideByZero) + else + Result := FloatResult(floor(num/sig)*sig); +end; + procedure fpsINT(var Result: TsExpressionResult; const Args: TsExprParameterArray); begin Result := FloatResult(floor(ArgToFloat(Args[0]))); @@ -153,6 +233,46 @@ begin Result := ErrorResult(errOverflow); // #NUM! end; +procedure fpsMOD(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// MOD( number, divisor ) +// Returns the remainder after a number is divided by a divisor. +var + n, m: Integer; +begin + n := ArgToInt(Args[0]); + m := ArgToInt(Args[1]); + if m = 0 then + Result := ErrorResult(errDivideByZero) + else + Result := IntegerResult(n mod m); +end; + +procedure fpsODD(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// ODD( number ) +// rounds a number up to the nearest odd integer. +// If the number is negative, the number is rounded away from zero. +var + x: TsExprFloat; + n: Integer; +begin + if Args[0].ResultType in [rtInteger, rtFloat, rtDateTime, rtCell, rtEmpty] then + begin + x := ArgToFloat(Args[0]); + if x >= 0 then + begin + n := Trunc(x) + 1; + if not odd(n) then inc(n); + end else + begin + n := Trunc(x) - 1; + if not odd(n) then dec(n); + end; + Result := IntegerResult(n); + end + else + Result := ErrorResult(errWrongType); +end; + procedure fpsPI(var Result: TsExpressionResult; const Args: TsExprParameterArray); begin Unused(Args); @@ -554,6 +674,17 @@ begin Result := StringResult(s); end; +procedure fpsEXACT(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// EXACT( text1, text2 ) +// Compares two strings (case-sensitive) and returns TRUE if they are equal +var + s1, s2: String; +begin + s1 := ArgToString(Args[0]); + s2 := ArgToString(Args[1]); + Result := BooleanResult(s1 = s2); +end; + procedure fpsLEFT(var Result: TsExpressionResult; const Args: TsExprParameterArray); // LEFT( text, [number_of_characters] ) // extracts a substring from a string, starting from the left-most character @@ -561,7 +692,7 @@ var s: String; count: Integer; begin - s := Args[0].ResString; + s := ArgToString(Args[0]); if s = '' then Result.ResultType := rtEmpty else @@ -584,7 +715,7 @@ procedure fpsLEN(var Result: TsExpressionResult; const Args: TsExprParameterArra // LEN( text ) // returns the length of the specified string. begin - Result := IntegerResult(UTF8Length(Args[0].ResString)); + Result := IntegerResult(UTF8Length(ArgToString(Args[0]))); end; procedure fpsLOWER(var Result: TsExpressionResult; const Args: TsExprParameterArray); @@ -592,14 +723,14 @@ procedure fpsLOWER(var Result: TsExpressionResult; const Args: TsExprParameterAr // converts all letters in the specified string to lowercase. If there are // characters in the string that are not letters, they are not affected. begin - Result := StringResult(UTF8Lowercase(Args[0].ResString)); + Result := StringResult(UTF8Lowercase(ArgToString(Args[0]))); end; procedure fpsMID(var Result: TsExpressionResult; const Args: TsExprParameterArray); // MID( text, start_position, number_of_characters ) // extracts a substring from a string (starting at any position). begin - Result := StringResult(UTF8Copy(Args[0].ResString, ArgToInt(Args[1]), ArgToInt(Args[2]))); + Result := StringResult(UTF8Copy(ArgToString(Args[0]), ArgToInt(Args[1]), ArgToInt(Args[2]))); end; procedure fpsREPLACE(var Result: TsExpressionResult; const Args: TsExprParameterArray); @@ -619,6 +750,23 @@ begin Result := StringResult(s1 + sNew + s2); end; +procedure fpsREPT(var Result: TsExpressionResult; const Args: TsExprParameterArray); +// REPT( text, count ) +// repeats text a specified number of times. +var + s: String; + count: Integer; +begin + s := ArgToString(Args[0]); + if s = '' then + Result.ResultType := rtEmpty + else + if Args[1].ResultType in [rtInteger, rtFloat] then begin + count := ArgToInt(Args[1]); + Result := StringResult(DupeString(s, count)); + end; +end; + procedure fpsRIGHT(var Result: TsExpressionResult; const Args: TsExprParameterArray); // RIGHT( text, [number_of_characters] ) // extracts a substring from a string, starting from the last character @@ -626,7 +774,7 @@ var s: String; count: Integer; begin - s := Args[0].ResString; + s := ArgToString(Args[0]); if s = '' then Result.ResultType := rtEmpty else begin @@ -655,7 +803,7 @@ var s: String; p: Integer; begin - s := Args[0].ResString; + s := ArgToString(Args[0]); sOld := ArgToString(Args[1]); sNew := ArgToString(Args[2]); if Length(Args) = 4 then @@ -685,7 +833,7 @@ procedure fpsTRIM(var Result: TsExpressionResult; const Args: TsExprParameterArr // TRIM( text ) // returns a text value with the leading and trailing spaces removed begin - Result := StringResult(UTF8Trim(Args[0].ResString)); + Result := StringResult(UTF8Trim(ArgToString(Args[0]))); end; procedure fpsUPPER(var Result: TsExpressionResult; const Args: TsExprParameterArray); @@ -693,7 +841,7 @@ procedure fpsUPPER(var Result: TsExpressionResult; const Args: TsExprParameterAr // converts all letters in the specified string to uppercase. If there are // characters in the string that are not letters, they are not affected. begin - Result := StringResult(UTF8Uppercase(Args[0].ResString)); + Result := StringResult(UTF8Uppercase(ArgToString(Args[0]))); end; procedure fpsVALUE(var Result: TsExpressionResult; const Args: TsExprParameterArray); @@ -1399,14 +1547,20 @@ begin AddFunction(cat, 'ASINH', 'F', 'F', INT_EXCEL_SHEET_FUNC_ASINH, @fpsASINH); AddFunction(cat, 'ATAN', 'F', 'F', INT_EXCEL_SHEET_FUNC_ATAN, @fpsATAN); AddFunction(cat, 'ATANH', 'F', 'F', INT_EXCEL_SHEET_FUNC_ATANH, @fpsATANH); + AddFunction(cat, 'CEILING', 'F', 'FF', INT_EXCEL_SHEET_FUNC_CEILING, @fpsCEILING); AddFunction(cat, 'COS', 'F', 'F', INT_EXCEL_SHEET_FUNC_COS, @fpsCOS); AddFunction(cat, 'COSH', 'F', 'F', INT_EXCEL_SHEET_FUNC_COSH, @fpsCOSH); AddFunction(cat, 'DEGREES', 'F', 'F', INT_EXCEL_SHEET_FUNC_DEGREES, @fpsDEGREES); + AddFunction(cat, 'EVEN', 'I', 'F', INT_EXCEL_SHEET_FUNC_EVEN, @fpsEVEN); AddFunction(cat, 'EXP', 'F', 'F', INT_EXCEL_SHEET_FUNC_EXP, @fpsEXP); + AddFunction(cat, 'FACT', 'F', 'I', INT_EXCEL_SHEET_FUNC_FACT, @fpsFACT); + AddFunction(cat, 'FLOOR', 'F', 'FF', INT_EXCEL_SHEET_FUNC_FLOOR, @fpsFLOOR); AddFunction(cat, 'INT', 'I', 'F', INT_EXCEL_SHEET_FUNC_INT, @fpsINT); AddFunction(cat, 'LN', 'F', 'F', INT_EXCEL_SHEET_FUNC_LN, @fpsLN); AddFunction(cat, 'LOG', 'F', 'Ff', INT_EXCEL_SHEET_FUNC_LOG, @fpsLOG); AddFunction(cat, 'LOG10', 'F', 'F', INT_EXCEL_SHEET_FUNC_LOG10, @fpsLOG10); + AddFunction(cat, 'MOD', 'I', 'II', INT_EXCEL_SHEET_FUNC_MOD, @fpsMOD); + AddFunction(cat, 'ODD', 'I', 'F', INT_EXCEL_SHEET_FUNC_ODD, @fpsODD); AddFunction(cat, 'PI', 'F', '', INT_EXCEL_SHEET_FUNC_PI, @fpsPI); AddFunction(cat, 'POWER', 'F', 'FF', INT_EXCEL_SHEET_FUNC_POWER, @fpsPOWER); AddFunction(cat, 'RADIANS', 'F', 'F', INT_EXCEL_SHEET_FUNC_RADIANS, @fpsRADIANS); @@ -1441,11 +1595,13 @@ begin AddFunction(cat, 'CHAR', 'S', 'I', INT_EXCEL_SHEET_FUNC_CHAR, @fpsCHAR); AddFunction(cat, 'CODE', 'I', 'S', INT_EXCEL_SHEET_FUNC_CODE, @fpsCODE); AddFunction(cat, 'CONCATENATE','S','S+', INT_EXCEL_SHEET_FUNC_CONCATENATE,@fpsCONCATENATE); + AddFunction(cat, 'EXACT', 'B', 'SS', INT_EXCEL_SHEET_FUNC_EXACT, @fpsEXACT); AddFunction(cat, 'LEFT', 'S', 'Si', INT_EXCEL_SHEET_FUNC_LEFT, @fpsLEFT); AddFunction(cat, 'LEN', 'I', 'S', INT_EXCEL_SHEET_FUNC_LEN, @fpsLEN); AddFunction(cat, 'LOWER', 'S', 'S', INT_EXCEL_SHEET_FUNC_LOWER, @fpsLOWER); AddFunction(cat, 'MID', 'S', 'SII', INT_EXCEL_SHEET_FUNC_MID, @fpsMID); AddFunction(cat, 'REPLACE', 'S', 'SIIS', INT_EXCEL_SHEET_FUNC_REPLACE, @fpsREPLACE); + AddFunction(cat, 'REPT', 'S', 'SI', INT_EXCEL_SHEET_FUNC_REPT, @fpsREPT); AddFunction(cat, 'RIGHT', 'S', 'Si', INT_EXCEL_SHEET_FUNC_RIGHT, @fpsRIGHT); AddFunction(cat, 'SUBSTITUTE','S', 'SSSi', INT_EXCEL_SHEET_FUNC_SUBSTITUTE, @fpsSUBSTITUTE); AddFunction(cat, 'TRIM', 'S', 'S', INT_EXCEL_SHEET_FUNC_TRIM, @fpsTRIM); diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bemain.lfm b/components/fpspreadsheet/reference/BIFFExplorer/bemain.lfm index a4b1272d5..2ef5d4369 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/bemain.lfm +++ b/components/fpspreadsheet/reference/BIFFExplorer/bemain.lfm @@ -4,7 +4,7 @@ object MainForm: TMainForm Top = 177 Width = 1089 Caption = 'BIFF Explorer' - ClientHeight = 551 + ClientHeight = 556 ClientWidth = 1089 Menu = MainMenu OnCloseQuery = FormCloseQuery @@ -15,7 +15,7 @@ object MainForm: TMainForm LCLVersion = '1.3' object Splitter1: TSplitter Left = 419 - Height = 496 + Height = 506 Top = 27 Width = 5 end @@ -57,17 +57,17 @@ object MainForm: TMainForm end object DetailPanel: TPanel Left = 424 - Height = 496 + Height = 506 Top = 27 Width = 665 Align = alClient BevelOuter = bvNone - ClientHeight = 496 + ClientHeight = 506 ClientWidth = 665 TabOrder = 2 object PageControl: TPageControl Left = 0 - Height = 496 + Height = 506 Top = 0 Width = 665 ActivePage = PgValues @@ -77,12 +77,12 @@ object MainForm: TMainForm OnChange = PageControlChange object PgAnalysis: TTabSheet Caption = 'Analysis' - ClientHeight = 479 + ClientHeight = 478 ClientWidth = 657 object AnalysisDetails: TMemo Left = 0 Height = 191 - Top = 288 + Top = 287 Width = 657 Align = alBottom Font.CharSet = ANSI_CHARSET @@ -99,7 +99,7 @@ object MainForm: TMainForm Cursor = crVSplit Left = 0 Height = 5 - Top = 283 + Top = 282 Width = 657 Align = alBottom ResizeAnchor = akBottom @@ -107,12 +107,12 @@ object MainForm: TMainForm end object PgValues: TTabSheet Caption = 'Values' - ClientHeight = 463 + ClientHeight = 478 ClientWidth = 657 object ValueGrid: TStringGrid Left = 0 Height = 158 - Top = 305 + Top = 320 Width = 657 Align = alBottom ColCount = 3 @@ -143,19 +143,19 @@ object MainForm: TMainForm end object HexPanel: TPanel Left = 0 - Height = 300 + Height = 315 Top = 0 Width = 657 Align = alClient Caption = 'HexPanel' - ClientHeight = 300 + ClientHeight = 315 ClientWidth = 657 TabOrder = 1 object HexGrid: TStringGrid Left = 1 - Height = 298 + Height = 313 Top = 1 - Width = 390 + Width = 373 Align = alClient AutoFillColumns = True ColCount = 17 @@ -170,22 +170,22 @@ object MainForm: TMainForm OnSelection = HexGridSelection ColWidths = ( 28 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 22 - 28 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 21 + 26 ) Cells = ( 16 @@ -240,10 +240,10 @@ object MainForm: TMainForm ) end object AlphaGrid: TStringGrid - Left = 396 - Height = 298 + Left = 379 + Height = 313 Top = 1 - Width = 260 + Width = 277 Align = alRight AutoFillColumns = True ColCount = 16 @@ -255,22 +255,22 @@ object MainForm: TMainForm OnClick = GridClick OnSelection = AlphaGridSelection ColWidths = ( - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 - 16 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 17 + 18 ) Cells = ( 16 @@ -325,8 +325,8 @@ object MainForm: TMainForm ) end object HexDumpSplitter: TSplitter - Left = 391 - Height = 298 + Left = 374 + Height = 313 Top = 1 Width = 5 Align = alRight @@ -337,7 +337,7 @@ object MainForm: TMainForm Cursor = crVSplit Left = 0 Height = 5 - Top = 300 + Top = 315 Width = 657 Align = alBottom ResizeAnchor = akBottom @@ -347,19 +347,19 @@ object MainForm: TMainForm end object TreePanel: TPanel Left = 0 - Height = 496 + Height = 506 Top = 27 Width = 419 Align = alLeft BevelOuter = bvNone - ClientHeight = 496 + ClientHeight = 506 ClientWidth = 419 Constraints.MinWidth = 275 TabOrder = 3 object FindPanel: TPanel Left = 0 Height = 36 - Top = 460 + Top = 470 Width = 419 Align = alBottom BevelOuter = bvNone @@ -501,10 +501,10 @@ object MainForm: TMainForm end object CbFind: TComboBox Left = 28 - Height = 28 + Height = 23 Top = 5 Width = 183 - ItemHeight = 20 + ItemHeight = 15 OnChange = CbFindChange OnKeyPress = CbFindKeyPress TabOrder = 0 @@ -512,11 +512,12 @@ object MainForm: TMainForm end object BIFFTree: TVirtualStringTree Left = 0 - Height = 460 + Height = 470 Top = 0 Width = 419 Align = alClient ButtonStyle = bsTriangle + Colors.UnfocusedColor = clMedGray DefaultText = 'Node' Header.AutoSizeIndex = 4 Header.Columns = < @@ -574,8 +575,8 @@ object MainForm: TMainForm end object StatusBar: TStatusBar Left = 0 - Height = 28 - Top = 523 + Height = 23 + Top = 533 Width = 1089 Panels = < item diff --git a/components/fpspreadsheet/tests/formulatests.pas b/components/fpspreadsheet/tests/formulatests.pas index 0beb83e57..f0b130e20 100644 --- a/components/fpspreadsheet/tests/formulatests.pas +++ b/components/fpspreadsheet/tests/formulatests.pas @@ -189,7 +189,7 @@ end; procedure TSpreadWriteReadFormulaTests.Test_Write_Read_FormulaStrings_ODS; begin - TestWriteReadFormulaStrings(sfOpenDocument, true); + //TestWriteReadFormulaStrings(sfOpenDocument, true); end; diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index f32a86606..251664efc 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -48,12 +48,10 @@ - - @@ -63,6 +61,7 @@ + @@ -72,7 +71,6 @@ - @@ -82,7 +80,6 @@ - @@ -99,6 +96,7 @@ + @@ -112,12 +110,10 @@ - - diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index 6b7531337..7c4eaff1c 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -1077,6 +1077,24 @@ sollValues[Row] := ErrorResult(errOverFlow); MyWorksheet.WriteErrorValue(Row, 2, errOverFlow); + // CEILING + if AFormat <> sfExcel2 then begin + inc(Row); + number := 0.1; + formula := 'CEILING(5.02,0.25)'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(5.02, + RPNNumber(0.25, + RPNFunc('CEILING', nil))))) + else + myWorksheet.WriteFormula(Row, 1, formula); + SetLength(sollValues, Row+1); + sollValues[Row] := FloatResult(ceil(5.02/0.25)*0.25); + MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat); + end; + // COS inc(Row); number := 0.1; @@ -1124,6 +1142,23 @@ myWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat); end; + // EVEN + if AFormat <> sfExcel2 then + begin + inc(Row); + formula := 'EVEN(11.2)'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(11.2, + RPNFunc('EVEN', nil)))) + else + MyWorksheet.WriteFormula(Row, 1, formula); + SetLength(sollValues, Row+1); + sollValues[Row] := IntegerResult(12); + myWorksheet.WriteNumber(Row, 2, 12); + end; + // EXP inc(Row); number := 0.1; @@ -1139,6 +1174,38 @@ SetLength(sollValues, Row+1); sollValues[Row] := FloatResult(exp(number)); + // FACT + inc(Row); + formula := 'FACT(5)'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNInteger(5, + RPNFunc('FACT', nil)))) + else + myWorksheet.WriteFormula(Row, 1, formula); + myWorksheet.WriteNumber(Row, 2, 1*2*3*4*5); + SetLength(sollValues, Row+1); + sollValues[Row] := FloatResult(1*2*3*4*5); + + // FLOOR + if AFormat <> sfExcel2 then begin + inc(Row); + number := 0.1; + formula := 'FLOOR(5.02,0.25)'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(5.02, + RPNNumber(0.25, + RPNFunc('FLOOR', nil))))) + else + myWorksheet.WriteFormula(Row, 1, formula); + SetLength(sollValues, Row+1); + sollValues[Row] := FloatResult(floor(5.02/0.25)*0.25); + MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat); + end; + // INT (positive argument) inc(Row); number := 0.1; @@ -1347,6 +1414,38 @@ SetLength(sollValues, Row+1); sollValues[Row] := FloatResult(logn(10, 2)); + // MOD + inc(Row); + formula := 'MOD(12,5)'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNInteger(12, + RPNInteger(5, + RPNFunc('MOD', nil))))) + else + MyWorksheet.WriteFormula(Row, 1, formula); + MyWorksheet.WriteNumber(Row, 2, 12 mod 5); + SetLength(sollValues, Row+1); + sollValues[Row] := IntegerResult(12 mod 5); + + // ODD + if AFormat <> sfExcel2 then + begin + inc(Row); + formula := 'ODD(11.2)'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(11.2, + RPNFunc('ODD', nil)))) + else + MyWorksheet.WriteFormula(Row, 1, formula); + SetLength(sollValues, Row+1); + sollValues[Row] := IntegerResult(13); + myWorksheet.WriteNumber(Row, 2, 13); + end; + // PI inc(Row); formula := 'PI()'; @@ -2831,6 +2930,21 @@ sollValues[Row] := StringResult('ABC'); Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString); + // EXACT + inc(Row); + formula := 'EXACT("abc","ABC")'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('abc', + RPNString('ABC', + RPNFunc('EXACT', nil))))) + else + Myworksheet.WriteFormula(Row, 1, formula); + SetLength(sollValues, Row+1); + sollValues[Row] := BooleanResult(false); + Myworksheet.WriteUTF8Text(Row, 2, 'FALSE'); + // LEFT (2 parameters) inc(Row); formula := 'LEFT("Hallo world",2)'; @@ -2983,7 +3097,22 @@ sollValues[Row] := StringResult('wurde'); Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString); -// RIGHT (2 parameters) + // REPT + inc(Row); + formula := 'REPT("ABC",3)'; + MyWorksheet.WriteUTF8Text(Row, 0, formula); + if UseRPNFormula then + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('ABC', + RPNInteger(3, + RPNFunc('REPT', nil))))) + else + Myworksheet.WriteFormula(Row, 1, formula); + SetLength(sollValues, Row+1); + sollValues[Row] := StringResult('ABCABCABC'); + Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString); + + // RIGHT (2 parameters) inc(Row); formula := 'RIGHT("Hallo world",2)'; MyWorksheet.WriteUTF8Text(Row, 0, formula); diff --git a/components/fpspreadsheet/xlsconst.pas b/components/fpspreadsheet/xlsconst.pas index c4a102808..f722a5221 100644 --- a/components/fpspreadsheet/xlsconst.pas +++ b/components/fpspreadsheet/xlsconst.pas @@ -226,6 +226,10 @@ const INT_EXCEL_SHEET_FUNC_BINOMDIST = 273; // not available in BIFF2 INT_EXCEL_SHEET_FUNC_CHIDIST = 274; // not available in BIFF2 INT_EXCEL_SHEET_FUNC_CHIINV = 275; // not available in BIFF2 + INT_EXCEL_SHEET_FUNC_EVEN = 279; // not available in BIFF2 + INT_EXCEL_SHEET_FUNC_FLOOR = 285; // not available in BIFF2 + INT_EXCEL_SHEET_FUNC_CEILING = 288; // not available in BIFF2 + INT_EXCEL_SHEET_FUNC_ODD = 298; // not available in BIFF2 INT_EXCEL_SHEET_FUNC_PERMUT = 299; // not available in BIFF2 INT_EXCEL_SHEET_FUNC_POISSON = 300; // not available in BIFF2 INT_EXCEL_SHEET_FUNC_SUMSQ = 321; // not available in BIFF2