From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Serge Petrenko Subject: [PATCH] decimal: add methods truncate and set_scale Date: Fri, 19 Jul 2019 14:21:36 +0300 Message-Id: <20190719112136.23483-1-sergepetrenko@tarantool.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: vdavydov.dev@gmail.com Cc: tarantool-patches@freelists.org, Serge Petrenko List-ID: 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)