1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "interpre.hxx"
22 #include <comphelper/string.hxx>
23 #include <sfx2/linkmgr.hxx>
24 #include <sfx2/dispatch.hxx>
25 #include <sfx2/objsh.hxx>
26 #include <svl/stritem.hxx>
27 #include <svl/zforlist.hxx>
28 #include <svl/sharedstringpool.hxx>
29 #include <sal/macros.h>
30 #include <boost/math/special_functions/log1p.hpp>
34 #include "ddelink.hxx"
35 #include "scmatrix.hxx"
36 #include "compiler.hxx"
37 #include "formulacell.hxx"
38 #include "document.hxx"
39 #include "dociter.hxx"
40 #include "docoptio.hxx"
41 #include "unitconv.hxx"
42 #include "globstr.hrc"
44 #include "dpobject.hxx"
46 #include "tokenarray.hxx"
47 #include "globalnames.hxx"
49 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
55 using namespace com::sun::star
;
56 using namespace formula
;
58 #define SCdEpsilon 1.0E-7
62 double ScInterpreter::GetDateSerial( sal_Int16 nYear
, sal_Int16 nMonth
, sal_Int16 nDay
,
63 bool bStrict
, bool bCheckGregorian
)
65 if ( nYear
< 100 && !bStrict
)
66 nYear
= pFormatter
->ExpandTwoDigitYear( nYear
);
67 // Do not use a default Date ctor here because it asks system time with a
68 // performance penalty.
71 nY
= nYear
, nM
= nMonth
, nD
= nDay
;
76 nY
= nYear
+ (nMonth
-1) / 12;
77 nM
= ((nMonth
-1) % 12) + 1;
81 nY
= nYear
+ (nMonth
-12) / 12;
82 nM
= 12 - (-nMonth
) % 12;
86 Date
aDate( nD
, nM
, nY
);
89 if ((!bCheckGregorian
&& aDate
.IsValidDate()) || (bCheckGregorian
&& aDate
.IsValidAndGregorian()))
90 return (double) (aDate
- *(pFormatter
->GetNullDate()));
98 void ScInterpreter::ScGetActDate()
100 nFuncFmtType
= css::util::NumberFormat::DATE
;
101 Date
aActDate( Date::SYSTEM
);
102 long nDiff
= aActDate
- *(pFormatter
->GetNullDate());
103 PushDouble((double) nDiff
);
106 void ScInterpreter::ScGetActTime()
108 nFuncFmtType
= css::util::NumberFormat::DATETIME
;
109 Date
aActDate( Date::SYSTEM
);
110 long nDiff
= aActDate
- *(pFormatter
->GetNullDate());
111 tools::Time
aActTime( tools::Time::SYSTEM
);
112 double nTime
= aActTime
.GetHour() / static_cast<double>(::tools::Time::hourPerDay
) +
113 aActTime
.GetMin() / static_cast<double>(::tools::Time::minutePerDay
) +
114 aActTime
.GetSec() / static_cast<double>(::tools::Time::secondPerDay
) +
115 aActTime
.GetNanoSec() / static_cast<double>(::tools::Time::nanoSecPerDay
);
116 PushDouble( (double) nDiff
+ nTime
);
119 void ScInterpreter::ScGetYear()
121 Date aDate
= *(pFormatter
->GetNullDate());
122 aDate
+= (long) ::rtl::math::approxFloor(GetDouble());
123 PushDouble( (double) aDate
.GetYear() );
126 void ScInterpreter::ScGetMonth()
128 Date aDate
= *(pFormatter
->GetNullDate());
129 aDate
+= (long) ::rtl::math::approxFloor(GetDouble());
130 PushDouble( (double) aDate
.GetMonth() );
133 void ScInterpreter::ScGetDay()
135 Date aDate
= *(pFormatter
->GetNullDate());
136 aDate
+= (long)::rtl::math::approxFloor(GetDouble());
137 PushDouble((double) aDate
.GetDay());
140 void ScInterpreter::ScGetMin()
142 double fTime
= GetDouble();
143 fTime
-= ::rtl::math::approxFloor(fTime
); // Datumsanteil weg
144 long nVal
= (long)::rtl::math::approxFloor(fTime
*DATE_TIME_FACTOR
+0.5) % ::tools::Time::secondPerHour
;
145 PushDouble( (double) (nVal
/ ::tools::Time::secondPerMinute
) );
148 void ScInterpreter::ScGetSec()
150 double fTime
= GetDouble();
151 fTime
-= ::rtl::math::approxFloor(fTime
); // Datumsanteil weg
152 long nVal
= (long)::rtl::math::approxFloor(fTime
*DATE_TIME_FACTOR
+0.5) % ::tools::Time::secondPerMinute
;
153 PushDouble( (double) nVal
);
156 void ScInterpreter::ScGetHour()
158 double fTime
= GetDouble();
159 fTime
-= ::rtl::math::approxFloor(fTime
); // Datumsanteil weg
160 long nVal
= (long)::rtl::math::approxFloor(fTime
*DATE_TIME_FACTOR
+0.5) / ::tools::Time::secondPerHour
;
161 PushDouble((double) nVal
);
164 void ScInterpreter::ScGetDateValue()
166 OUString aInputString
= GetString().getString();
167 sal_uInt32 nFIndex
= 0; // damit default Land/Spr.
169 if (pFormatter
->IsNumberFormat(aInputString
, nFIndex
, fVal
))
171 short eType
= pFormatter
->GetType(nFIndex
);
172 if (eType
== css::util::NumberFormat::DATE
|| eType
== css::util::NumberFormat::DATETIME
)
173 PushDouble(::rtl::math::approxFloor(fVal
));
175 PushIllegalArgument();
178 PushIllegalArgument();
181 void ScInterpreter::ScGetDayOfWeek()
183 sal_uInt8 nParamCount
= GetByte();
184 if ( MustHaveParamCount( nParamCount
, 1, 2 ) )
187 if (nParamCount
== 2)
188 nFlag
= (short) ::rtl::math::approxFloor(GetDouble());
192 Date aDate
= *(pFormatter
->GetNullDate());
193 aDate
+= (long)::rtl::math::approxFloor(GetDouble());
194 int nVal
= (int) aDate
.GetDayOfWeek();
208 void ScInterpreter::ScGetWeekOfYear()
210 if ( MustHaveParamCount( GetByte(), 2 ) )
212 short nFlag
= (short) ::rtl::math::approxFloor(GetDouble());
214 Date aDate
= *(pFormatter
->GetNullDate());
215 aDate
+= (long)::rtl::math::approxFloor(GetDouble());
216 PushInt( (int) aDate
.GetWeekOfYear( nFlag
== 1 ? SUNDAY
: MONDAY
));
220 void ScInterpreter::ScEasterSunday()
222 nFuncFmtType
= css::util::NumberFormat::DATE
;
223 if ( MustHaveParamCount( GetByte(), 1 ) )
225 sal_Int16 nDay
, nMonth
, nYear
;
226 nYear
= (sal_Int16
) ::rtl::math::approxFloor( GetDouble() );
228 nYear
= pFormatter
->ExpandTwoDigitYear( nYear
);
229 // don't worry, be happy :)
230 int B
,C
,D
,E
,F
,G
,H
,I
,K
,L
,M
,N
,O
;
232 B
= int(nYear
/ 100);
236 F
= int((B
+ 8) / 25);
237 G
= int((B
- F
+ 1) / 3);
238 H
= (19 * N
+ B
- D
- G
+ 15) % 30;
241 L
= (32 + 2 * E
+ 2 * I
- H
- K
) % 7;
242 M
= int((N
+ 11 * H
+ 22 * L
) / 451);
243 O
= H
+ L
- 7 * M
+ 114;
244 nDay
= sal::static_int_cast
<sal_Int16
>( O
% 31 + 1 );
245 nMonth
= sal::static_int_cast
<sal_Int16
>( int(O
/ 31) );
246 PushDouble( GetDateSerial( nYear
, nMonth
, nDay
, true, true ) );
250 sal_uInt16
ScInterpreter::GetWeekendAndHolidayMasks(
251 const sal_uInt8 nParamCount
, const sal_uInt32 nNullDate
, vector
< double >& rSortArray
,
252 bool bWeekendMask
[ 7 ] )
254 if ( nParamCount
== 4 )
256 vector
< double > nWeekendDays
;
257 GetNumberSequenceArray( 1, nWeekendDays
, false );
262 if ( nWeekendDays
.size() != 7 )
263 return errIllegalArgument
;
265 for ( int i
= 0; i
< 7; i
++ )
266 bWeekendMask
[ i
] = ( bool ) nWeekendDays
[ i
];
271 for ( int i
= 0; i
< 7; i
++ )
272 bWeekendMask
[ i
] = false;
274 bWeekendMask
[ SATURDAY
] = true;
275 bWeekendMask
[ SUNDAY
] = true;
278 if ( nParamCount
>= 3 )
280 GetSortArray( 1, rSortArray
, NULL
, false, true );
281 size_t nMax
= rSortArray
.size();
282 for ( size_t i
= 0; i
< nMax
; i
++ )
283 rSortArray
.at( i
) = ::rtl::math::approxFloor( rSortArray
.at( i
) ) + nNullDate
;
289 sal_uInt16
ScInterpreter::GetWeekendAndHolidayMasks_MS(
290 const sal_uInt8 nParamCount
, const sal_uInt32 nNullDate
, vector
< double >& rSortArray
,
291 bool bWeekendMask
[ 7 ] )
294 OUString aWeekendDays
;
295 if ( nParamCount
== 4 )
297 GetSortArray( 1, rSortArray
, NULL
, true, true );
298 size_t nMax
= rSortArray
.size();
299 for ( size_t i
= 0; i
< nMax
; i
++ )
300 rSortArray
.at( i
) = ::rtl::math::approxFloor( rSortArray
.at( i
) ) + nNullDate
;
303 if ( nParamCount
>= 3 )
304 aWeekendDays
= GetString().getString();
306 for ( int i
= 0; i
< 7; i
++ )
307 bWeekendMask
[ i
] = false;
309 if ( aWeekendDays
.isEmpty() )
311 bWeekendMask
[ SATURDAY
] = true;
312 bWeekendMask
[ SUNDAY
] = true;
316 switch ( aWeekendDays
.getLength() )
319 // Weekend days defined by code
320 switch ( aWeekendDays
[ 0 ] )
322 case '1' : bWeekendMask
[ SATURDAY
] = true; bWeekendMask
[ SUNDAY
] = true; break;
323 case '2' : bWeekendMask
[ SUNDAY
] = true; bWeekendMask
[ MONDAY
] = true; break;
324 case '3' : bWeekendMask
[ MONDAY
] = true; bWeekendMask
[ TUESDAY
] = true; break;
325 case '4' : bWeekendMask
[ TUESDAY
] = true; bWeekendMask
[ WEDNESDAY
] = true; break;
326 case '5' : bWeekendMask
[ WEDNESDAY
] = true; bWeekendMask
[ THURSDAY
] = true; break;
327 case '6' : bWeekendMask
[ THURSDAY
] = true; bWeekendMask
[ FRIDAY
] = true; break;
328 case '7' : bWeekendMask
[ FRIDAY
] = true; bWeekendMask
[ SATURDAY
] = true; break;
329 default : nErr
= errIllegalArgument
; break;
333 // Weekend day defined by code
334 if ( aWeekendDays
[ 0 ] == '1' )
336 switch ( aWeekendDays
[ 1 ] )
338 case '1' : bWeekendMask
[ SUNDAY
] = true; break;
339 case '2' : bWeekendMask
[ MONDAY
] = true; break;
340 case '3' : bWeekendMask
[ TUESDAY
] = true; break;
341 case '4' : bWeekendMask
[ WEDNESDAY
] = true; break;
342 case '5' : bWeekendMask
[ THURSDAY
] = true; break;
343 case '6' : bWeekendMask
[ FRIDAY
] = true; break;
344 case '7' : bWeekendMask
[ SATURDAY
] = true; break;
345 default : nErr
= errIllegalArgument
; break;
349 nErr
= errIllegalArgument
;
352 // Weekend days defined by string
353 for ( int i
= 0; i
< 7 && !nErr
; i
++ )
355 switch ( aWeekendDays
[ i
] )
357 case '0' : bWeekendMask
[ i
] = false; break;
358 case '1' : bWeekendMask
[ i
] = true; break;
359 default : nErr
= errIllegalArgument
; break;
364 nErr
= errIllegalArgument
;
371 void ScInterpreter::ScNetWorkdays( bool bOOXML_Version
)
373 sal_uInt8 nParamCount
= GetByte();
374 if ( MustHaveParamCount( nParamCount
, 2, 4 ) )
376 vector
<double> nSortArray
;
377 bool bWeekendMask
[ 7 ];
378 Date aNullDate
= *( pFormatter
->GetNullDate() );
379 sal_uInt32 nNullDate
= Date::DateToDays( aNullDate
.GetDay(), aNullDate
.GetMonth(), aNullDate
.GetYear() );
381 if ( bOOXML_Version
)
383 nErr
= GetWeekendAndHolidayMasks_MS( nParamCount
, nNullDate
,
384 nSortArray
, bWeekendMask
);
388 nErr
= GetWeekendAndHolidayMasks( nParamCount
, nNullDate
,
389 nSortArray
, bWeekendMask
);
395 sal_uInt32 nDate2
= ( sal_uInt32
)::rtl::math::approxFloor( GetDouble() ) + nNullDate
;
396 sal_uInt32 nDate1
= ( sal_uInt32
)::rtl::math::approxFloor( GetDouble() ) + nNullDate
;
400 bool bReverse
= ( nDate1
> nDate2
);
403 sal_uInt32 nTemp
= nDate1
;
407 size_t nMax
= nSortArray
.size();
408 while ( nDate1
<= nDate2
)
410 if ( !bWeekendMask
[ GetDayOfWeek( nDate1
) ] )
412 while ( nRef
< nMax
&& nSortArray
.at( nRef
) < nDate1
)
414 if ( !( nRef
< nMax
&& nSortArray
.at( nRef
) == nDate1
) )
419 PushDouble( ( double ) ( bReverse
? -nCnt
: nCnt
) );
424 void ScInterpreter::ScWorkday_MS()
426 sal_uInt8 nParamCount
= GetByte();
427 if ( MustHaveParamCount( nParamCount
, 2, 4 ) )
429 nFuncFmtType
= css::util::NumberFormat::DATE
;
430 vector
<double> nSortArray
;
431 bool bWeekendMask
[ 7 ];
432 Date aNullDate
= *( pFormatter
->GetNullDate() );
433 sal_uInt32 nNullDate
= Date::DateToDays( aNullDate
.GetDay(), aNullDate
.GetMonth(), aNullDate
.GetYear() );
434 sal_uInt16 nErr
= GetWeekendAndHolidayMasks_MS( nParamCount
, nNullDate
,
435 nSortArray
, bWeekendMask
);
440 sal_Int32 nDays
= ::rtl::math::approxFloor( GetDouble() );
441 sal_uInt32 nDate
= ( sal_uInt32
)::rtl::math::approxFloor( GetDouble() ) + nNullDate
;
444 PushDouble( ( double ) ( nDate
- nNullDate
) );
447 size_t nMax
= nSortArray
.size();
453 while ( nRef
< nMax
&& nSortArray
.at( nRef
) < nDate
)
455 if ( !( nRef
< nMax
&& nSortArray
.at( nRef
) == nDate
) || nRef
>= nMax
)
460 while ( bWeekendMask
[ GetDayOfWeek( nDate
) ] ); //jump over weekend day(s)
465 sal_Int16 nRef
= nMax
- 1;
468 while ( nRef
>= 0 && nSortArray
.at( nRef
) > nDate
)
470 if ( !( nRef
>= 0 && nSortArray
.at( nRef
) == nDate
) || nRef
< 0 )
475 while ( bWeekendMask
[ GetDayOfWeek( nDate
) ] ); //jump over weekend day(s)
478 PushDouble( ( double ) ( nDate
- nNullDate
) );
484 void ScInterpreter::ScGetDate()
486 nFuncFmtType
= css::util::NumberFormat::DATE
;
487 if ( MustHaveParamCount( GetByte(), 3 ) )
489 sal_Int16 nDay
= (sal_Int16
) ::rtl::math::approxFloor(GetDouble());
490 sal_Int16 nMonth
= (sal_Int16
) ::rtl::math::approxFloor(GetDouble());
491 sal_Int16 nYear
= (sal_Int16
) ::rtl::math::approxFloor(GetDouble());
493 PushIllegalArgument();
496 PushDouble(GetDateSerial(nYear
, nMonth
, nDay
, false, true));
501 void ScInterpreter::ScGetTime()
503 nFuncFmtType
= css::util::NumberFormat::TIME
;
504 if ( MustHaveParamCount( GetByte(), 3 ) )
506 double nSec
= GetDouble();
507 double nMin
= GetDouble();
508 double nHour
= GetDouble();
509 double fTime
= fmod( (nHour
* ::tools::Time::secondPerHour
) + (nMin
* ::tools::Time::secondPerMinute
) + nSec
, DATE_TIME_FACTOR
) / DATE_TIME_FACTOR
;
511 PushIllegalArgument();
517 void ScInterpreter::ScGetDiffDate()
519 if ( MustHaveParamCount( GetByte(), 2 ) )
521 double nDate2
= GetDouble();
522 double nDate1
= GetDouble();
523 PushDouble(nDate1
- nDate2
);
527 void ScInterpreter::ScGetDiffDate360()
529 /* Implementation follows
530 * http://www.bondmarkets.com/eCommerce/SMD_Fields_030802.pdf
531 * Appendix B: Day-Count Bases, there are 7 different ways to calculate the
532 * 30-days count. That document also claims that Excel implements the "PSA
533 * 30" or "NASD 30" method (funny enough they also state that Excel is the
534 * only tool that does so).
536 * Note that the definition given in
537 * http://msdn.microsoft.com/library/en-us/office97/html/SEB7C.asp
538 * is _not_ the way how it is actually calculated by Excel (that would not
539 * even match any of the 7 methods mentioned above) and would result in the
540 * following test cases producing wrong results according to that appendix B:
542 * 28-Feb-95 31-Aug-95 181 instead of 180
543 * 29-Feb-96 31-Aug-96 181 instead of 180
544 * 30-Jan-96 31-Mar-96 61 instead of 60
545 * 31-Jan-96 31-Mar-96 61 instead of 60
547 * Still, there is a difference between OOoCalc and Excel:
549 * 02-Feb-99 31-Mar-00 results in 419
550 * 31-Mar-00 02-Feb-99 results in -418
551 * In Calc the result is 419 respectively -419. I consider the -418 a bug in Excel.
554 sal_uInt8 nParamCount
= GetByte();
555 if ( MustHaveParamCount( nParamCount
, 2, 3 ) )
558 if (nParamCount
== 3)
562 double nDate2
= GetDouble();
563 double nDate1
= GetDouble();
565 PushError( nGlobalError
);
569 // #i84934# only for non-US European algorithm swap dates. Else
570 // follow Excel's meaningless extrapolation for "interoperability".
571 if (bFlag
&& (nDate2
< nDate1
))
580 Date aDate1
= *(pFormatter
->GetNullDate());
581 aDate1
+= (long) ::rtl::math::approxFloor(nDate1
);
582 Date aDate2
= *(pFormatter
->GetNullDate());
583 aDate2
+= (long) ::rtl::math::approxFloor(nDate2
);
584 if (aDate1
.GetDay() == 31)
585 aDate1
-= (sal_uLong
) 1;
588 if (aDate1
.GetMonth() == 2)
590 switch ( aDate1
.GetDay() )
593 if ( !aDate1
.IsLeapYear() )
602 if (aDate2
.GetDay() == 31)
606 if (aDate1
.GetDay() == 30)
607 aDate2
-= (sal_uLong
) 1;
612 PushDouble( fSign
* (double)
613 ( (double) aDate2
.GetDay() + (double) aDate2
.GetMonth() * 30.0 +
614 (double) aDate2
.GetYear() * 360.0
615 - (double) aDate1
.GetDay() - (double) aDate1
.GetMonth() * 30.0
616 - (double)aDate1
.GetYear() * 360.0) );
621 // fdo#44456 function DATEDIF as defined in ODF1.2 (Par. 6.10.3)
622 void ScInterpreter::ScGetDateDif()
624 if ( MustHaveParamCount( GetByte(), 3 ) )
626 OUString aInterval
= GetString().getString();
627 double nDate2
= GetDouble();
628 double nDate1
= GetDouble();
632 PushError( nGlobalError
);
636 // Excel doesn't swap dates or return negative numbers, so don't we.
639 PushIllegalArgument();
643 long dd
= nDate2
- nDate1
;
644 // Zero difference or number of days can be returned immediately.
645 if (dd
== 0 || aInterval
.equalsIgnoreAsciiCase( "d" ))
651 // split dates in day, month, year for use with formats other than "d"
652 sal_uInt16 d1
, m1
, y1
, d2
, m2
, y2
;
653 Date
aDate1( *( pFormatter
->GetNullDate()));
654 aDate1
+= (long) ::rtl::math::approxFloor( nDate1
);
655 y1
= aDate1
.GetYear();
656 m1
= aDate1
.GetMonth();
657 d1
= aDate1
.GetDay();
658 Date
aDate2( *( pFormatter
->GetNullDate()));
659 aDate2
+= (long) ::rtl::math::approxFloor( nDate2
);
660 y2
= aDate2
.GetYear();
661 m2
= aDate2
.GetMonth();
662 d2
= aDate2
.GetDay();
664 if ( aInterval
.equalsIgnoreAsciiCase( "m" ) )
666 // Return number of months.
667 int md
= m2
- m1
+ 12 * (y2
- y1
);
672 else if ( aInterval
.equalsIgnoreAsciiCase( "y" ) )
674 // Return number of years.
678 if (m2
> m1
|| (m2
== m1
&& d2
>= d1
))
679 yd
= y2
- y1
; // complete years between dates
681 yd
= y2
- y1
- 1; // one incomplete year
685 // Year is equal as we don't allow reversed arguments, no
686 // complete year between dates.
691 else if ( aInterval
.equalsIgnoreAsciiCase( "md" ) )
693 // Return number of days, excluding months and years.
694 // This is actually the remainder of days when subtracting years
695 // and months from the difference of dates. Birthday-like 23 years
696 // and 10 months and 19 days.
698 // Algorithm's roll-over behavior extracted from Excel by try and
700 // If day1 <= day2 then simply day2 - day1.
701 // If day1 > day2 then set month1 to month2-1 and year1 to
702 // year2(-1) and subtract dates, e.g. for 2012-01-28,2012-03-01 set
703 // 2012-02-28 and then (2012-03-01)-(2012-02-28) => 2 days (leap
705 // For 2011-01-29,2011-03-01 the non-existent 2011-02-29 rolls over
706 // to 2011-03-01 so the result is 0. Same for day 31 in months with
716 aDate1
.SetYear( y2
- 1 );
717 aDate1
.SetMonth( 12 );
721 aDate1
.SetYear( y2
);
722 aDate1
.SetMonth( m2
- 1 );
725 nd
= aDate2
- aDate1
;
729 else if ( aInterval
.equalsIgnoreAsciiCase( "ym" ) )
731 // Return number of months, excluding years.
732 int md
= m2
- m1
+ 12 * (y2
- y1
);
738 else if ( aInterval
.equalsIgnoreAsciiCase( "yd" ) )
740 // Return number of days, excluding years.
742 /* TODO: check what Excel really does, though this seems to be
745 // Condition corresponds with "y".
746 if (m2
> m1
|| (m2
== m1
&& d2
>= d1
))
747 aDate1
.SetYear( y2
);
749 aDate1
.SetYear( y2
- 1 );
750 // XXX NOTE: Excel for the case 1988-06-22,2012-05-11 returns
751 // 323, whereas the result here is 324. Don't they use the leap
753 // http://www.cpearson.com/excel/datedif.aspx "DATEDIF And Leap
754 // Years" is not correct and Excel 2010 correctly returns 0 in
755 // both cases mentioned there. Also using year1 as mentioned
756 // produces incorrect results in other cases and different from
757 // Excel 2010. Apparently they fixed some calculations.
759 double nd
= aDate2
- aDate1
;
763 PushIllegalArgument(); // unsupported format
767 void ScInterpreter::ScGetTimeValue()
769 OUString aInputString
= GetString().getString();
770 sal_uInt32 nFIndex
= 0; // damit default Land/Spr.
772 if (pFormatter
->IsNumberFormat(aInputString
, nFIndex
, fVal
))
774 short eType
= pFormatter
->GetType(nFIndex
);
775 if (eType
== css::util::NumberFormat::TIME
|| eType
== css::util::NumberFormat::DATETIME
)
777 double fDateVal
= rtl::math::approxFloor(fVal
);
778 double fTimeVal
= fVal
- fDateVal
;
779 PushDouble(fTimeVal
);
782 PushIllegalArgument();
785 PushIllegalArgument();
788 void ScInterpreter::ScPlusMinus()
790 double nVal
= GetDouble();
799 void ScInterpreter::ScAbs()
801 PushDouble(fabs(GetDouble()));
804 void ScInterpreter::ScInt()
806 PushDouble(::rtl::math::approxFloor(GetDouble()));
809 void ScInterpreter::RoundNumber( rtl_math_RoundingMode eMode
)
811 sal_uInt8 nParamCount
= GetByte();
812 if ( MustHaveParamCount( nParamCount
, 1, 2 ) )
815 if (nParamCount
== 1)
816 fVal
= ::rtl::math::round( GetDouble(), 0, eMode
);
819 sal_Int32 nDec
= (sal_Int32
) ::rtl::math::approxFloor(GetDouble());
820 if( nDec
< -20 || nDec
> 20 )
821 PushIllegalArgument();
823 fVal
= ::rtl::math::round( GetDouble(), (short)nDec
, eMode
);
829 void ScInterpreter::ScRound()
831 RoundNumber( rtl_math_RoundingMode_Corrected
);
834 void ScInterpreter::ScRoundDown()
836 RoundNumber( rtl_math_RoundingMode_Down
);
839 void ScInterpreter::ScRoundUp()
841 RoundNumber( rtl_math_RoundingMode_Up
);
844 /** tdf69552 ODFF1.2 function CEILING and Excel function CEILING.MATH
845 In essence, the difference between the two is that ODFF-CEILING needs to
846 have arguments value and significance of the same sign and with
847 CEILING.MATH the sign of argument significance is irrevelevant.
848 This is why ODFF-CEILING is exported to Excel as CEILING.MATH and
849 CEILING.MATH is imported in Calc as CEILING.MATH
851 void ScInterpreter::ScCeil( bool bODFF
)
853 sal_uInt8 nParamCount
= GetByte();
854 if ( MustHaveParamCount( nParamCount
, 1, 3 ) )
856 bool bAbs
= nParamCount
== 3 && GetBool();
858 if ( nParamCount
== 1 )
861 fDec
= ( fVal
< 0 ? -1 : 1 );
865 bool bArgumentMissing
= IsMissing();
868 if ( bArgumentMissing
)
869 fDec
= ( fVal
< 0 ? -1 : 1 );
871 if ( fVal
== 0 || fDec
== 0.0 )
875 if ( bODFF
&& fVal
* fDec
< 0 )
876 PushIllegalArgument();
879 if ( fVal
* fDec
< 0.0 )
882 if ( !bAbs
&& fVal
< 0.0 )
883 PushDouble(::rtl::math::approxFloor( fVal
/ fDec
) * fDec
);
885 PushDouble(::rtl::math::approxCeil( fVal
/ fDec
) * fDec
);
891 void ScInterpreter::ScCeil_MS()
893 sal_uInt8 nParamCount
= GetByte();
894 if ( MustHaveParamCount( nParamCount
, 2 ) )
896 double fDec
= GetDouble();
897 double fVal
= GetDouble();
898 if ( fVal
== 0 || fDec
== 0.0 )
900 else if ( fVal
* fDec
> 0 )
901 PushDouble(::rtl::math::approxCeil( fVal
/ fDec
) * fDec
);
902 else if ( fVal
< 0.0 )
903 PushDouble(::rtl::math::approxFloor( fVal
/ -fDec
) * -fDec
);
905 PushIllegalArgument();
909 void ScInterpreter::ScCeil_Precise()
911 sal_uInt8 nParamCount
= GetByte();
912 if ( MustHaveParamCount( nParamCount
, 1, 2 ) )
915 if ( nParamCount
== 1 )
922 fDec
= fabs( GetDoubleWithDefault( 1.0 ));
925 if ( fDec
== 0.0 || fVal
== 0.0 )
928 PushDouble(::rtl::math::approxCeil( fVal
/ fDec
) * fDec
);
932 /** tdf69552 ODFF1.2 function FLOOR and Excel function FLOOR.MATH
933 In essence, the difference between the two is that ODFF-FLOOR needs to
934 have arguments value and significance of the same sign and with
935 FLOOR.MATH the sign of argument significance is irrevelevant.
936 This is why ODFF-FLOOR is exported to Excel as FLOOR.MATH and
937 FLOOR.MATH is imported in Calc as FLOOR.MATH
939 void ScInterpreter::ScFloor( bool bODFF
)
941 sal_uInt8 nParamCount
= GetByte();
942 if ( MustHaveParamCount( nParamCount
, 1, 3 ) )
944 bool bAbs
= ( nParamCount
== 3 && GetBool() );
946 if ( nParamCount
== 1 )
949 fDec
= ( fVal
< 0 ? -1 : 1 );
953 bool bArgumentMissing
= IsMissing();
956 if ( bArgumentMissing
)
957 fDec
= ( fVal
< 0 ? -1 : 1 );
959 if ( fDec
== 0.0 || fVal
== 0.0 )
963 if ( bODFF
&& ( fVal
* fDec
< 0.0 ) )
964 PushIllegalArgument();
967 if ( fVal
* fDec
< 0.0 )
970 if ( !bAbs
&& fVal
< 0.0 )
971 PushDouble(::rtl::math::approxCeil( fVal
/ fDec
) * fDec
);
973 PushDouble(::rtl::math::approxFloor( fVal
/ fDec
) * fDec
);
979 void ScInterpreter::ScFloor_MS()
981 sal_uInt8 nParamCount
= GetByte();
982 if ( MustHaveParamCount( nParamCount
, 2 ) )
984 double fDec
= GetDouble();
985 double fVal
= GetDouble();
989 else if ( fVal
* fDec
> 0 )
990 PushDouble(::rtl::math::approxFloor( fVal
/ fDec
) * fDec
);
991 else if ( fDec
== 0 )
992 PushIllegalArgument();
993 else if ( fVal
< 0.0 )
994 PushDouble(::rtl::math::approxCeil( fVal
/ -fDec
) * -fDec
);
996 PushIllegalArgument();
1000 void ScInterpreter::ScFloor_Precise()
1002 sal_uInt8 nParamCount
= GetByte();
1003 if ( MustHaveParamCount( nParamCount
, 1, 2 ) )
1006 if ( nParamCount
== 1 )
1013 fDec
= fabs( GetDoubleWithDefault( 1.0 ) );
1016 if ( fDec
== 0.0 || fVal
== 0.0 )
1019 PushDouble(::rtl::math::approxFloor( fVal
/ fDec
) * fDec
);
1023 void ScInterpreter::ScEven()
1025 double fVal
= GetDouble();
1027 PushDouble(::rtl::math::approxFloor(fVal
/2.0) * 2.0);
1029 PushDouble(::rtl::math::approxCeil(fVal
/2.0) * 2.0);
1032 void ScInterpreter::ScOdd()
1034 double fVal
= GetDouble();
1037 fVal
= ::rtl::math::approxCeil(fVal
);
1038 if (fmod(fVal
, 2.0) == 0.0)
1043 fVal
= ::rtl::math::approxFloor(fVal
);
1044 if (fmod(fVal
, 2.0) == 0.0)
1050 void ScInterpreter::ScArcTan2()
1052 if ( MustHaveParamCount( GetByte(), 2 ) )
1054 double nVal2
= GetDouble();
1055 double nVal1
= GetDouble();
1056 PushDouble(atan2(nVal2
, nVal1
));
1060 void ScInterpreter::ScLog()
1062 sal_uInt8 nParamCount
= GetByte();
1063 if ( MustHaveParamCount( nParamCount
, 1, 2 ) )
1066 if (nParamCount
== 2)
1067 nBase
= GetDouble();
1070 double nVal
= GetDouble();
1071 if (nVal
> 0.0 && nBase
> 0.0 && nBase
!= 1.0)
1072 PushDouble(log(nVal
) / log(nBase
));
1074 PushIllegalArgument();
1078 void ScInterpreter::ScLn()
1080 double fVal
= GetDouble();
1082 PushDouble(log(fVal
));
1084 PushIllegalArgument();
1087 void ScInterpreter::ScLog10()
1089 double fVal
= GetDouble();
1091 PushDouble(log10(fVal
));
1093 PushIllegalArgument();
1096 void ScInterpreter::ScNPV()
1098 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1099 short nParamCount
= GetByte();
1100 if ( MustHaveParamCount( nParamCount
, 2, 31 ) )
1103 //We turn the stack upside down!
1104 FormulaToken
* pTemp
[ 31 ];
1105 for( short i
= 0; i
< nParamCount
; i
++ )
1106 pTemp
[ i
] = pStack
[ sp
- i
- 1 ];
1107 memcpy( &pStack
[ sp
- nParamCount
], pTemp
, nParamCount
* sizeof( FormulaToken
* ) );
1108 if (nGlobalError
== 0)
1110 double nCount
= 1.0;
1111 double nInterest
= GetDouble();
1113 size_t nRefInList
= 0;
1115 while (nParamCount
-- > 0)
1117 switch (GetStackType())
1121 nVal
+= (GetDouble() / pow(1.0 + nInterest
, (double)nCount
));
1128 PopSingleRef( aAdr
);
1129 ScRefCellValue aCell
;
1130 aCell
.assign(*pDok
, aAdr
);
1131 if (!aCell
.hasEmptyValue() && aCell
.hasNumeric())
1133 double nCellVal
= GetCellValue(aAdr
, aCell
);
1134 nVal
+= (nCellVal
/ pow(1.0 + nInterest
, (double)nCount
));
1142 sal_uInt16 nErr
= 0;
1144 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
1145 ScHorizontalValueIterator
aValIter( pDok
, aRange
);
1146 while ((nErr
== 0) && aValIter
.GetNext(nCellVal
, nErr
))
1148 nVal
+= (nCellVal
/ pow(1.0 + nInterest
, (double)nCount
));
1155 default : SetError(errIllegalParameter
); break;
1163 void ScInterpreter::ScIRR()
1166 nFuncFmtType
= css::util::NumberFormat::PERCENT
;
1167 sal_uInt8 nParamCount
= GetByte();
1168 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
1170 if (nParamCount
== 2)
1171 fEstimated
= GetDouble();
1174 sal_uInt16 sPos
= sp
; //memory the position of the stack
1177 if (fEstimated
== -1.0)
1178 x
= 0.1; // default result for divion by zero
1180 x
= fEstimated
; // startvalue
1181 switch (GetStackType())
1187 PushIllegalParameter();
1191 const sal_uInt16 nIterationsMax
= 20;
1192 sal_uInt16 nItCount
= 0;
1194 while (fEps
> SCdEpsilon
&& nItCount
< nIterationsMax
)
1195 { // Newtons method:
1196 sp
= sPos
; // reset stack
1197 double nCount
= 0.0;
1199 double fDenom
= 0.0;
1200 sal_uInt16 nErr
= 0;
1201 PopDoubleRef( aRange
);
1202 ScValueIterator
aValIter(pDok
, aRange
, mnSubTotalFlags
);
1203 if (aValIter
.GetFirst(fValue
, nErr
))
1205 fNom
+= fValue
/ pow(1.0+x
,(double)nCount
);
1206 fDenom
+= -nCount
* fValue
/ pow(1.0+x
,nCount
+1.0);
1208 while ((nErr
== 0) && aValIter
.GetNext(fValue
, nErr
))
1210 fNom
+= fValue
/ pow(1.0+x
,(double)nCount
);
1211 fDenom
+= -nCount
* fValue
/ pow(1.0+x
,nCount
+1.0);
1216 double xNew
= x
- fNom
/ fDenom
; // x(i+1) = x(i)-f(x(i))/f'(x(i))
1218 fEps
= fabs(xNew
- x
);
1221 if (fEstimated
== 0.0 && fabs(x
) < SCdEpsilon
)
1222 x
= 0.0; // adjust to zero
1223 if (fEps
< SCdEpsilon
)
1226 PushError( errNoConvergence
);
1229 void ScInterpreter::ScMIRR()
1230 { // range_of_values ; rate_invest ; rate_reinvest
1231 nFuncFmtType
= css::util::NumberFormat::PERCENT
;
1232 if( MustHaveParamCount( GetByte(), 3 ) )
1234 double fRate1_reinvest
= GetDouble() + 1;
1235 double fRate1_invest
= GetDouble() + 1;
1238 PopDoubleRef( aRange
);
1241 PushError( nGlobalError
);
1244 double fNPV_reinvest
= 0.0;
1245 double fPow_reinvest
= 1.0;
1246 double fNPV_invest
= 0.0;
1247 double fPow_invest
= 1.0;
1248 ScValueIterator
aValIter( pDok
, aRange
, mnSubTotalFlags
);
1250 sal_uLong nCount
= 0;
1251 sal_uInt16 nIterError
= 0;
1253 bool bLoop
= aValIter
.GetFirst( fCellValue
, nIterError
);
1256 if( fCellValue
> 0.0 ) // reinvestments
1257 fNPV_reinvest
+= fCellValue
* fPow_reinvest
;
1258 else if( fCellValue
< 0.0 ) // investments
1259 fNPV_invest
+= fCellValue
* fPow_invest
;
1260 fPow_reinvest
/= fRate1_reinvest
;
1261 fPow_invest
/= fRate1_invest
;
1264 bLoop
= aValIter
.GetNext( fCellValue
, nIterError
);
1267 PushError( nIterError
);
1270 double fResult
= -fNPV_reinvest
/ fNPV_invest
;
1271 fResult
*= pow( fRate1_reinvest
, (double) nCount
- 1 );
1272 fResult
= pow( fResult
, div( 1.0, (nCount
- 1)) );
1273 PushDouble( fResult
- 1.0 );
1279 void ScInterpreter::ScISPMT()
1280 { // rate ; period ; total_periods ; invest
1281 if( MustHaveParamCount( GetByte(), 4 ) )
1283 double fInvest
= GetDouble();
1284 double fTotal
= GetDouble();
1285 double fPeriod
= GetDouble();
1286 double fRate
= GetDouble();
1289 PushError( nGlobalError
);
1291 PushDouble( fInvest
* fRate
* (fPeriod
/ fTotal
- 1.0) );
1296 double ScInterpreter::ScGetBw(double fInterest
, double fZzr
, double fRmz
,
1297 double fZw
, double fF
)
1300 if (fInterest
== 0.0)
1301 fBw
= fZw
+ fRmz
* fZzr
;
1303 fBw
= (fZw
* pow(1.0 + fInterest
, -fZzr
))
1304 + (fRmz
* (1.0 - pow(1.0 + fInterest
, -fZzr
+ 1.0)) / fInterest
)
1307 fBw
= (fZw
* pow(1.0 + fInterest
, -fZzr
))
1308 + (fRmz
* (1.0 - pow(1.0 + fInterest
, -fZzr
)) / fInterest
);
1312 void ScInterpreter::ScPV()
1314 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1315 double nRmz
, nZzr
, nInterest
, nZw
= 0, nFlag
= 0;
1316 sal_uInt8 nParamCount
= GetByte();
1317 if ( !MustHaveParamCount( nParamCount
, 3, 5 ) )
1319 if (nParamCount
== 5)
1320 nFlag
= GetDouble();
1321 if (nParamCount
>= 4)
1325 nInterest
= GetDouble();
1326 PushDouble(ScGetBw(nInterest
, nZzr
, nRmz
, nZw
, nFlag
));
1329 void ScInterpreter::ScSYD()
1331 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1332 if ( MustHaveParamCount( GetByte(), 4 ) )
1334 double nZr
= GetDouble();
1335 double nTimeLength
= GetDouble();
1336 double nRest
= GetDouble();
1337 double nValue
= GetDouble();
1338 double nDia
= ((nValue
- nRest
) * (nTimeLength
- nZr
+ 1.0)) /
1339 ((nTimeLength
* (nTimeLength
+ 1.0)) / 2.0);
1344 double ScInterpreter::ScGetGDA(double fValue
, double fRest
, double fTimeLength
,
1345 double fPeriod
, double fFactor
)
1347 double fGda
, fInterest
, fOldValue
, fNewValue
;
1348 fInterest
= fFactor
/ fTimeLength
;
1349 if (fInterest
>= 1.0)
1358 fOldValue
= fValue
* pow(1.0 - fInterest
, fPeriod
- 1.0);
1359 fNewValue
= fValue
* pow(1.0 - fInterest
, fPeriod
);
1361 if (fNewValue
< fRest
)
1362 fGda
= fOldValue
- fRest
;
1364 fGda
= fOldValue
- fNewValue
;
1370 void ScInterpreter::ScDDB()
1372 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1373 sal_uInt8 nParamCount
= GetByte();
1374 if ( MustHaveParamCount( nParamCount
, 4, 5 ) )
1377 if (nParamCount
== 5)
1378 nFactor
= GetDouble();
1381 double nPeriod
= GetDouble();
1382 double nTimeLength
= GetDouble();
1383 double nRest
= GetDouble();
1384 double nValue
= GetDouble();
1385 if (nValue
< 0.0 || nRest
< 0.0 || nFactor
<= 0.0 || nRest
> nValue
1386 || nPeriod
< 1.0 || nPeriod
> nTimeLength
)
1387 PushIllegalArgument();
1389 PushDouble(ScGetGDA(nValue
, nRest
, nTimeLength
, nPeriod
, nFactor
));
1393 void ScInterpreter::ScDB()
1395 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1396 sal_uInt8 nParamCount
= GetByte();
1397 if ( !MustHaveParamCount( nParamCount
, 4, 5 ) )
1400 if (nParamCount
== 4)
1403 nMonths
= ::rtl::math::approxFloor(GetDouble());
1404 double nPeriod
= GetDouble();
1405 double nTimeLength
= GetDouble();
1406 double nRest
= GetDouble();
1407 double nValue
= GetDouble();
1408 if (nMonths
< 1.0 || nMonths
> 12.0 || nTimeLength
> 1200.0 || nRest
< 0.0 ||
1409 nPeriod
> (nTimeLength
+ 1.0) || nRest
> nValue
|| nValue
< 0.0)
1411 PushIllegalArgument();
1414 double nOffRate
= 1.0 - pow(nRest
/ nValue
, 1.0 / nTimeLength
);
1415 nOffRate
= ::rtl::math::approxFloor((nOffRate
* 1000.0) + 0.5) / 1000.0;
1416 double nFirstOffRate
= nValue
* nOffRate
* nMonths
/ 12.0;
1418 if (::rtl::math::approxFloor(nPeriod
) == 1)
1419 nGda2
= nFirstOffRate
;
1422 double nSumOffRate
= nFirstOffRate
;
1423 double nMin
= nTimeLength
;
1424 if (nMin
> nPeriod
) nMin
= nPeriod
;
1425 sal_uInt16 iMax
= (sal_uInt16
)::rtl::math::approxFloor(nMin
);
1426 for (sal_uInt16 i
= 2; i
<= iMax
; i
++)
1428 nGda2
= (nValue
- nSumOffRate
) * nOffRate
;
1429 nSumOffRate
+= nGda2
;
1431 if (nPeriod
> nTimeLength
)
1432 nGda2
= ((nValue
- nSumOffRate
) * nOffRate
* (12.0 - nMonths
)) / 12.0;
1437 double ScInterpreter::ScInterVDB(double fValue
,double fRest
,double fTimeLength
,
1438 double fTimeLength1
,double fPeriod
,double fFactor
)
1441 double fIntEnd
= ::rtl::math::approxCeil(fPeriod
);
1442 sal_uLong nLoopEnd
= (sal_uLong
) fIntEnd
;
1445 double fSalvageValue
= fValue
- fRest
;
1446 bool bNowLia
= false;
1451 for ( i
= 1; i
<= nLoopEnd
; i
++)
1455 fGda
= ScGetGDA(fValue
, fRest
, fTimeLength
, (double) i
, fFactor
);
1456 fLia
= fSalvageValue
/ (fTimeLength1
- (double) (i
-1));
1466 fSalvageValue
-= fGda
;
1475 fTerm
*= ( fPeriod
+ 1.0 - fIntEnd
);
1482 inline double DblMin( double a
, double b
)
1484 return (a
< b
) ? a
: b
;
1487 void ScInterpreter::ScVDB()
1489 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1490 sal_uInt8 nParamCount
= GetByte();
1491 if ( MustHaveParamCount( nParamCount
, 5, 7 ) )
1493 double fValue
, fRest
, fTimeLength
, fStart
, fEnd
, fFactor
, fVdb
= 0.0;
1495 if (nParamCount
== 7)
1499 if (nParamCount
>= 6)
1500 fFactor
= GetDouble();
1504 fStart
= GetDouble();
1505 fTimeLength
= GetDouble();
1506 fRest
= GetDouble();
1507 fValue
= GetDouble();
1508 if (fStart
< 0.0 || fEnd
< fStart
|| fEnd
> fTimeLength
|| fValue
< 0.0
1509 || fRest
> fValue
|| fFactor
<= 0.0)
1510 PushIllegalArgument();
1513 double fIntStart
= ::rtl::math::approxFloor(fStart
);
1514 double fIntEnd
= ::rtl::math::approxCeil(fEnd
);
1515 sal_uLong nLoopStart
= (sal_uLong
) fIntStart
;
1516 sal_uLong nLoopEnd
= (sal_uLong
) fIntEnd
;
1521 for (sal_uLong i
= nLoopStart
+ 1; i
<= nLoopEnd
; i
++)
1523 double fTerm
= ScGetGDA(fValue
, fRest
, fTimeLength
, (double) i
, fFactor
);
1525 //respect partial period in the Beginning/ End:
1526 if ( i
== nLoopStart
+1 )
1527 fTerm
*= ( DblMin( fEnd
, fIntStart
+ 1.0 ) - fStart
);
1528 else if ( i
== nLoopEnd
)
1529 fTerm
*= ( fEnd
+ 1.0 - fIntEnd
);
1537 double fTimeLength1
=fTimeLength
;
1539 //@ The question of all questions: 'Is this right'
1540 if(!::rtl::math::approxEqual(fStart
,::rtl::math::approxFloor(fStart
)))
1544 if(fStart
>fTimeLength
/2 || ::rtl::math::approxEqual(fStart
,fTimeLength
/2))
1546 double fPart
=fStart
-fTimeLength
/2;
1547 fStart
=fTimeLength
/2;
1554 fValue
-=ScInterVDB(fValue
,fRest
,fTimeLength
,fTimeLength1
,fStart
,fFactor
);
1555 fVdb
=ScInterVDB(fValue
,fRest
,fTimeLength
,fTimeLength
-fStart
,fEnd
-fStart
,fFactor
);
1562 void ScInterpreter::ScDuration()
1564 if ( MustHaveParamCount( GetByte(), 3 ) )
1566 double nFuture
= GetDouble();
1567 double nPresent
= GetDouble();
1568 double nInterest
= GetDouble();
1569 PushDouble(log(nFuture
/ nPresent
) / boost::math::log1p(nInterest
));
1573 void ScInterpreter::ScSLN()
1575 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1576 if ( MustHaveParamCount( GetByte(), 3 ) )
1578 double nTimeLength
= GetDouble();
1579 double nRest
= GetDouble();
1580 double nValue
= GetDouble();
1581 PushDouble((nValue
- nRest
) / nTimeLength
);
1585 double ScInterpreter::ScGetRmz(double fRate
, double fNper
, double fPv
,
1586 double fFv
, double fPaytype
)
1590 fPayment
= (fPv
+ fFv
) / fNper
;
1593 if (fPaytype
> 0.0) // payment in advance
1594 fPayment
= (fFv
+ fPv
* exp( fNper
* ::rtl::math::log1p(fRate
) ) ) * fRate
/
1595 (::rtl::math::expm1( (fNper
+ 1) * ::rtl::math::log1p(fRate
) ) - fRate
);
1596 else // payment in arrear
1597 fPayment
= (fFv
+ fPv
* exp(fNper
* ::rtl::math::log1p(fRate
) ) ) * fRate
/
1598 ::rtl::math::expm1( fNper
* ::rtl::math::log1p(fRate
) );
1603 void ScInterpreter::ScPMT()
1605 double nInterest
, nZzr
, nBw
, nZw
= 0, nFlag
= 0;
1606 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1607 sal_uInt8 nParamCount
= GetByte();
1608 if ( !MustHaveParamCount( nParamCount
, 3, 5 ) )
1610 if (nParamCount
== 5)
1611 nFlag
= GetDouble();
1612 if (nParamCount
>= 4)
1616 nInterest
= GetDouble();
1617 PushDouble(ScGetRmz(nInterest
, nZzr
, nBw
, nZw
, nFlag
));
1620 void ScInterpreter::ScRRI()
1622 nFuncFmtType
= css::util::NumberFormat::PERCENT
;
1623 if ( MustHaveParamCount( GetByte(), 3 ) )
1625 double nValueInFuture
= GetDouble();
1626 double nValueNow
= GetDouble();
1627 double nSpaceOfTime
= GetDouble();
1628 PushDouble(pow(nValueInFuture
/ nValueNow
, 1.0 / nSpaceOfTime
) - 1.0);
1632 double ScInterpreter::ScGetZw(double fInterest
, double fZzr
, double fRmz
,
1633 double fBw
, double fF
)
1636 if (fInterest
== 0.0)
1637 fZw
= fBw
+ fRmz
* fZzr
;
1640 double fTerm
= pow(1.0 + fInterest
, fZzr
);
1642 fZw
= fBw
* fTerm
+ fRmz
*(1.0 + fInterest
)*(fTerm
- 1.0)/fInterest
;
1644 fZw
= fBw
* fTerm
+ fRmz
*(fTerm
- 1.0)/fInterest
;
1649 void ScInterpreter::ScFV()
1651 double nInterest
, nZzr
, nRmz
, nBw
= 0, nFlag
= 0;
1652 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1653 sal_uInt8 nParamCount
= GetByte();
1654 if ( !MustHaveParamCount( nParamCount
, 3, 5 ) )
1656 if (nParamCount
== 5)
1657 nFlag
= GetDouble();
1658 if (nParamCount
>= 4)
1662 nInterest
= GetDouble();
1663 PushDouble(ScGetZw(nInterest
, nZzr
, nRmz
, nBw
, nFlag
));
1666 void ScInterpreter::ScNper()
1668 double nInterest
, nRmz
, nBw
, nZw
= 0, nFlag
= 0;
1669 sal_uInt8 nParamCount
= GetByte();
1670 if ( !MustHaveParamCount( nParamCount
, 3, 5 ) )
1672 if (nParamCount
== 5)
1673 nFlag
= GetDouble();
1674 if (nParamCount
>= 4)
1678 nInterest
= GetDouble();
1679 if (nInterest
== 0.0)
1680 PushDouble(-(nBw
+ nZw
)/nRmz
);
1681 else if (nFlag
> 0.0)
1682 PushDouble(log(-(nInterest
*nZw
-nRmz
*(1.0+nInterest
))/(nInterest
*nBw
+nRmz
*(1.0+nInterest
)))
1683 /boost::math::log1p(nInterest
));
1685 PushDouble(log(-(nInterest
*nZw
-nRmz
)/(nInterest
*nBw
+nRmz
))/boost::math::log1p(nInterest
));
1688 bool ScInterpreter::RateIteration( double fNper
, double fPayment
, double fPv
,
1689 double fFv
, double fPayType
, double & fGuess
)
1691 // See also #i15090#
1692 // Newton-Raphson method: x(i+1) = x(i) - f(x(i)) / f'(x(i))
1693 // This solution handles integer and non-integer values of Nper different.
1694 // If ODFF will constraint Nper to integer, the distinction of cases can be
1695 // removed; only the integer-part is needed then.
1696 bool bValid
= true, bFound
= false;
1697 double fX
, fXnew
, fTerm
, fTermDerivation
;
1698 double fGeoSeries
, fGeoSeriesDerivation
;
1699 const sal_uInt16 nIterationsMax
= 150;
1700 sal_uInt16 nCount
= 0;
1701 const double fEpsilonSmall
= 1.0E-14;
1702 // convert any fPayType situation to fPayType == zero situation
1703 fFv
= fFv
- fPayment
* fPayType
;
1704 fPv
= fPv
+ fPayment
* fPayType
;
1705 if (fNper
== ::rtl::math::round( fNper
, 0, rtl_math_RoundingMode_Corrected
))
1706 { // Nper is an integer value
1708 double fPowN
, fPowNminus1
; // for (1.0+fX)^Nper and (1.0+fX)^(Nper-1)
1709 while (!bFound
&& nCount
< nIterationsMax
)
1711 fPowNminus1
= pow( 1.0+fX
, fNper
-1.0);
1712 fPowN
= fPowNminus1
* (1.0+fX
);
1713 if (rtl::math::approxEqual( fabs(fX
), 0.0))
1716 fGeoSeriesDerivation
= fNper
* (fNper
-1.0)/2.0;
1720 fGeoSeries
= (fPowN
-1.0)/fX
;
1721 fGeoSeriesDerivation
= fNper
* fPowNminus1
/ fX
- fGeoSeries
/ fX
;
1723 fTerm
= fFv
+ fPv
*fPowN
+ fPayment
* fGeoSeries
;
1724 fTermDerivation
= fPv
* fNper
* fPowNminus1
+ fPayment
* fGeoSeriesDerivation
;
1725 if (fabs(fTerm
) < fEpsilonSmall
)
1726 bFound
= true; // will catch root which is at an extreme
1729 if (rtl::math::approxEqual( fabs(fTermDerivation
), 0.0))
1730 fXnew
= fX
+ 1.1 * SCdEpsilon
; // move away from zero slope
1732 fXnew
= fX
- fTerm
/ fTermDerivation
;
1734 // more accuracy not possible in oscillating cases
1735 bFound
= (fabs(fXnew
- fX
) < SCdEpsilon
);
1739 // Gnumeric returns roots < -1, Excel gives an error in that cases,
1740 // ODFF says nothing about it. Enable the statement, if you want Excel's
1742 //bValid =(fX >=-1.0);
1743 // Update 2013-06-17: Gnumeric (v1.12.2) doesn't return roots <= -1
1745 bValid
= (fX
> -1.0);
1748 { // Nper is not an integer value.
1749 fX
= (fGuess
< -1.0) ? -1.0 : fGuess
; // start with a valid fX
1750 while (bValid
&& !bFound
&& nCount
< nIterationsMax
)
1752 if (rtl::math::approxEqual( fabs(fX
), 0.0))
1755 fGeoSeriesDerivation
= fNper
* (fNper
-1.0)/2.0;
1759 fGeoSeries
= (pow( 1.0+fX
, fNper
) - 1.0) / fX
;
1760 fGeoSeriesDerivation
= fNper
* pow( 1.0+fX
, fNper
-1.0) / fX
- fGeoSeries
/ fX
;
1762 fTerm
= fFv
+ fPv
*pow(1.0 + fX
,fNper
)+ fPayment
* fGeoSeries
;
1763 fTermDerivation
= fPv
* fNper
* pow( 1.0+fX
, fNper
-1.0) + fPayment
* fGeoSeriesDerivation
;
1764 if (fabs(fTerm
) < fEpsilonSmall
)
1765 bFound
= true; // will catch root which is at an extreme
1768 if (rtl::math::approxEqual( fabs(fTermDerivation
), 0.0))
1769 fXnew
= fX
+ 1.1 * SCdEpsilon
; // move away from zero slope
1771 fXnew
= fX
- fTerm
/ fTermDerivation
;
1773 // more accuracy not possible in oscillating cases
1774 bFound
= (fabs(fXnew
- fX
) < SCdEpsilon
);
1776 bValid
= (fX
>= -1.0); // otherwise pow(1.0+fX,fNper) will fail
1780 fGuess
= fX
; // return approximate root
1781 return bValid
&& bFound
;
1784 // In Calc UI it is the function RATE(Nper;Pmt;Pv;Fv;Type;Guess)
1785 void ScInterpreter::ScRate()
1787 double fPv
, fPayment
, fNper
;
1788 // defaults for missing arguments, see ODFF spec
1789 double fFv
= 0, fPayType
= 0, fGuess
= 0.1, fOrigGuess
= 0.1;
1791 bool bDefaultGuess
= true;
1792 nFuncFmtType
= css::util::NumberFormat::PERCENT
;
1793 sal_uInt8 nParamCount
= GetByte();
1794 if ( !MustHaveParamCount( nParamCount
, 3, 6 ) )
1796 if (nParamCount
== 6)
1798 fOrigGuess
= fGuess
= GetDouble();
1799 bDefaultGuess
= false;
1801 if (nParamCount
>= 5)
1802 fPayType
= GetDouble();
1803 if (nParamCount
>= 4)
1806 fPayment
= GetDouble();
1807 fNper
= GetDouble();
1808 if (fNper
<= 0.0) // constraint from ODFF spec
1810 PushIllegalArgument();
1813 // other values for fPayType might be meaningful,
1814 // ODFF spec is not clear yet, enable statement if you want only 0 and 1
1815 //if (fPayType != 0.0) fPayType = 1.0;
1816 bValid
= RateIteration(fNper
, fPayment
, fPv
, fFv
, fPayType
, fGuess
);
1819 /* TODO: try also for specified guess values, not only default? As is,
1820 * a specified 0.1 guess may be error result but a default 0.1 guess
1821 * may succeed. On the other hand, using a different guess value than
1822 * the specified one may not be desired, even if that didn't match. */
1825 /* TODO: this is rather ugly, instead of looping over different
1826 * guess values and doing a Newton goal seek for each we could
1827 * first insert the values into the RATE equation to obtain a set
1828 * of y values and then do a bisecting goal seek, possibly using
1829 * different algorithms. */
1830 double fX
= fOrigGuess
;
1831 for (int nStep
= 2; nStep
<= 10 && !bValid
; ++nStep
)
1833 fGuess
= fX
* nStep
;
1834 bValid
= RateIteration( fNper
, fPayment
, fPv
, fFv
, fPayType
, fGuess
);
1837 fGuess
= fX
/ nStep
;
1838 bValid
= RateIteration( fNper
, fPayment
, fPv
, fFv
, fPayType
, fGuess
);
1843 SetError(errNoConvergence
);
1848 double ScInterpreter::ScGetCompoundInterest(double fInterest
, double fZr
, double fZzr
, double fBw
,
1849 double fZw
, double fF
, double& fRmz
)
1851 fRmz
= ScGetRmz(fInterest
, fZzr
, fBw
, fZw
, fF
); // fuer kapz auch bei fZr == 1
1852 double fCompoundInterest
;
1853 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1857 fCompoundInterest
= 0.0;
1859 fCompoundInterest
= -fBw
;
1864 fCompoundInterest
= ScGetZw(fInterest
, fZr
-2.0, fRmz
, fBw
, 1.0) - fRmz
;
1866 fCompoundInterest
= ScGetZw(fInterest
, fZr
-1.0, fRmz
, fBw
, 0.0);
1868 return fCompoundInterest
* fInterest
;
1871 void ScInterpreter::ScIpmt()
1873 double nInterest
, nZr
, nZzr
, nBw
, nZw
= 0, nFlag
= 0;
1874 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1875 sal_uInt8 nParamCount
= GetByte();
1876 if ( !MustHaveParamCount( nParamCount
, 4, 6 ) )
1878 if (nParamCount
== 6)
1879 nFlag
= GetDouble();
1880 if (nParamCount
>= 5)
1885 nInterest
= GetDouble();
1886 if (nZr
< 1.0 || nZr
> nZzr
)
1887 PushIllegalArgument();
1891 PushDouble(ScGetCompoundInterest(nInterest
, nZr
, nZzr
, nBw
, nZw
, nFlag
, nRmz
));
1895 void ScInterpreter::ScPpmt()
1897 double nInterest
, nZr
, nZzr
, nBw
, nZw
= 0, nFlag
= 0;
1898 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1899 sal_uInt8 nParamCount
= GetByte();
1900 if ( !MustHaveParamCount( nParamCount
, 4, 6 ) )
1902 if (nParamCount
== 6)
1903 nFlag
= GetDouble();
1904 if (nParamCount
>= 5)
1909 nInterest
= GetDouble();
1910 if (nZr
< 1.0 || nZr
> nZzr
)
1911 PushIllegalArgument();
1915 double nInterestz
= ScGetCompoundInterest(nInterest
, nZr
, nZzr
, nBw
, nZw
, nFlag
, nRmz
);
1916 PushDouble(nRmz
- nInterestz
);
1920 void ScInterpreter::ScCumIpmt()
1922 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1923 if ( MustHaveParamCount( GetByte(), 6 ) )
1925 double fInterest
, fZzr
, fBw
, fStart
, fEnd
, fF
;
1927 fEnd
= ::rtl::math::approxFloor(GetDouble());
1928 fStart
= ::rtl::math::approxFloor(GetDouble());
1931 fInterest
= GetDouble();
1932 if (fStart
< 1.0 || fEnd
< fStart
|| fInterest
<= 0.0 ||
1933 fEnd
> fZzr
|| fZzr
<= 0.0 || fBw
<= 0.0)
1934 PushIllegalArgument();
1937 sal_uLong nStart
= (sal_uLong
) fStart
;
1938 sal_uLong nEnd
= (sal_uLong
) fEnd
;
1939 double fRmz
= ScGetRmz(fInterest
, fZzr
, fBw
, 0.0, fF
);
1940 double fCompoundInterest
= 0.0;
1944 fCompoundInterest
= -fBw
;
1947 for (sal_uLong i
= nStart
; i
<= nEnd
; i
++)
1950 fCompoundInterest
+= ScGetZw(fInterest
, (double)(i
-2), fRmz
, fBw
, 1.0) - fRmz
;
1952 fCompoundInterest
+= ScGetZw(fInterest
, (double)(i
-1), fRmz
, fBw
, 0.0);
1954 fCompoundInterest
*= fInterest
;
1955 PushDouble(fCompoundInterest
);
1960 void ScInterpreter::ScCumPrinc()
1962 nFuncFmtType
= css::util::NumberFormat::CURRENCY
;
1963 if ( MustHaveParamCount( GetByte(), 6 ) )
1965 double fInterest
, fZzr
, fBw
, fStart
, fEnd
, fF
;
1967 fEnd
= ::rtl::math::approxFloor(GetDouble());
1968 fStart
= ::rtl::math::approxFloor(GetDouble());
1971 fInterest
= GetDouble();
1972 if (fStart
< 1.0 || fEnd
< fStart
|| fInterest
<= 0.0 ||
1973 fEnd
> fZzr
|| fZzr
<= 0.0 || fBw
<= 0.0)
1974 PushIllegalArgument();
1977 double fRmz
= ScGetRmz(fInterest
, fZzr
, fBw
, 0.0, fF
);
1979 sal_uLong nStart
= (sal_uLong
) fStart
;
1980 sal_uLong nEnd
= (sal_uLong
) fEnd
;
1984 fKapZ
= fRmz
+ fBw
* fInterest
;
1989 for (sal_uLong i
= nStart
; i
<= nEnd
; i
++)
1992 fKapZ
+= fRmz
- (ScGetZw(fInterest
, (double)(i
-2), fRmz
, fBw
, 1.0) - fRmz
) * fInterest
;
1994 fKapZ
+= fRmz
- ScGetZw(fInterest
, (double)(i
-1), fRmz
, fBw
, 0.0) * fInterest
;
2001 void ScInterpreter::ScEffective()
2003 nFuncFmtType
= css::util::NumberFormat::PERCENT
;
2004 if ( MustHaveParamCount( GetByte(), 2 ) )
2006 double fPeriods
= GetDouble();
2007 double fNominal
= GetDouble();
2008 if (fPeriods
< 1.0 || fNominal
<= 0.0)
2009 PushIllegalArgument();
2012 fPeriods
= ::rtl::math::approxFloor(fPeriods
);
2013 PushDouble(pow(1.0 + fNominal
/fPeriods
, fPeriods
) - 1.0);
2018 void ScInterpreter::ScNominal()
2020 nFuncFmtType
= css::util::NumberFormat::PERCENT
;
2021 if ( MustHaveParamCount( GetByte(), 2 ) )
2023 double fPeriods
= GetDouble();
2024 double fEffective
= GetDouble();
2025 if (fPeriods
< 1.0 || fEffective
<= 0.0)
2026 PushIllegalArgument();
2029 fPeriods
= ::rtl::math::approxFloor(fPeriods
);
2030 PushDouble( (pow(fEffective
+ 1.0, 1.0 / fPeriods
) - 1.0) * fPeriods
);
2035 void ScInterpreter::ScMod()
2037 if ( MustHaveParamCount( GetByte(), 2 ) )
2039 double fVal2
= GetDouble(); // Denominator
2040 double fVal1
= GetDouble(); // Numerator
2041 if (fVal2
== floor(fVal2
)) // a pure integral number stored in double
2043 double fResult
= fmod(fVal1
,fVal2
);
2044 if ( (fResult
!= 0.0) &&
2045 ((fVal1
> 0.0 && fVal2
< 0.0) || (fVal1
< 0.0 && fVal2
> 0.0)))
2047 PushDouble( fResult
);
2051 PushDouble( ::rtl::math::approxSub( fVal1
,
2052 ::rtl::math::approxFloor(fVal1
/ fVal2
) * fVal2
));
2057 void ScInterpreter::ScIntersect()
2059 formula::FormulaTokenRef p2nd
= PopToken();
2060 formula::FormulaTokenRef p1st
= PopToken();
2062 if (nGlobalError
|| !p2nd
|| !p1st
)
2064 PushIllegalArgument();
2068 StackVar sv1
= p1st
->GetType();
2069 StackVar sv2
= p2nd
->GetType();
2070 if ((sv1
!= svSingleRef
&& sv1
!= svDoubleRef
&& sv1
!= svRefList
) ||
2071 (sv2
!= svSingleRef
&& sv2
!= svDoubleRef
&& sv2
!= svRefList
))
2073 PushIllegalArgument();
2077 formula::FormulaToken
* x1
= p1st
.get();
2078 formula::FormulaToken
* x2
= p2nd
.get();
2079 if (sv1
== svRefList
|| sv2
== svRefList
)
2081 // Now this is a bit nasty but it simplifies things, and having
2082 // intersections with lists isn't too common, if at all..
2083 // Convert a reference to list.
2084 formula::FormulaToken
* xt
[2] = { x1
, x2
};
2085 StackVar sv
[2] = { sv1
, sv2
};
2086 for (size_t i
=0; i
<2; ++i
)
2088 if (sv
[i
] == svSingleRef
)
2090 ScComplexRefData aRef
;
2091 aRef
.Ref1
= aRef
.Ref2
= *xt
[i
]->GetSingleRef();
2092 xt
[i
] = new ScRefListToken
;
2093 xt
[i
]->GetRefList()->push_back( aRef
);
2095 else if (sv
[i
] == svDoubleRef
)
2097 ScComplexRefData aRef
= *xt
[i
]->GetDoubleRef();
2098 xt
[i
] = new ScRefListToken
;
2099 xt
[i
]->GetRefList()->push_back( aRef
);
2102 x1
= xt
[0], x2
= xt
[1];
2104 ScTokenRef xRes
= new ScRefListToken
;
2105 ScRefList
* pRefList
= xRes
->GetRefList();
2106 ScRefList::const_iterator
end1( x1
->GetRefList()->end());
2107 ScRefList::const_iterator
end2( x2
->GetRefList()->end());
2108 for (ScRefList::const_iterator
it1( x1
->GetRefList()->begin());
2111 const ScAddress
& r11
= (*it1
).Ref1
.toAbs(aPos
);
2112 const ScAddress
& r12
= (*it1
).Ref2
.toAbs(aPos
);
2113 for (ScRefList::const_iterator
it2( x2
->GetRefList()->begin());
2116 const ScAddress
& r21
= (*it2
).Ref1
.toAbs(aPos
);
2117 const ScAddress
& r22
= (*it2
).Ref2
.toAbs(aPos
);
2118 SCCOL nCol1
= ::std::max( r11
.Col(), r21
.Col());
2119 SCROW nRow1
= ::std::max( r11
.Row(), r21
.Row());
2120 SCTAB nTab1
= ::std::max( r11
.Tab(), r21
.Tab());
2121 SCCOL nCol2
= ::std::min( r12
.Col(), r22
.Col());
2122 SCROW nRow2
= ::std::min( r12
.Row(), r22
.Row());
2123 SCTAB nTab2
= ::std::min( r12
.Tab(), r22
.Tab());
2124 if (nCol2
< nCol1
|| nRow2
< nRow1
|| nTab2
< nTab1
)
2128 ScComplexRefData aRef
;
2129 aRef
.InitRange( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
2130 pRefList
->push_back( aRef
);
2134 size_t n
= pRefList
->size();
2136 PushError( errNoRef
);
2139 const ScComplexRefData
& rRef
= (*pRefList
)[0];
2140 if (rRef
.Ref1
== rRef
.Ref2
)
2141 PushTempToken( new ScSingleRefToken( rRef
.Ref1
));
2143 PushTempToken( new ScDoubleRefToken( rRef
));
2146 PushTempToken( xRes
.get());
2150 formula::FormulaToken
* pt
[2] = { x1
, x2
};
2151 StackVar sv
[2] = { sv1
, sv2
};
2152 SCCOL nC1
[2], nC2
[2];
2153 SCROW nR1
[2], nR2
[2];
2154 SCTAB nT1
[2], nT2
[2];
2155 for (size_t i
=0; i
<2; ++i
)
2163 const ScAddress
& r
= pt
[i
]->GetSingleRef()->toAbs(aPos
);
2168 if (sv
[i
] == svDoubleRef
)
2170 const ScAddress
& r
= pt
[i
]->GetSingleRef2()->toAbs(aPos
);
2184 ; // nothing, prevent compiler warning
2187 SCCOL nCol1
= ::std::max( nC1
[0], nC1
[1]);
2188 SCROW nRow1
= ::std::max( nR1
[0], nR1
[1]);
2189 SCTAB nTab1
= ::std::max( nT1
[0], nT1
[1]);
2190 SCCOL nCol2
= ::std::min( nC2
[0], nC2
[1]);
2191 SCROW nRow2
= ::std::min( nR2
[0], nR2
[1]);
2192 SCTAB nTab2
= ::std::min( nT2
[0], nT2
[1]);
2193 if (nCol2
< nCol1
|| nRow2
< nRow1
|| nTab2
< nTab1
)
2194 PushError( errNoRef
);
2195 else if (nCol2
== nCol1
&& nRow2
== nRow1
&& nTab2
== nTab1
)
2196 PushSingleRef( nCol1
, nRow1
, nTab1
);
2198 PushDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
2202 void ScInterpreter::ScRangeFunc()
2204 formula::FormulaTokenRef x2
= PopToken();
2205 formula::FormulaTokenRef x1
= PopToken();
2207 if (nGlobalError
|| !x2
|| !x1
)
2209 PushIllegalArgument();
2212 FormulaTokenRef xRes
= extendRangeReference( *x1
, *x2
, aPos
, false);
2214 PushIllegalArgument();
2216 PushTempToken( xRes
.get());
2219 void ScInterpreter::ScUnionFunc()
2221 formula::FormulaTokenRef p2nd
= PopToken();
2222 formula::FormulaTokenRef p1st
= PopToken();
2224 if (nGlobalError
|| !p2nd
|| !p1st
)
2226 PushIllegalArgument();
2230 StackVar sv1
= p1st
->GetType();
2231 StackVar sv2
= p2nd
->GetType();
2232 if ((sv1
!= svSingleRef
&& sv1
!= svDoubleRef
&& sv1
!= svRefList
) ||
2233 (sv2
!= svSingleRef
&& sv2
!= svDoubleRef
&& sv2
!= svRefList
))
2235 PushIllegalArgument();
2239 formula::FormulaToken
* x1
= p1st
.get();
2240 formula::FormulaToken
* x2
= p2nd
.get();
2243 // Append to an existing RefList if there is one.
2244 if (sv1
== svRefList
)
2247 sv1
= svUnknown
; // mark as handled
2249 else if (sv2
== svRefList
)
2252 sv2
= svUnknown
; // mark as handled
2255 xRes
= new ScRefListToken
;
2256 ScRefList
* pRes
= xRes
->GetRefList();
2257 formula::FormulaToken
* pt
[2] = { x1
, x2
};
2258 StackVar sv
[2] = { sv1
, sv2
};
2259 for (size_t i
=0; i
<2; ++i
)
2267 ScComplexRefData aRef
;
2268 aRef
.Ref1
= aRef
.Ref2
= *pt
[i
]->GetSingleRef();
2269 pRes
->push_back( aRef
);
2273 pRes
->push_back( *pt
[i
]->GetDoubleRef());
2277 const ScRefList
* p
= pt
[i
]->GetRefList();
2278 ScRefList::const_iterator
it( p
->begin());
2279 ScRefList::const_iterator
end( p
->end());
2280 for ( ; it
!= end
; ++it
)
2282 pRes
->push_back( *it
);
2287 ; // nothing, prevent compiler warning
2290 ValidateRef( *pRes
); // set #REF! if needed
2291 PushTempToken( xRes
.get());
2294 void ScInterpreter::ScCurrent()
2296 FormulaTokenRef
xTok( PopToken());
2299 PushTempToken( xTok
.get());
2300 PushTempToken( xTok
.get());
2303 PushError( errUnknownStackVariable
);
2306 void ScInterpreter::ScStyle()
2308 sal_uInt8 nParamCount
= GetByte();
2309 if (nParamCount
>= 1 && nParamCount
<= 3)
2311 OUString aStyle2
; // Template after timer
2312 if (nParamCount
>= 3)
2313 aStyle2
= GetString().getString();
2314 long nTimeOut
= 0; // timeout
2315 if (nParamCount
>= 2)
2316 nTimeOut
= (long)(GetDouble()*1000.0);
2317 OUString aStyle1
= GetString().getString(); // Template for immediate
2322 // Execute request to apply template
2323 if ( !pDok
->IsClipOrUndo() )
2325 SfxObjectShell
* pShell
= pDok
->GetDocumentShell();
2328 // notify object shell directly!
2330 ScRange
aRange(aPos
);
2331 ScAutoStyleHint
aHint( aRange
, aStyle1
, nTimeOut
, aStyle2
);
2332 pShell
->Broadcast( aHint
);
2339 PushIllegalParameter();
2342 static ScDdeLink
* lcl_GetDdeLink( sfx2::LinkManager
* pLinkMgr
,
2343 const OUString
& rA
, const OUString
& rT
, const OUString
& rI
, sal_uInt8 nM
)
2345 size_t nCount
= pLinkMgr
->GetLinks().size();
2346 for (size_t i
=0; i
<nCount
; i
++ )
2348 ::sfx2::SvBaseLink
* pBase
= *pLinkMgr
->GetLinks()[i
];
2349 if (pBase
->ISA(ScDdeLink
))
2351 ScDdeLink
* pLink
= static_cast<ScDdeLink
*>(pBase
);
2352 if ( pLink
->GetAppl() == rA
&&
2353 pLink
->GetTopic() == rT
&&
2354 pLink
->GetItem() == rI
&&
2355 pLink
->GetMode() == nM
)
2363 void ScInterpreter::ScDde()
2365 // application, file, scope
2366 // application, Topic, Item
2368 sal_uInt8 nParamCount
= GetByte();
2369 if ( MustHaveParamCount( nParamCount
, 3, 4 ) )
2371 sal_uInt8 nMode
= SC_DDE_DEFAULT
;
2372 if (nParamCount
== 4)
2373 nMode
= (sal_uInt8
) ::rtl::math::approxFloor(GetDouble());
2374 OUString aItem
= GetString().getString();
2375 OUString aTopic
= GetString().getString();
2376 OUString aAppl
= GetString().getString();
2378 if (nMode
> SC_DDE_TEXT
)
2379 nMode
= SC_DDE_DEFAULT
;
2381 // temporary documents (ScFunctionAccess) have no DocShell
2382 // and no LinkManager -> abort
2384 sfx2::LinkManager
* pLinkMgr
= pDok
->GetLinkManager();
2391 // Need to reinterpret after loading (build links)
2393 if ( rArr
.IsRecalcModeNormal() )
2394 rArr
.SetExclusiveRecalcModeOnLoad();
2396 // while the link ist not evaluated idle must be disabled (to avoid circular references)
2398 bool bOldEnabled
= pDok
->IsIdleEnabled();
2399 pDok
->EnableIdle(false);
2401 // Get/ Create link object
2403 ScDdeLink
* pLink
= lcl_GetDdeLink( pLinkMgr
, aAppl
, aTopic
, aItem
, nMode
);
2405 //TODO: Save Dde-links (in addition) more efficient at document !!!!!
2406 // ScDdeLink* pLink = pDok->GetDdeLink( aAppl, aTopic, aItem );
2408 bool bWasError
= ( pMyFormulaCell
&& pMyFormulaCell
->GetRawError() != 0 );
2412 pLink
= new ScDdeLink( pDok
, aAppl
, aTopic
, aItem
, nMode
);
2413 pLinkMgr
->InsertDDELink( pLink
, aAppl
, aTopic
, aItem
);
2414 if ( pLinkMgr
->GetLinks().size() == 1 ) // erster ?
2416 SfxBindings
* pBindings
= pDok
->GetViewBindings();
2418 pBindings
->Invalidate( SID_LINKS
); // Link-Manager enablen
2421 //TODO: evaluate asynchron ???
2422 pLink
->TryUpdate(); // TryUpdate doesn't call Update multiple times
2426 // StartListening after the Update to avoid circular references
2427 pMyFormulaCell
->StartListening( *pLink
);
2433 pMyFormulaCell
->StartListening( *pLink
);
2436 // If an new Error from Reschedule appears when the link is executed then reset the errorflag
2439 if ( pMyFormulaCell
&& pMyFormulaCell
->GetRawError() && !bWasError
)
2440 pMyFormulaCell
->SetErrCode(0);
2444 const ScMatrix
* pLinkMat
= pLink
->GetResult();
2448 pLinkMat
->GetDimensions(nC
, nR
);
2449 ScMatrixRef pNewMat
= GetNewMat( nC
, nR
);
2452 pLinkMat
->MatCopy(*pNewMat
); // copy
2453 PushMatrix( pNewMat
);
2456 PushIllegalArgument();
2461 pDok
->EnableIdle(bOldEnabled
);
2462 pLinkMgr
->CloseCachedComps();
2466 void ScInterpreter::ScBase()
2467 { // Value, Base [, MinLen]
2468 sal_uInt8 nParamCount
= GetByte();
2469 if ( MustHaveParamCount( nParamCount
, 2, 3 ) )
2471 static const sal_Unicode pDigits
[] = {
2472 '0','1','2','3','4','5','6','7','8','9',
2473 'A','B','C','D','E','F','G','H','I','J','K','L','M',
2474 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
2477 static const int nDigits
= (sizeof (pDigits
)/sizeof(pDigits
[0]))-1;
2479 if ( nParamCount
== 3 )
2481 double fLen
= ::rtl::math::approxFloor( GetDouble() );
2482 if ( 1.0 <= fLen
&& fLen
< SAL_MAX_UINT16
)
2483 nMinLen
= (sal_Int32
) fLen
;
2484 else if ( fLen
== 0.0 )
2487 nMinLen
= 0; // Error
2491 double fBase
= ::rtl::math::approxFloor( GetDouble() );
2492 double fVal
= ::rtl::math::approxFloor( GetDouble() );
2493 double fChars
= ((fVal
> 0.0 && fBase
> 0.0) ?
2494 (ceil( log( fVal
) / log( fBase
) ) + 2.0) :
2496 if ( fChars
>= SAL_MAX_UINT16
)
2497 nMinLen
= 0; // Error
2499 if ( !nGlobalError
&& nMinLen
&& 2 <= fBase
&& fBase
<= nDigits
&& 0 <= fVal
)
2501 const sal_Int32 nConstBuf
= 128;
2502 sal_Unicode aBuf
[nConstBuf
];
2503 sal_Int32 nBuf
= std::max
<sal_Int32
>( fChars
, nMinLen
+ 1 );
2504 sal_Unicode
* pBuf
= (nBuf
<= nConstBuf
? aBuf
: new sal_Unicode
[nBuf
]);
2505 for ( sal_Int32 j
= 0; j
< nBuf
; ++j
)
2509 sal_Unicode
* p
= pBuf
+ nBuf
- 1;
2511 if ( fVal
<= (sal_uLong
)(~0) )
2513 sal_uLong nVal
= (sal_uLong
) fVal
;
2514 sal_uLong nBase
= (sal_uLong
) fBase
;
2515 while ( nVal
&& p
> pBuf
)
2517 *--p
= pDigits
[ nVal
% nBase
];
2520 fVal
= (double) nVal
;
2525 while ( fVal
&& p
> pBuf
)
2527 //TODO: roundoff error starting with numbers greater than 2**48
2528 // double fDig = ::rtl::math::approxFloor( fmod( fVal, fBase ) );
2529 // a little bit better:
2530 double fInt
= ::rtl::math::approxFloor( fVal
/ fBase
);
2531 double fMult
= fInt
* fBase
;
2532 #if OSL_DEBUG_LEVEL > 1
2533 // =BASIS(1e308;36) => GPF with
2534 // nDig = (size_t) ::rtl::math::approxFloor( fVal - fMult );
2535 // in spite off previous test if fVal >= fMult
2536 double fDebug1
= fVal
- fMult
;
2537 // fVal := 7,5975311883090e+290
2538 // fMult := 7,5975311883090e+290
2539 // fDebug1 := 1,3848924157003e+275 <- RoundOff-Error
2540 // fVal != fMult, aber: ::rtl::math::approxEqual( fVal, fMult ) == TRUE
2541 double fDebug2
= ::rtl::math::approxSub( fVal
, fMult
);
2542 // und ::rtl::math::approxSub( fVal, fMult ) == 0
2543 double fDebug3
= ( fInt
? fVal
/ fInt
: 0.0 );
2545 // Actual after strange fDebug1 and fVal < fMult is fDebug2 == fBase, but
2546 // anyway it can't be compared, then bDirt is executed an everything is good...
2548 // prevent compiler warnings
2549 (void)fDebug1
; (void)fDebug2
; (void)fDebug3
;
2553 { // something is wrong there
2559 double fDig
= ::rtl::math::approxFloor( ::rtl::math::approxSub( fVal
, fMult
) );
2567 else if ( fDig
>= fBase
)
2568 nDig
= ((size_t) fBase
) - 1;
2570 nDig
= (size_t) fDig
;
2572 *--p
= pDigits
[ nDig
];
2577 PushError( errStringOverflow
);
2580 if ( nBuf
- (p
- pBuf
) <= nMinLen
)
2581 p
= pBuf
+ nBuf
- 1 - nMinLen
;
2582 PushStringBuffer( p
);
2588 PushIllegalArgument();
2592 void ScInterpreter::ScDecimal()
2594 if ( MustHaveParamCount( GetByte(), 2 ) )
2596 double fBase
= ::rtl::math::approxFloor( GetDouble() );
2597 OUString aStr
= GetString().getString();
2598 if ( !nGlobalError
&& 2 <= fBase
&& fBase
<= 36 )
2601 int nBase
= (int) fBase
;
2602 const sal_Unicode
* p
= aStr
.getStr();
2603 while ( *p
== ' ' || *p
== '\t' )
2604 p
++; // strip leading white space
2606 { // evtl. hex-prefix strippen
2607 if ( *p
== 'x' || *p
== 'X' )
2609 else if ( *p
== '0' && (*(p
+1) == 'x' || *(p
+1) == 'X') )
2615 if ( '0' <= *p
&& *p
<= '9' )
2617 else if ( 'A' <= *p
&& *p
<= 'Z' )
2618 n
= 10 + (*p
- 'A');
2619 else if ( 'a' <= *p
&& *p
<= 'z' )
2620 n
= 10 + (*p
- 'a');
2626 ( (nBase
== 2 && (*p
== 'b' || *p
== 'B'))
2627 ||(nBase
== 16 && (*p
== 'h' || *p
== 'H')) )
2629 ; // 101b and F00Dh are ok
2632 PushIllegalArgument();
2637 fVal
= fVal
* fBase
+ n
;
2644 PushIllegalArgument();
2648 void ScInterpreter::ScConvert()
2649 { // Value, FromUnit, ToUnit
2650 if ( MustHaveParamCount( GetByte(), 3 ) )
2652 OUString aToUnit
= GetString().getString();
2653 OUString aFromUnit
= GetString().getString();
2654 double fVal
= GetDouble();
2656 PushError( nGlobalError
);
2659 // first of all search for the given order; if it can't be found then search for the inverse
2661 if ( ScGlobal::GetUnitConverter()->GetValue( fConv
, aFromUnit
, aToUnit
) )
2662 PushDouble( fVal
* fConv
);
2663 else if ( ScGlobal::GetUnitConverter()->GetValue( fConv
, aToUnit
, aFromUnit
) )
2664 PushDouble( fVal
/ fConv
);
2671 void ScInterpreter::ScRoman()
2673 sal_uInt8 nParamCount
= GetByte();
2674 if( MustHaveParamCount( nParamCount
, 1, 2 ) )
2676 double fMode
= (nParamCount
== 2) ? ::rtl::math::approxFloor( GetDouble() ) : 0.0;
2677 double fVal
= ::rtl::math::approxFloor( GetDouble() );
2679 PushError( nGlobalError
);
2680 else if( (fMode
>= 0.0) && (fMode
< 5.0) && (fVal
>= 0.0) && (fVal
< 4000.0) )
2682 static const sal_Unicode pChars
[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
2683 static const sal_uInt16 pValues
[] = { 1000, 500, 100, 50, 10, 5, 1 };
2684 static const sal_uInt16 nMaxIndex
= (sal_uInt16
)((sizeof(pValues
)/sizeof(pValues
[0])) - 1);
2687 sal_uInt16 nVal
= (sal_uInt16
) fVal
;
2688 sal_uInt16 nMode
= (sal_uInt16
) fMode
;
2690 for( sal_uInt16 i
= 0; i
<= nMaxIndex
/ 2; i
++ )
2692 sal_uInt16 nIndex
= 2 * i
;
2693 sal_uInt16 nDigit
= nVal
/ pValues
[ nIndex
];
2695 if( (nDigit
% 5) == 4 )
2697 // assert can't happen with nVal<4000 precondition
2698 assert( ((nDigit
== 4) ? (nIndex
>= 1) : (nIndex
>= 2)));
2700 sal_uInt16 nIndex2
= (nDigit
== 4) ? nIndex
- 1 : nIndex
- 2;
2701 sal_uInt16 nSteps
= 0;
2702 while( (nSteps
< nMode
) && (nIndex
< nMaxIndex
) )
2705 if( pValues
[ nIndex2
] - pValues
[ nIndex
+ 1 ] <= nVal
)
2710 aRoman
+= OUString( pChars
[ nIndex
] );
2711 aRoman
+= OUString( pChars
[ nIndex2
] );
2712 nVal
= sal::static_int_cast
<sal_uInt16
>( nVal
+ pValues
[ nIndex
] );
2713 nVal
= sal::static_int_cast
<sal_uInt16
>( nVal
- pValues
[ nIndex2
] );
2719 // assert can't happen with nVal<4000 precondition
2720 assert( nIndex
>= 1 );
2721 aRoman
+= OUString( pChars
[ nIndex
- 1 ] );
2723 sal_Int32 nPad
= nDigit
% 5;
2726 OUStringBuffer
aBuf(aRoman
);
2727 comphelper::string::padToLength(aBuf
, aBuf
.getLength() + nPad
,
2729 aRoman
= aBuf
.makeStringAndClear();
2731 nVal
%= pValues
[ nIndex
];
2735 PushString( aRoman
);
2738 PushIllegalArgument();
2742 static bool lcl_GetArabicValue( sal_Unicode cChar
, sal_uInt16
& rnValue
, bool& rbIsDec
)
2746 case 'M': rnValue
= 1000; rbIsDec
= true; break;
2747 case 'D': rnValue
= 500; rbIsDec
= false; break;
2748 case 'C': rnValue
= 100; rbIsDec
= true; break;
2749 case 'L': rnValue
= 50; rbIsDec
= false; break;
2750 case 'X': rnValue
= 10; rbIsDec
= true; break;
2751 case 'V': rnValue
= 5; rbIsDec
= false; break;
2752 case 'I': rnValue
= 1; rbIsDec
= true; break;
2753 default: return false;
2758 void ScInterpreter::ScArabic()
2760 OUString aRoman
= GetString().getString();
2762 PushError( nGlobalError
);
2765 aRoman
= aRoman
.toAsciiUpperCase();
2767 sal_uInt16 nValue
= 0;
2768 sal_uInt16 nValidRest
= 3999;
2769 sal_Int32 nCharIndex
= 0;
2770 sal_Int32 nCharCount
= aRoman
.getLength();
2773 while( bValid
&& (nCharIndex
< nCharCount
) )
2775 sal_uInt16 nDigit1
= 0;
2776 sal_uInt16 nDigit2
= 0;
2777 bool bIsDec1
= false;
2778 bValid
= lcl_GetArabicValue( aRoman
[nCharIndex
], nDigit1
, bIsDec1
);
2779 if( bValid
&& (nCharIndex
+ 1 < nCharCount
) )
2781 bool bIsDec2
= false;
2782 bValid
= lcl_GetArabicValue( aRoman
[nCharIndex
+ 1], nDigit2
, bIsDec2
);
2786 if( nDigit1
>= nDigit2
)
2788 nValue
= sal::static_int_cast
<sal_uInt16
>( nValue
+ nDigit1
);
2789 nValidRest
%= (nDigit1
* (bIsDec1
? 5 : 2));
2790 bValid
= (nValidRest
>= nDigit1
);
2792 nValidRest
= sal::static_int_cast
<sal_uInt16
>( nValidRest
- nDigit1
);
2795 else if( nDigit1
* 2 != nDigit2
)
2797 sal_uInt16 nDiff
= nDigit2
- nDigit1
;
2798 nValue
= sal::static_int_cast
<sal_uInt16
>( nValue
+ nDiff
);
2799 bValid
= (nValidRest
>= nDiff
);
2801 nValidRest
= nDigit1
- 1;
2811 PushIllegalArgument();
2815 void ScInterpreter::ScHyperLink()
2817 sal_uInt8 nParamCount
= GetByte();
2818 if ( MustHaveParamCount( nParamCount
, 1, 2 ) )
2821 svl::SharedString aStr
;
2822 ScMatValType nResultType
= SC_MATVAL_STRING
;
2824 if ( nParamCount
== 2 )
2826 switch ( GetStackType() )
2830 nResultType
= SC_MATVAL_VALUE
;
2839 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2842 ScRefCellValue aCell
;
2843 aCell
.assign(*pDok
, aAdr
);
2844 if (aCell
.hasEmptyValue())
2845 nResultType
= SC_MATVAL_EMPTY
;
2848 sal_uInt16 nErr
= GetCellErrCode(aCell
);
2851 else if (aCell
.hasNumeric())
2853 fVal
= GetCellValue(aAdr
, aCell
);
2854 nResultType
= SC_MATVAL_VALUE
;
2857 GetCellString(aStr
, aCell
);
2862 nResultType
= GetDoubleOrStringFromMatrix( fVal
, aStr
);
2869 nResultType
= SC_MATVAL_VALUE
;
2873 SetError( errIllegalArgument
);
2876 svl::SharedString aUrl
= GetString();
2877 ScMatrixRef pResMat
= GetNewMat( 1, 2);
2880 fVal
= CreateDoubleError( nGlobalError
);
2881 nResultType
= SC_MATVAL_VALUE
;
2883 if (nParamCount
== 2 || nGlobalError
)
2885 if (ScMatrix::IsValueType( nResultType
))
2886 pResMat
->PutDouble( fVal
, 0);
2887 else if (ScMatrix::IsRealStringType( nResultType
))
2888 pResMat
->PutString(aStr
, 0);
2889 else // EmptyType, EmptyPathType, mimic xcl
2890 pResMat
->PutDouble( 0.0, 0 );
2893 pResMat
->PutString(aUrl
, 0);
2894 pResMat
->PutString(aUrl
, 1);
2895 bMatrixFormula
= true;
2896 PushMatrix(pResMat
);
2900 /** Resources at the website of the European Commission:
2901 http://ec.europa.eu/economy_finance/euro/adoption/conversion/
2902 http://ec.europa.eu/economy_finance/euro/countries/
2904 static bool lclConvertMoney( const OUString
& aSearchUnit
, double& rfRate
, int& rnDec
)
2908 const sal_Char
* pCurrText
;
2912 static const ConvertInfo aConvertTable
[] = {
2914 { "ATS", 13.7603, 2 },
2915 { "BEF", 40.3399, 0 },
2916 { "DEM", 1.95583, 2 },
2917 { "ESP", 166.386, 0 },
2918 { "FIM", 5.94573, 2 },
2919 { "FRF", 6.55957, 2 },
2920 { "IEP", 0.787564, 2 },
2921 { "ITL", 1936.27, 0 },
2922 { "LUF", 40.3399, 0 },
2923 { "NLG", 2.20371, 2 },
2924 { "PTE", 200.482, 2 },
2925 { "GRD", 340.750, 2 },
2926 { "SIT", 239.640, 2 },
2927 { "MTL", 0.429300, 2 },
2928 { "CYP", 0.585274, 2 },
2929 { "SKK", 30.1260, 2 },
2930 { "EEK", 15.6466, 2 },
2931 { "LVL", 0.702804, 2 },
2932 { "LTL", 3.45280, 2 }
2935 static const size_t nConversionCount
= sizeof( aConvertTable
) / sizeof( aConvertTable
[0] );
2936 for ( size_t i
= 0; i
< nConversionCount
; ++i
)
2937 if ( aSearchUnit
.equalsIgnoreAsciiCaseAscii( aConvertTable
[i
].pCurrText
) )
2939 rfRate
= aConvertTable
[i
].fRate
;
2940 rnDec
= aConvertTable
[i
].nDec
;
2946 void ScInterpreter::ScEuroConvert()
2947 { //Value, FromUnit, ToUnit[, FullPrecision, [TriangulationPrecision]]
2948 sal_uInt8 nParamCount
= GetByte();
2949 if ( MustHaveParamCount( nParamCount
, 3, 5 ) )
2951 double nPrecision
= 0.0;
2952 if ( nParamCount
== 5 )
2954 nPrecision
= ::rtl::math::approxFloor(GetDouble());
2955 if ( nPrecision
< 3 )
2957 PushIllegalArgument();
2961 bool bFullPrecision
= false;
2962 if ( nParamCount
>= 4 )
2963 bFullPrecision
= GetBool();
2964 OUString aToUnit
= GetString().getString();
2965 OUString aFromUnit
= GetString().getString();
2966 double fVal
= GetDouble();
2968 PushError( nGlobalError
);
2975 OUString
aEur( "EUR");
2976 if ( lclConvertMoney( aFromUnit
, fFromRate
, nFromDec
)
2977 && lclConvertMoney( aToUnit
, fToRate
, nToDec
) )
2980 if ( aFromUnit
.equalsIgnoreAsciiCase( aToUnit
) )
2984 if ( aFromUnit
.equalsIgnoreAsciiCase( aEur
) )
2985 fRes
= fVal
* fToRate
;
2988 double fIntermediate
= fVal
/ fFromRate
;
2990 fIntermediate
= ::rtl::math::round( fIntermediate
,
2992 fRes
= fIntermediate
* fToRate
;
2994 if ( !bFullPrecision
)
2995 fRes
= ::rtl::math::round( fRes
, nToDec
);
3000 PushIllegalArgument();
3006 #define UTF8_TH_0 "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214"
3007 #define UTF8_TH_1 "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207"
3008 #define UTF8_TH_2 "\340\270\252\340\270\255\340\270\207"
3009 #define UTF8_TH_3 "\340\270\252\340\270\262\340\270\241"
3010 #define UTF8_TH_4 "\340\270\252\340\270\265\340\271\210"
3011 #define UTF8_TH_5 "\340\270\253\340\271\211\340\270\262"
3012 #define UTF8_TH_6 "\340\270\253\340\270\201"
3013 #define UTF8_TH_7 "\340\271\200\340\270\210\340\271\207\340\270\224"
3014 #define UTF8_TH_8 "\340\271\201\340\270\233\340\270\224"
3015 #define UTF8_TH_9 "\340\271\200\340\270\201\340\271\211\340\270\262"
3016 #define UTF8_TH_10 "\340\270\252\340\270\264\340\270\232"
3017 #define UTF8_TH_11 "\340\271\200\340\270\255\340\271\207\340\270\224"
3018 #define UTF8_TH_20 "\340\270\242\340\270\265\340\271\210"
3019 #define UTF8_TH_1E2 "\340\270\243\340\271\211\340\270\255\340\270\242"
3020 #define UTF8_TH_1E3 "\340\270\236\340\270\261\340\270\231"
3021 #define UTF8_TH_1E4 "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231"
3022 #define UTF8_TH_1E5 "\340\271\201\340\270\252\340\270\231"
3023 #define UTF8_TH_1E6 "\340\270\245\340\271\211\340\270\262\340\270\231"
3024 #define UTF8_TH_DOT0 "\340\270\226\340\271\211\340\270\247\340\270\231"
3025 #define UTF8_TH_BAHT "\340\270\232\340\270\262\340\270\227"
3026 #define UTF8_TH_SATANG "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214"
3027 #define UTF8_TH_MINUS "\340\270\245\340\270\232"
3032 inline void lclSplitBlock( double& rfInt
, sal_Int32
& rnBlock
, double fValue
, double fSize
)
3034 rnBlock
= static_cast< sal_Int32
>( modf( (fValue
+ 0.1) / fSize
, &rfInt
) * fSize
+ 0.1 );
3037 /** Appends a digit (0 to 9) to the passed string. */
3038 void lclAppendDigit( OStringBuffer
& rText
, sal_Int32 nDigit
)
3042 case 0: rText
.append( UTF8_TH_0
); break;
3043 case 1: rText
.append( UTF8_TH_1
); break;
3044 case 2: rText
.append( UTF8_TH_2
); break;
3045 case 3: rText
.append( UTF8_TH_3
); break;
3046 case 4: rText
.append( UTF8_TH_4
); break;
3047 case 5: rText
.append( UTF8_TH_5
); break;
3048 case 6: rText
.append( UTF8_TH_6
); break;
3049 case 7: rText
.append( UTF8_TH_7
); break;
3050 case 8: rText
.append( UTF8_TH_8
); break;
3051 case 9: rText
.append( UTF8_TH_9
); break;
3052 default: OSL_FAIL( "lclAppendDigit - illegal digit" );
3056 /** Appends a value raised to a power of 10: nDigit*10^nPow10.
3057 @param nDigit A digit in the range from 1 to 9.
3058 @param nPow10 A value in the range from 2 to 5.
3060 void lclAppendPow10( OStringBuffer
& rText
, sal_Int32 nDigit
, sal_Int32 nPow10
)
3062 OSL_ENSURE( (1 <= nDigit
) && (nDigit
<= 9), "lclAppendPow10 - illegal digit" );
3063 lclAppendDigit( rText
, nDigit
);
3066 case 2: rText
.append( UTF8_TH_1E2
); break;
3067 case 3: rText
.append( UTF8_TH_1E3
); break;
3068 case 4: rText
.append( UTF8_TH_1E4
); break;
3069 case 5: rText
.append( UTF8_TH_1E5
); break;
3070 default: OSL_FAIL( "lclAppendPow10 - illegal power" );
3074 /** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */
3075 void lclAppendBlock( OStringBuffer
& rText
, sal_Int32 nValue
)
3077 OSL_ENSURE( (1 <= nValue
) && (nValue
<= 999999), "lclAppendBlock - illegal value" );
3078 if( nValue
>= 100000 )
3080 lclAppendPow10( rText
, nValue
/ 100000, 5 );
3083 if( nValue
>= 10000 )
3085 lclAppendPow10( rText
, nValue
/ 10000, 4 );
3088 if( nValue
>= 1000 )
3090 lclAppendPow10( rText
, nValue
/ 1000, 3 );
3095 lclAppendPow10( rText
, nValue
/ 100, 2 );
3100 sal_Int32 nTen
= nValue
/ 10;
3101 sal_Int32 nOne
= nValue
% 10;
3105 lclAppendDigit( rText
, nTen
);
3106 else if( nTen
== 2 )
3107 rText
.append( UTF8_TH_20
);
3108 rText
.append( UTF8_TH_10
);
3110 if( (nTen
> 0) && (nOne
== 1) )
3111 rText
.append( UTF8_TH_11
);
3113 lclAppendDigit( rText
, nOne
);
3119 void ScInterpreter::ScBahtText()
3121 sal_uInt8 nParamCount
= GetByte();
3122 if ( MustHaveParamCount( nParamCount
, 1 ) )
3124 double fValue
= GetDouble();
3127 PushError( nGlobalError
);
3132 bool bMinus
= fValue
< 0.0;
3133 fValue
= fabs( fValue
);
3135 // round to 2 digits after decimal point, fValue contains Satang as integer
3136 fValue
= ::rtl::math::approxFloor( fValue
* 100.0 + 0.5 );
3138 // split Baht and Satang
3140 sal_Int32 nSatang
= 0;
3141 lclSplitBlock( fBaht
, nSatang
, fValue
, 100.0 );
3143 OStringBuffer aText
;
3145 // generate text for Baht value
3149 aText
.append( UTF8_TH_0
);
3151 else while( fBaht
> 0.0 )
3153 OStringBuffer aBlock
;
3154 sal_Int32 nBlock
= 0;
3155 lclSplitBlock( fBaht
, nBlock
, fBaht
, 1.0e6
);
3157 lclAppendBlock( aBlock
, nBlock
);
3158 // add leading "million", if there will come more blocks
3160 aBlock
.insert( 0, OString(UTF8_TH_1E6
) );
3162 aText
.insert(0, aBlock
.makeStringAndClear());
3164 if (!aText
.isEmpty())
3165 aText
.append( UTF8_TH_BAHT
);
3167 // generate text for Satang value
3170 aText
.append( UTF8_TH_DOT0
);
3174 lclAppendBlock( aText
, nSatang
);
3175 aText
.append( UTF8_TH_SATANG
);
3178 // add the minus sign
3180 aText
.insert( 0, OString( UTF8_TH_MINUS
) );
3182 PushString( OStringToOUString(aText
.makeStringAndClear(), RTL_TEXTENCODING_UTF8
) );
3186 void ScInterpreter::ScGetPivotData()
3188 sal_uInt8 nParamCount
= GetByte();
3190 if (!MustHaveParamCount(nParamCount
, 2, 30) || (nParamCount
% 2) == 1)
3192 PushError(errNoRef
);
3196 bool bOldSyntax
= false;
3197 if (nParamCount
== 2)
3199 // if the first parameter is a ref, assume old syntax
3200 StackVar eFirstType
= GetStackType(2);
3201 if (eFirstType
== svSingleRef
|| eFirstType
== svDoubleRef
)
3205 std::vector
<sheet::DataPilotFieldFilter
> aFilters
;
3206 OUString aDataFieldName
;
3211 aDataFieldName
= GetString().getString();
3213 switch (GetStackType())
3216 PopDoubleRef(aBlock
);
3221 PopSingleRef(aAddr
);
3226 PushError(errNoRef
);
3232 // Standard syntax: separate name/value pairs
3234 sal_uInt16 nFilterCount
= nParamCount
/ 2 - 1;
3235 aFilters
.resize(nFilterCount
);
3237 sal_uInt16 i
= nFilterCount
;
3240 //TODO: should allow numeric constraint values
3241 aFilters
[i
].MatchValue
= GetString().getString();
3242 aFilters
[i
].FieldName
= GetString().getString();
3245 switch (GetStackType())
3248 PopDoubleRef(aBlock
);
3253 PopSingleRef(aAddr
);
3258 PushError(errNoRef
);
3262 aDataFieldName
= GetString().getString(); // First parameter is data field name.
3265 // NOTE : MS Excel docs claim to use the 'most recent' which is not
3266 // exactly the same as what we do in ScDocument::GetDPAtBlock
3267 // However we do need to use GetDPABlock
3268 ScDPObject
* pDPObj
= pDok
->GetDPAtBlock(aBlock
);
3271 PushError(errNoRef
);
3277 OUString aFilterStr
= aDataFieldName
;
3278 std::vector
<sheet::GeneralFunction
> aFilterFuncs
;
3279 if (!pDPObj
->ParseFilters(aDataFieldName
, aFilters
, aFilterFuncs
, aFilterStr
))
3281 PushError(errNoRef
);
3285 // TODO : For now, we ignore filter functions since we couldn't find a
3286 // live example of how they are supposed to be used. We'll support
3287 // this again once we come across a real-world example.
3290 double fVal
= pDPObj
->GetPivotData(aDataFieldName
, aFilters
);
3291 if (rtl::math::isNan(fVal
))
3293 PushError(errNoRef
);
3299 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */