Tarantool development patches archive
 help / color / mirror / Atom feed
* [PATCH] decimal: add methods truncate and set_scale
@ 2019-07-19 11:21 Serge Petrenko
  2019-07-19 11:57 ` [tarantool-patches] " Konstantin Osipov
  0 siblings, 1 reply; 4+ messages in thread
From: Serge Petrenko @ 2019-07-19 11:21 UTC (permalink / raw)
  To: vdavydov.dev; +Cc: tarantool-patches, Serge Petrenko

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)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [tarantool-patches] [PATCH] decimal: add methods truncate and set_scale
  2019-07-19 11:21 [PATCH] decimal: add methods truncate and set_scale Serge Petrenko
@ 2019-07-19 11:57 ` Konstantin Osipov
  2019-07-19 12:15   ` [tarantool-patches] " Serge Petrenko
  0 siblings, 1 reply; 4+ messages in thread
From: Konstantin Osipov @ 2019-07-19 11:57 UTC (permalink / raw)
  To: tarantool-patches; +Cc: vdavydov.dev, Serge Petrenko

* Serge Petrenko <sergepetrenko@tarantool.org> [19/07/19 14:42]:
> 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.
> 

Please note that it is Trim in the ldec library.
truncate() has a slightly different meaning.

> +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;

Please also note here: the name is rescale(), not set_scale(). The
meaning of set_scale() is slightly different.

Please use less confusing names.

otherwise lgtm.

-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [tarantool-patches] Re: [PATCH] decimal: add methods truncate and set_scale
  2019-07-19 11:57 ` [tarantool-patches] " Konstantin Osipov
@ 2019-07-19 12:15   ` Serge Petrenko
  2019-07-24 11:42     ` Vladimir Davydov
  0 siblings, 1 reply; 4+ messages in thread
From: Serge Petrenko @ 2019-07-19 12:15 UTC (permalink / raw)
  To: Konstantin Osipov; +Cc: Vladimir Davydov, tarantool-patches


> 19 июля 2019 г., в 14:57, Konstantin Osipov <kostja@tarantool.org> написал(а):
> 
> * Serge Petrenko <sergepetrenko@tarantool.org> [19/07/19 14:42]:
>> 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.
>> 
> 
> Please note that it is Trim in the ldec library.
> truncate() has a slightly different meaning.
> 
>> +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;
> 
> Please also note here: the name is rescale(), not set_scale(). The
> meaning of set_scale() is slightly different.
> 
> Please use less confusing names.

Hi! Thank you for review.
No problem, renamed to trim() and rescale().
The changes are on the branch.

> 
> otherwise lgtm.
> 
> -- 
> Konstantin Osipov, Moscow, Russia
> 


--
Serge Petrenko
sergepetrenko@tarantool.org

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [tarantool-patches] Re: [PATCH] decimal: add methods truncate and set_scale
  2019-07-19 12:15   ` [tarantool-patches] " Serge Petrenko
@ 2019-07-24 11:42     ` Vladimir Davydov
  0 siblings, 0 replies; 4+ messages in thread
From: Vladimir Davydov @ 2019-07-24 11:42 UTC (permalink / raw)
  To: Serge Petrenko; +Cc: Konstantin Osipov, tarantool-patches

On Fri, Jul 19, 2019 at 03:15:11PM +0300, Serge Petrenko wrote:
> No problem, renamed to trim() and rescale().
> The changes are on the branch.

Pushed to master.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-07-24 11:42 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-19 11:21 [PATCH] decimal: add methods truncate and set_scale Serge Petrenko
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox