From: Serge Petrenko <sergepetrenko@tarantool.org> To: vdavydov.dev@gmail.com Cc: tarantool-patches@freelists.org, Serge Petrenko <sergepetrenko@tarantool.org> Subject: [PATCH] decimal: add methods truncate and set_scale Date: Fri, 19 Jul 2019 14:21:36 +0300 [thread overview] Message-ID: <20190719112136.23483-1-sergepetrenko@tarantool.org> (raw) This patch adds 2 methods to decimal library and lua module: truncate will remove all trailing fractional zeros and set_scale will either perform rounding or append excess fractional zeros. Closes #4372 @TarantoolBot document Title: document 2 new functions in decimal Lua module 2 new functions are added to the decimal module: `decimal.truncate()` and `decimal.set_scale()` `decimal.truncate()` removes any trailing fractional zeros from the number: ``` tarantool> a = dec.new('123.45570000') --- ... tarantool> decimal.truncate(a) --- - '123.4557' ... ``` `decimal.set_scale()` will round the number to a given scale, if it is less than the number scale. Otherwise it will append trailing fractional zeros, so that the resulting number scale will be the same as the given one. ``` tarantool> a = dec.new(123.45) --- ... tarantool> dec.set_scale(a,1) --- - '123.5' ... tarantool> dec.set_scale(a, 4) --- - '123.4500' ... ``` --- https://github.com/tarantool/tarantool/issues/4372 https://github.com/tarantool/tarantool/tree/sp/gh-4372-decimal-truncate src/lib/core/decimal.c | 28 +++++++++++++++++++++ src/lib/core/decimal.h | 19 ++++++++++++++ src/lua/decimal.c | 36 +++++++++++++++++++++++++++ test/app/decimal.result | 52 +++++++++++++++++++++++++++++++++++++++ test/app/decimal.test.lua | 15 +++++++++++ 5 files changed, 150 insertions(+) diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c index 1b4f44362..641d9feef 100644 --- a/src/lib/core/decimal.c +++ b/src/lib/core/decimal.c @@ -191,6 +191,34 @@ decimal_round(decimal_t *dec, int scale) return dec; } +decimal_t * +decimal_truncate(decimal_t *dec) +{ + decimal_t *res = decNumberTrim(dec); + + /* No errors are possible */ + assert(res == dec); + return res; +} + +decimal_t * +decimal_set_scale(decimal_t *dec, int scale) +{ + if (scale < 0) + return NULL; + if (scale <= decimal_scale(dec)) + return decimal_round(dec, scale); + /* how much zeros shoud we append. */ + int delta = scale + dec->exponent; + if (scale > DECIMAL_MAX_DIGITS || dec->digits + delta > DECIMAL_MAX_DIGITS) + return NULL; + decimal_t new_scale; + decimal_from_int64(&new_scale, -scale); + decNumberRescale(dec, dec, &new_scale, &decimal_context); + assert(decimal_check_status(dec, &decimal_context) != NULL); + return dec; +} + decimal_t * decimal_abs(decimal_t *res, const decimal_t *dec) { diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h index 1d0f2582e..f80f1a41b 100644 --- a/src/lib/core/decimal.h +++ b/src/lib/core/decimal.h @@ -122,6 +122,25 @@ decimal_compare(const decimal_t *lhs, const decimal_t *rhs); decimal_t * decimal_round(decimal_t *dec, int scale); +/** + * Remove trailing zeros from the fractional part of a number. + * @return \a dec with trimmed fractional zeros. + */ +decimal_t * +decimal_truncate(decimal_t *dec); + +/** + * Set scale of \a dec to \a scale. + * If \a scale is < than scale(\a dec), + * performs decimal_round(). + * Otherwise appends a sufficient amount of trailing + * fractional zeros. + * @return NULL, scale < 0 or too big. + * @return \a dec with set scale. + */ +decimal_t * +decimal_set_scale(decimal_t *dec, int scale); + /** * res is set to the absolute value of dec * decimal_abs(&a, &a) is allowed. diff --git a/src/lua/decimal.c b/src/lua/decimal.c index e548cdb9d..db8c647db 100644 --- a/src/lua/decimal.c +++ b/src/lua/decimal.c @@ -264,10 +264,44 @@ ldecimal_round(struct lua_State *L) int n = lua_tointeger(L, 2); decimal_t *res = lua_pushdecimal(L); *res = *lhs; + /* + * If the operation fails, it just + * leaves the number intact. + */ decimal_round(res, n); return 1; } +static int +ldecimal_truncate(struct lua_State *L) +{ + if (lua_gettop(L) < 1) + return luaL_error(L, "usage: decimal.truncate(decimal)"); + decimal_t *lhs = lua_checkdecimal(L, 1); + decimal_t *res = lua_pushdecimal(L); + *res = *lhs; + /* truncate never fails */ + decimal_truncate(res); + return 1; +} + +static int +ldecimal_set_scale(struct lua_State *L) +{ + if (lua_gettop(L) < 2) + return luaL_error(L, "usage: decimal.set_scale(decimal, scale)"); + decimal_t *lhs = lua_checkdecimal(L, 1); + int n = lua_tointeger(L, 2); + decimal_t *res = lua_pushdecimal(L); + *res = *lhs; + /* + * If the operation fails, it just + * leaves the number intact. + */ + decimal_set_scale(res, n); + return 1; +} + static int ldecimal_scale(struct lua_State *L) { @@ -321,6 +355,8 @@ static const luaL_Reg ldecimal_lib[] = { {"sqrt", ldecimal_sqrt}, {"round", ldecimal_round}, {"scale", ldecimal_scale}, + {"truncate", ldecimal_truncate}, + {"set_scale", ldecimal_set_scale}, {"precision", ldecimal_precision}, {"abs", ldecimal_abs}, {"new", ldecimal_new}, diff --git a/test/app/decimal.result b/test/app/decimal.result index ecb189277..9d58fe864 100644 --- a/test/app/decimal.result +++ b/test/app/decimal.result @@ -458,3 +458,55 @@ a ^ 2.5 | --- | - error: '[string "return a ^ 2.5 "]:1: decimal operation failed' | ... + +a = decimal.new('1e5') + | --- + | ... +a + | --- + | - '100000' + | ... +decimal.truncate(a) + | --- + | - '100000' + | ... +decimal.truncate(decimal.set_scale(a, 10)) + | --- + | - '100000' + | ... +decimal.set_scale(a, 10) + | --- + | - '100000.0000000000' + | ... +decimal.set_scale(a, -5) + | --- + | - '100000' + | ... +decimal.set_scale(a, 0) + | --- + | - '100000' + | ... +decimal.set_scale(a, 32) + | --- + | - '100000.00000000000000000000000000000000' + | ... +-- scale too big +decimal.set_scale(a, 33) + | --- + | - '100000' + | ... +decimal.truncate(decimal.set_scale(a, 10)) + | --- + | - '100000' + | ... +a = decimal.new('123.456789000000000') + | --- + | ... +a + | --- + | - '123.456789000000000' + | ... +decimal.truncate(a) + | --- + | - '123.456789' + | ... diff --git a/test/app/decimal.test.lua b/test/app/decimal.test.lua index 4aff0f2a4..6f2801f6d 100644 --- a/test/app/decimal.test.lua +++ b/test/app/decimal.test.lua @@ -128,3 +128,18 @@ a = decimal.new('-13') a ^ 2 -- fractional powers are allowed only for positive numbers a ^ 2.5 + +a = decimal.new('1e5') +a +decimal.truncate(a) +decimal.truncate(decimal.set_scale(a, 10)) +decimal.set_scale(a, 10) +decimal.set_scale(a, -5) +decimal.set_scale(a, 0) +decimal.set_scale(a, 32) +-- scale too big +decimal.set_scale(a, 33) +decimal.truncate(decimal.set_scale(a, 10)) +a = decimal.new('123.456789000000000') +a +decimal.truncate(a) -- 2.20.1 (Apple Git-117)
next reply other threads:[~2019-07-19 11:21 UTC|newest] Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-07-19 11:21 Serge Petrenko [this message] 2019-07-19 11:57 ` [tarantool-patches] " Konstantin Osipov 2019-07-19 12:15 ` [tarantool-patches] " Serge Petrenko 2019-07-24 11:42 ` Vladimir Davydov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20190719112136.23483-1-sergepetrenko@tarantool.org \ --to=sergepetrenko@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=vdavydov.dev@gmail.com \ --subject='Re: [PATCH] decimal: add methods truncate and set_scale' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox