Tarantool development patches archive
 help / color / mirror / Atom feed
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)

             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