@@ -193,6 +193,124 @@ namespace te_builtins
193193 te_parser::te_nan;
194194 }
195195
196+ // / @warning This version of round emulates Excel's behavior of supporting
197+ // / negative decimal places (e.g., ROUND(21.5, -1) = 20). Be aware
198+ // / of that if using this function outside of TinyExpr++.
199+ [[nodiscard]]
200+ static te_typete_round (te_type val, te_type decimalPlaces)// NOLINT
201+ {
202+ const bool useNegativeRound{ decimalPlaces <0 };
203+ const size_t adjustedDecimalPlaces{ !std::isfinite (decimalPlaces) ?
204+ 0 :
205+ static_cast <size_t >(std::abs (decimalPlaces)) };
206+
207+ const auto decimalPostition =static_cast <te_type>(std::pow (10 , adjustedDecimalPlaces));
208+ if (!std::isfinite (decimalPostition))
209+ {
210+ return te_parser::te_nan;
211+ }
212+ constexpr te_type ROUND_EPSILON{0.5 };// NOLINT
213+
214+ if (!useNegativeRound)
215+ {
216+ if (val <0 )
217+ {
218+ return (decimalPostition ==0 ) ?
219+ std::ceil (val - ROUND_EPSILON) :
220+ std::ceil (static_cast <te_type>(val * decimalPostition) - ROUND_EPSILON) /
221+ decimalPostition;
222+ }
223+ return (decimalPostition ==0 ) ?
224+ std::floor (val + ROUND_EPSILON) :
225+ std::floor (static_cast <te_type>(val * decimalPostition) + ROUND_EPSILON) /
226+ decimalPostition;
227+ }
228+ // ROUND(21.5, -1) = 20
229+ if (val <0 )
230+ {
231+ return std::ceil (static_cast <te_type>(val / decimalPostition) - ROUND_EPSILON) *
232+ decimalPostition;
233+ }
234+ return std::floor (static_cast <te_type>(val / decimalPostition) + ROUND_EPSILON) *
235+ decimalPostition;
236+ }
237+
238+ [[nodiscard]]
239+ static te_typete_nominal (te_type effectiveRate, te_type periods)
240+ {
241+ if (periods <1 || effectiveRate <=0 )
242+ {
243+ return te_parser::te_nan;
244+ }
245+ return periods * (std::pow (1 + effectiveRate, (1 / periods)) -1 );
246+ }
247+
248+ [[nodiscard]]
249+ static te_typete_effect (te_type nomicalRate, te_type periods)
250+ {
251+ if (periods <1 || nomicalRate <=0 )
252+ {
253+ return te_parser::te_nan;
254+ }
255+ return std::pow (1 + (nomicalRate / periods), periods) -1 ;
256+ }
257+
258+ [[nodiscard]]
259+ static te_typete_asset_depreciation (te_type cost, te_type salvage,
260+ te_type life, te_type period,
261+ te_type month)
262+ {
263+ if (!std::isfinite (month))
264+ {
265+ month =12 ;
266+ }
267+ else if (month <1 || month >12 || life <=0 || cost <=0 || period <1 )
268+ {
269+ return te_parser::te_nan;
270+ }
271+
272+ te_type intPrefix;
273+ te_type mantissa =std::modf (life, &intPrefix) *100 ;
274+ if (mantissa >0 )
275+ {
276+ return te_parser::te_nan;
277+ }
278+ mantissa =std::modf (period, &intPrefix) *100 ;
279+ if (mantissa >0 )
280+ {
281+ return te_parser::te_nan;
282+ }
283+
284+ // month gets rounded down in spreadsheet programs
285+ month =std::floor (static_cast <te_type>(month));
286+
287+ // we just verified that this are integral, but round down to fully ensure that
288+ life =std::floor (static_cast <te_type>(life));
289+ period =std::floor (static_cast <te_type>(period));
290+
291+ // rate gets clipped to three-decimal precision according to Excel docs
292+ const auto rate =te_round (1 - (std::pow ((salvage / cost), (1 / life))),3 );
293+ if (period ==1 )
294+ {
295+ return cost * rate * (month /12 );
296+ }
297+ else
298+ {
299+ te_type priorDepreciation{0.0 };
300+ te_type costAfterDepreciation{ cost };
301+ for (uint64_t i =1 ; i <static_cast <uint64_t >(period) -1 ; ++i)
302+ {
303+ auto depreciation = (costAfterDepreciation * rate);
304+ priorDepreciation += depreciation;
305+ costAfterDepreciation -= depreciation;
306+ }
307+ priorDepreciation += costAfterDepreciation * rate * (month /12 );
308+ return (period == life +1 ) ?
309+ ((cost - priorDepreciation) * rate * (12 - month)) /12 :
310+ (cost - priorDepreciation) * rate;
311+ }
312+ }
313+
196314 [[nodiscard]]
197315constexpr static te_typete_pi ()noexcept
198316 {
@@ -437,48 +555,6 @@ namespace te_builtins
437555return te_divide (total,static_cast <te_type>(validN));
438556 }
439557
440- // / @warning This version of round emulates Excel's behavior of supporting
441- // / negative decimal places (e.g., ROUND(21.5, -1) = 20). Be aware
442- // / of that if using this function outside of TinyExpr++.
443- [[nodiscard]]
444- static te_typete_round (te_type val, te_type decimalPlaces)// NOLINT
445- {
446- const bool useNegativeRound{ decimalPlaces <0 };
447- const size_t adjustedDecimalPlaces{ !std::isfinite (decimalPlaces) ?
448- 0 :
449- static_cast <size_t >(std::abs (decimalPlaces)) };
450-
451- const auto decimalPostition =static_cast <te_type>(std::pow (10 , adjustedDecimalPlaces));
452- if (!std::isfinite (decimalPostition))
453- {
454- return te_parser::te_nan;
455- }
456- constexpr te_type ROUND_EPSILON{0.5 };// NOLINT
457-
458- if (!useNegativeRound)
459- {
460- if (val <0 )
461- {
462- return (decimalPostition ==0 ) ?
463- std::ceil (val - ROUND_EPSILON) :
464- std::ceil (static_cast <te_type>(val * decimalPostition) - ROUND_EPSILON) /
465- decimalPostition;
466- }
467- return (decimalPostition ==0 ) ?
468- std::floor (val + ROUND_EPSILON) :
469- std::floor (static_cast <te_type>(val * decimalPostition) + ROUND_EPSILON) /
470- decimalPostition;
471- }
472- // ROUND(21.5, -1) = 20
473- if (val <0 )
474- {
475- return std::ceil (static_cast <te_type>(val / decimalPostition) - ROUND_EPSILON) *
476- decimalPostition;
477- }
478- return std::floor (static_cast <te_type>(val / decimalPostition) + ROUND_EPSILON) *
479- decimalPostition;
480- }
481-
482558// Combinations (without repetition)
483559 [[nodiscard]]
484560static te_typete_ncr (te_type val1, te_type val2)noexcept
@@ -1368,7 +1444,9 @@ const std::set<te_variable> te_parser::m_functions = { // NOLINT
13681444 {" cos" ,static_cast <te_fun1>(te_builtins::te_cos), TE_PURE },
13691445 {" cosh" ,static_cast <te_fun1>(te_builtins::te_cosh), TE_PURE },
13701446 {" cot" ,static_cast <te_fun1>(te_builtins::te_cot), TE_PURE },
1447+ {" db" ,static_cast <te_fun5>(te_builtins::te_asset_depreciation), TE_PURE },
13711448 {" e" ,static_cast <te_fun0>(te_builtins::te_e), TE_PURE },
1449+ {" effect" ,static_cast <te_fun2>(te_builtins::te_effect), TE_PURE },
13721450 {" even" ,static_cast <te_fun1>(te_builtins::te_even), TE_PURE },
13731451 {" exp" ,static_cast <te_fun1>(te_builtins::te_exp), TE_PURE },
13741452 {" fac" ,static_cast <te_fun1>(te_builtins::te_fac), TE_PURE },
@@ -1390,6 +1468,7 @@ const std::set<te_variable> te_parser::m_functions = { // NOLINT
13901468 {" mod" ,static_cast <te_fun2>(te_builtins::te_modulus), TE_PURE },
13911469 {" nan" ,static_cast <te_fun0>(te_builtins::te_nan_value), TE_PURE },
13921470 {" ncr" ,static_cast <te_fun2>(te_builtins::te_ncr), TE_PURE },
1471+ {" nominal" ,static_cast <te_fun2>(te_builtins::te_nominal), TE_PURE },
13931472 {" not" ,static_cast <te_fun1>(te_builtins::te_not), TE_PURE },
13941473 {" npr" ,static_cast <te_fun2>(te_builtins::te_npr), TE_PURE },
13951474 {" odd" ,static_cast <te_fun1>(te_builtins::te_odd), TE_PURE },