diff --git a/parser/mpFuncNonCmplx.cpp b/parser/mpFuncNonCmplx.cpp index 4b10329..1d206a5 100644 --- a/parser/mpFuncNonCmplx.cpp +++ b/parser/mpFuncNonCmplx.cpp @@ -33,6 +33,7 @@ */ #include "mpFuncNonCmplx.h" +#include "mpFuncRound.h" //--- Standard includes ---------------------------------------------------- #include @@ -48,16 +49,6 @@ MUP_NAMESPACE_START -//------------------------------------------------------------------------------ -// -// Auxiliary Functions -// -//------------------------------------------------------------------------------ -double round(long_double_type number, int_type precision) { - int_type decimals = std::pow(10, precision); - return (std::round(number * decimals)) / decimals; -} - //------------------------------------------------------------------------------ // // @@ -111,7 +102,6 @@ double round(long_double_type number, int_type precision) { MUP_UNARY_FUNC(FunExp, "exp", std::exp, "exp(x) - e to the power of x") // number functions MUP_UNARY_FUNC(FunAbs, "abs", std::fabs, "abs(x) - absolute value of x") - MUP_UNARY_FUNC(FunRound, "round", std::round, "round(x) - round the value of x to its nearest integer") #undef MUP_UNARY_FUNC #define MUP_BINARY_FUNC(CLASS, IDENT, FUNC, DESC) \ @@ -138,7 +128,6 @@ double round(long_double_type number, int_type precision) { MUP_BINARY_FUNC(FunHypot, "hypot", std::hypot, "hypot(x, y) - compute the length of the vector x,y") MUP_BINARY_FUNC(FunAtan2, "atan2", std::atan2, "arcus tangens with quadrant fix") MUP_BINARY_FUNC(FunFmod, "fmod", std::fmod, "fmod(x, y) - floating point remainder of x / y") - MUP_BINARY_FUNC(FunRoundDecimal, "round_decimal", round, "round_decimal(x, y) - round the x number considering y precision") MUP_BINARY_FUNC(FunRemainder, "remainder", std::remainder, "remainder(x, y) - IEEE remainder of x / y") #undef MUP_BINARY_FUNC diff --git a/parser/mpFuncNonCmplx.h b/parser/mpFuncNonCmplx.h index a07327a..4b91fd1 100644 --- a/parser/mpFuncNonCmplx.h +++ b/parser/mpFuncNonCmplx.h @@ -81,7 +81,6 @@ MUP_NAMESPACE_START MUP_UNARY_FUNC_DEF(FunExp) // number functions MUP_UNARY_FUNC_DEF(FunAbs) - MUP_UNARY_FUNC_DEF(FunRound) #undef MUP_UNARY_FUNC_DEF #define MUP_BINARY_FUNC_DEF(CLASS) \ @@ -98,7 +97,6 @@ MUP_NAMESPACE_START MUP_BINARY_FUNC_DEF(FunHypot) MUP_BINARY_FUNC_DEF(FunAtan2) MUP_BINARY_FUNC_DEF(FunFmod) - MUP_BINARY_FUNC_DEF(FunRoundDecimal) MUP_BINARY_FUNC_DEF(FunRemainder) #undef MUP_BINARY_FUNC_DEF diff --git a/parser/mpFuncRound.cpp b/parser/mpFuncRound.cpp new file mode 100644 index 0000000..47fd18c --- /dev/null +++ b/parser/mpFuncRound.cpp @@ -0,0 +1,94 @@ +#include "mpFuncRound.h" + +#include + +#include "mpError.h" +#include "mpValue.h" + +MUP_NAMESPACE_START + +namespace +{ + void eval_round(ICallback &callback, + ptr_val_type &ret, + const ptr_val_type *args, + int argc, + bool has_precision) + { + int required_argc = has_precision ? 2 : 1; + if (argc < required_argc) { + throw ParserError(ErrorContext(ecTOO_FEW_PARAMS, + callback.GetExprPos(), + callback.GetIdent())); + } else if (argc > required_argc + 1) { + throw ParserError(ErrorContext(ecTOO_MANY_PARAMS, + callback.GetExprPos(), + callback.GetIdent())); + } + + int_type scale = 1; + if (has_precision) { + int_type precision = args[1]->GetFloat(); + scale = std::pow(10, precision); + } + + float_type value = args[0]->GetFloat() * scale; + if (argc == required_argc) { + *ret = std::round(value) / scale; + return; + } + + string_type direction = args[required_argc]->GetString(); + if (direction == _T("up")) { + *ret = std::ceil(value) / scale; + } else if (direction == _T("down")) { + *ret = std::floor(value) / scale; + } else { + ErrorContext err(ecINVALID_PARAMETER, + callback.GetExprPos(), + callback.GetIdent()); + err.Arg = required_argc + 1; + throw ParserError(err); + } + } +} + +FunRound::FunRound() + :ICallback(cmFUNC, _T("round"), -1) +{} + +void FunRound::Eval(ptr_val_type &ret, const ptr_val_type *a_pArg, int a_iArgc) +{ + eval_round(*this, ret, a_pArg, a_iArgc, false); +} + +const char_type* FunRound::GetDesc() const +{ + return _T("round(x[, direction]) - round x normally, up or down"); +} + +IToken* FunRound::Clone() const +{ + return new FunRound(*this); +} + +FunRoundDecimal::FunRoundDecimal() + :ICallback(cmFUNC, _T("round_decimal"), -1) +{} + +void FunRoundDecimal::Eval(ptr_val_type &ret, const ptr_val_type *a_pArg, int a_iArgc) +{ + eval_round(*this, ret, a_pArg, a_iArgc, true); +} + +const char_type* FunRoundDecimal::GetDesc() const +{ + return _T("round_decimal(x, precision[, direction]) - round x at the given precision"); +} + +IToken* FunRoundDecimal::Clone() const +{ + return new FunRoundDecimal(*this); +} + +MUP_NAMESPACE_END diff --git a/parser/mpFuncRound.h b/parser/mpFuncRound.h new file mode 100644 index 0000000..739930f --- /dev/null +++ b/parser/mpFuncRound.h @@ -0,0 +1,28 @@ +#ifndef MUP_FUNC_ROUND_H +#define MUP_FUNC_ROUND_H + +#include "mpICallback.h" + +MUP_NAMESPACE_START + +class FunRound : public ICallback +{ +public: + FunRound(); + virtual void Eval(ptr_val_type &ret, const ptr_val_type *a_pArg, int a_iArgc) override; + virtual const char_type* GetDesc() const override; + virtual IToken* Clone() const override; +}; + +class FunRoundDecimal : public ICallback +{ +public: + FunRoundDecimal(); + virtual void Eval(ptr_val_type &ret, const ptr_val_type *a_pArg, int a_iArgc) override; + virtual const char_type* GetDesc() const override; + virtual IToken* Clone() const override; +}; + +MUP_NAMESPACE_END + +#endif diff --git a/parser/mpPackageNonCmplx.cpp b/parser/mpPackageNonCmplx.cpp index 6b84dc1..a4dc3fc 100644 --- a/parser/mpPackageNonCmplx.cpp +++ b/parser/mpPackageNonCmplx.cpp @@ -32,6 +32,7 @@ #include "mpParserBase.h" #include "mpFuncNonCmplx.h" +#include "mpFuncRound.h" #include "mpOprtNonCmplx.h" #include "mpOprtBinCommon.h" diff --git a/tests.sh b/tests.sh index 8fdf8e3..734e085 100755 --- a/tests.sh +++ b/tests.sh @@ -65,6 +65,10 @@ test_eval "10.5 / 5.25" "2" test_eval "abs(-5)" "5" test_eval "log10(10)" "1" test_eval "round(4.4)" "4" +test_eval 'round(10.1, "up")' "11" +test_eval 'round(10.9, "down")' "10" +test_eval 'round(-10.9, "up")' "-10" +test_eval 'round(-10.1, "down")' "-11" test_eval "(3^3)^2" "729" test_eval "3^(3^(2))" "19683" test_eval "10!" "3628800" @@ -75,6 +79,10 @@ test_eval "max(1, 2) + min(3, 4) + sum(5, 6)" "16" test_eval "avg(9, 9.8, 10)" "9.6" test_eval "pow(2, 3)" "8" test_eval "round_decimal(4.559, 2)" "4.56" +test_eval 'round_decimal(10.11, 1, "up")' "10.2" +test_eval 'round_decimal(10.19, 1, "down")' "10.1" +test_eval 'round_decimal(-10.19, 1, "up")' "-10.1" +test_eval 'round_decimal(-10.11, 1, "down")' "-10.2" # Conditional tests test_eval "4 > 2 ? \"bigger\" : \"smaller\"" "\"bigger\""