[PATCH v3 5/6] decimal: add conversions to (u)int64_t
Serge Petrenko
sergepetrenko at tarantool.org
Tue Aug 20 20:10:03 MSK 2019
Update decNumber library, add methods to convert decimals to uint64_t
and int64_t, add unit tests.
Also replace decimal_round() function with decimal_round_with_mode() to
allow setting rounding mode.
We need to round with mode DEC_ROUND_DOWN in to_int64 conversions in
order to be consistent with double to int conversions.
It will be needed to compute hints for decimal fields.
Prerequisite #4333
---
src/lib/core/decimal.c | 57 +++++++++++++++++++++++++++++--
src/lib/core/decimal.h | 19 +++++++++++
test/unit/decimal.c | 66 ++++++++++++++++++++++++++++++++++-
test/unit/decimal.result | 74 ++++++++++++++++++++++++++++++++++++++--
third_party/decNumber | 2 +-
5 files changed, 210 insertions(+), 8 deletions(-)
diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c
index 356acf6f2..c44b2f6a2 100644
--- a/src/lib/core/decimal.c
+++ b/src/lib/core/decimal.c
@@ -157,6 +157,45 @@ decimal_to_string(const decimal_t *dec)
return buf;
}
+static decimal_t *
+decimal_to_integer(decimal_t *dec)
+{
+ decimal_t z;
+ decNumberZero(&z);
+ if (decimal_scale(dec) != 0) {
+ /*
+ * Rounding mode is important here.
+ * We want to be consistent with double
+ * to int conversion so that comparison
+ * hints work correctly.
+ */
+ decimal_floor(dec, 0);
+ }
+ /* Zero the number exponent for decNumberToInt64. */
+ decNumberRescale(dec, dec, &z, &decimal_context);
+ return decimal_check_status(dec, &decimal_context);
+}
+
+decimal_t *
+decimal_to_int64(const decimal_t *dec, int64_t *num)
+{
+ decimal_t d = *dec;
+ if (decimal_to_integer(&d) == NULL)
+ return NULL;
+ *num = decNumberToInt64(&d, &decimal_context);
+ return decimal_check_status(&d, &decimal_context);
+}
+
+decimal_t *
+decimal_to_uint64(const decimal_t *dec, uint64_t *num)
+{
+ decimal_t d = *dec;
+ if (decimal_to_integer(&d) == NULL)
+ return NULL;
+ *num = decNumberToUInt64(&d, &decimal_context);
+ return decimal_check_status(&d, &decimal_context);
+}
+
int
decimal_compare(const decimal_t *lhs, const decimal_t *rhs)
{
@@ -167,8 +206,8 @@ decimal_compare(const decimal_t *lhs, const decimal_t *rhs)
return r;
}
-decimal_t *
-decimal_round(decimal_t *dec, int scale)
+static decimal_t *
+decimal_round_with_mode(decimal_t *dec, int scale, enum rounding mode)
{
if (scale < 0 || scale > DECIMAL_MAX_DIGITS)
return NULL;
@@ -181,7 +220,7 @@ decimal_round(decimal_t *dec, int scale)
ndig, /* Precision */
ndig, /* emax */
scale != 0 ? -1 : 0, /* emin */
- DECIMAL_ROUNDING, /* rounding */
+ mode, /* rounding */
0, /* no traps */
0, /* zero status */
0 /* no clamping */
@@ -192,6 +231,18 @@ decimal_round(decimal_t *dec, int scale)
return dec;
}
+inline decimal_t *
+decimal_round(decimal_t *dec, int scale)
+{
+ return decimal_round_with_mode(dec, scale, DECIMAL_ROUNDING);
+}
+
+inline decimal_t *
+decimal_floor(decimal_t *dec, int scale)
+{
+ return decimal_round_with_mode(dec, scale, DEC_ROUND_DOWN);
+}
+
decimal_t *
decimal_trim(decimal_t *dec)
{
diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h
index ffe49b652..77b6dbfd0 100644
--- a/src/lib/core/decimal.h
+++ b/src/lib/core/decimal.h
@@ -101,6 +101,18 @@ decimal_from_uint64(decimal_t *dec, uint64_t num);
const char *
decimal_to_string(const decimal_t *dec);
+/**
+ * Convert a given decimal to int64_t
+ * \param[out] num - the result
+ * @return NULL if \a dec doesn't fit into int64_t
+ */
+decimal_t *
+decimal_to_int64(const decimal_t *dec, int64_t *num);
+
+/** \sa decimal_to_int64 */
+decimal_t *
+decimal_to_uint64(const decimal_t *dec, uint64_t *num);
+
/**
* Compare 2 decimal values.
* @return -1, lhs < rhs,
@@ -122,6 +134,13 @@ decimal_compare(const decimal_t *lhs, const decimal_t *rhs);
decimal_t *
decimal_round(decimal_t *dec, int scale);
+/**
+ * Round a decimal towards zero.
+ * \sa decimal_round
+ */
+decimal_t *
+decimal_floor(decimal_t *dec, int scale);
+
/**
* Remove trailing zeros from the fractional part of a number.
* @return \a dec with trimmed fractional zeros.
diff --git a/test/unit/decimal.c b/test/unit/decimal.c
index 625913efe..254114b5f 100644
--- a/test/unit/decimal.c
+++ b/test/unit/decimal.c
@@ -5,6 +5,7 @@
#include "msgpuck.h"
#include <limits.h>
#include <string.h>
+#include <inttypes.h>
#include <float.h> /* DBL_DIG */
#define success(x) x
@@ -117,6 +118,15 @@ char buf[32];
is(strcmp(decimal_to_string(&d2), str), 0, "str(decimal_unpack(decimal_pack("str")) == "str);\
})
+#define test_toint(type, num, out_fmt) ({\
+ decimal_t dec;\
+ type##_t val;\
+ decimal_from_##type(&dec, num);\
+ isnt(decimal_to_##type(&dec, &val), NULL, "Conversion of %"out_fmt\
+ " to decimal and back to "#type" successful", (type##_t) num);\
+ is(val, num, "Conversion back to "#type" correct");\
+})
+
static int
test_pack_unpack(void)
{
@@ -192,10 +202,62 @@ test_mp_decimal(void)
return check_plan();
}
+static int
+test_to_int(void)
+{
+ plan(66);
+
+ test_toint(uint64, ULLONG_MAX, PRIu64);
+ test_toint(int64, LLONG_MAX, PRId64);
+ test_toint(int64, LLONG_MIN, PRId64);
+ test_toint(uint64, 0, PRIu64);
+ test_toint(int64, 0, PRId64);
+ test_toint(int64, -1, PRId64);
+
+ /* test some arbitrary values. */
+ test_toint(uint64, ULLONG_MAX / 157, PRIu64);
+ test_toint(int64, LLONG_MAX / 157, PRId64);
+ test_toint(int64, LLONG_MIN / 157, PRId64);
+
+ test_toint(uint64, ULLONG_MAX / 157 / 151, PRIu64);
+ test_toint(int64, LLONG_MAX / 157 / 151, PRId64);
+ test_toint(int64, LLONG_MIN / 157 / 151, PRId64);
+
+ test_toint(uint64, ULLONG_MAX / 157 / 151 / 149, PRIu64);
+ test_toint(int64, LLONG_MAX / 157 / 151 / 149, PRId64);
+ test_toint(int64, LLONG_MIN / 157 / 151 / 149, PRId64);
+
+ test_toint(uint64, ULLONG_MAX / 157 / 151 / 149 / 139, PRIu64);
+ test_toint(int64, LLONG_MAX / 157 / 151 / 149 / 139, PRId64);
+ test_toint(int64, LLONG_MIN / 157 / 151 / 149 / 139, PRId64);
+
+ test_toint(uint64, ULLONG_MAX / 157 / 151 / 149 / 139 / 137, PRIu64);
+ test_toint(int64, LLONG_MAX / 156 / 151 / 149 / 139 / 137, PRId64);
+ test_toint(int64, LLONG_MIN / 156 / 151 / 149 / 139 / 137, PRId64);
+
+ test_toint(uint64, UINT_MAX, PRIu64);
+ test_toint(int64, INT_MAX, PRId64);
+ test_toint(int64, INT_MIN, PRId64);
+
+ test_toint(uint64, UINT_MAX / 157, PRIu64); /* ~ 27356479 */
+ test_toint(int64, INT_MAX / 157, PRId64);
+ test_toint(int64, INT_MIN / 157, PRId64);
+
+ test_toint(uint64, UINT_MAX / 157 / 151, PRIu64); /* ~ 181168 */
+ test_toint(int64, INT_MAX / 157 / 151, PRId64);
+ test_toint(int64, INT_MIN / 157 / 151, PRId64);
+
+ test_toint(uint64, UINT_MAX / 157 / 151 / 149, PRIu64); /* ~ 1215 */
+ test_toint(int64, INT_MAX / 157 / 151 / 149, PRId64);
+ test_toint(int64, INT_MIN / 157 / 151 / 149, PRId64);
+
+ return check_plan();
+}
+
int
main(void)
{
- plan(280);
+ plan(281);
dectest(314, 271, uint64, uint64_t);
dectest(65535, 23456, uint64, uint64_t);
@@ -255,6 +317,8 @@ main(void)
dectest_op1_fail(log10, -1);
dectest_op1_fail(sqrt, -10);
+ test_to_int();
+
test_pack_unpack();
test_mp_decimal();
diff --git a/test/unit/decimal.result b/test/unit/decimal.result
index e6812a8dd..e8432fb36 100644
--- a/test/unit/decimal.result
+++ b/test/unit/decimal.result
@@ -1,4 +1,4 @@
-1..280
+1..281
ok 1 - decimal(314)
ok 2 - decimal(271)
ok 3 - decimal(314) + decimal(271)
@@ -277,6 +277,74 @@ ok 275 - decimal_from_string(-1)
ok 276 - decimal_log10(-1) - error on wrong operands.
ok 277 - decimal_from_string(-10)
ok 278 - decimal_sqrt(-10) - error on wrong operands.
+ 1..66
+ ok 1 - Conversion of 18446744073709551615 to decimal and back to uint64 successful
+ ok 2 - Conversion back to uint64 correct
+ ok 3 - Conversion of 9223372036854775807 to decimal and back to int64 successful
+ ok 4 - Conversion back to int64 correct
+ ok 5 - Conversion of -9223372036854775808 to decimal and back to int64 successful
+ ok 6 - Conversion back to int64 correct
+ ok 7 - Conversion of 0 to decimal and back to uint64 successful
+ ok 8 - Conversion back to uint64 correct
+ ok 9 - Conversion of 0 to decimal and back to int64 successful
+ ok 10 - Conversion back to int64 correct
+ ok 11 - Conversion of -1 to decimal and back to int64 successful
+ ok 12 - Conversion back to int64 correct
+ ok 13 - Conversion of 117495185182863386 to decimal and back to uint64 successful
+ ok 14 - Conversion back to uint64 correct
+ ok 15 - Conversion of 58747592591431693 to decimal and back to int64 successful
+ ok 16 - Conversion back to int64 correct
+ ok 17 - Conversion of -58747592591431693 to decimal and back to int64 successful
+ ok 18 - Conversion back to int64 correct
+ ok 19 - Conversion of 778113809158035 to decimal and back to uint64 successful
+ ok 20 - Conversion back to uint64 correct
+ ok 21 - Conversion of 389056904579017 to decimal and back to int64 successful
+ ok 22 - Conversion back to int64 correct
+ ok 23 - Conversion of -389056904579017 to decimal and back to int64 successful
+ ok 24 - Conversion back to int64 correct
+ ok 25 - Conversion of 5222240329919 to decimal and back to uint64 successful
+ ok 26 - Conversion back to uint64 correct
+ ok 27 - Conversion of 2611120164959 to decimal and back to int64 successful
+ ok 28 - Conversion back to int64 correct
+ ok 29 - Conversion of -2611120164959 to decimal and back to int64 successful
+ ok 30 - Conversion back to int64 correct
+ ok 31 - Conversion of 37570074315 to decimal and back to uint64 successful
+ ok 32 - Conversion back to uint64 correct
+ ok 33 - Conversion of 18785037157 to decimal and back to int64 successful
+ ok 34 - Conversion back to int64 correct
+ ok 35 - Conversion of -18785037157 to decimal and back to int64 successful
+ ok 36 - Conversion back to int64 correct
+ ok 37 - Conversion of 274234119 to decimal and back to uint64 successful
+ ok 38 - Conversion back to uint64 correct
+ ok 39 - Conversion of 137996015 to decimal and back to int64 successful
+ ok 40 - Conversion back to int64 correct
+ ok 41 - Conversion of -137996015 to decimal and back to int64 successful
+ ok 42 - Conversion back to int64 correct
+ ok 43 - Conversion of 4294967295 to decimal and back to uint64 successful
+ ok 44 - Conversion back to uint64 correct
+ ok 45 - Conversion of 2147483647 to decimal and back to int64 successful
+ ok 46 - Conversion back to int64 correct
+ ok 47 - Conversion of -2147483648 to decimal and back to int64 successful
+ ok 48 - Conversion back to int64 correct
+ ok 49 - Conversion of 27356479 to decimal and back to uint64 successful
+ ok 50 - Conversion back to uint64 correct
+ ok 51 - Conversion of 13678239 to decimal and back to int64 successful
+ ok 52 - Conversion back to int64 correct
+ ok 53 - Conversion of -13678239 to decimal and back to int64 successful
+ ok 54 - Conversion back to int64 correct
+ ok 55 - Conversion of 181168 to decimal and back to uint64 successful
+ ok 56 - Conversion back to uint64 correct
+ ok 57 - Conversion of 90584 to decimal and back to int64 successful
+ ok 58 - Conversion back to int64 correct
+ ok 59 - Conversion of -90584 to decimal and back to int64 successful
+ ok 60 - Conversion back to int64 correct
+ ok 61 - Conversion of 1215 to decimal and back to uint64 successful
+ ok 62 - Conversion back to uint64 correct
+ ok 63 - Conversion of 607 to decimal and back to int64 successful
+ ok 64 - Conversion back to int64 correct
+ ok 65 - Conversion of -607 to decimal and back to int64 successful
+ ok 66 - Conversion back to int64 correct
+ok 279 - subtests
1..151
ok 1 - decimal_len(0)
ok 2 - decimal_len(0) == len(decimal_pack(0)
@@ -429,7 +497,7 @@ ok 278 - decimal_sqrt(-10) - error on wrong operands.
ok 149 - positive exponent number is packed/unpacked correctly
ok 150 - unpack malformed decimal fails
ok 151 - decode malformed decimal preserves buffer position
-ok 279 - subtests
+ok 280 - subtests
1..198
ok 1 - mp_sizeof_decimal(0)
ok 2 - mp_sizeof_decimal(0) == len(mp_encode_decimal(0))
@@ -629,4 +697,4 @@ ok 279 - subtests
ok 196 - decimal_unpack() after mp_decode_extl()
ok 197 - decimal_unpack() after mp_decode_extl() value
ok 198 - decimal_unpack() after mp_decode_extl() len
-ok 280 - subtests
+ok 281 - subtests
diff --git a/third_party/decNumber b/third_party/decNumber
index ee540fca6..878ed752f 160000
--- a/third_party/decNumber
+++ b/third_party/decNumber
@@ -1 +1 @@
-Subproject commit ee540fca6a44b3a2df7258dc7a1ec612cbf84dce
+Subproject commit 878ed752f2453cd5e73587e67f7782aec9181a22
--
2.20.1 (Apple Git-117)
More information about the Tarantool-patches
mailing list