From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Serge Petrenko Subject: [PATCH 4/5] decimal: add conversions to (u)int64_t Date: Wed, 17 Jul 2019 18:33:45 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: vdavydov.dev@gmail.com Cc: tarantool-patches@freelists.org, Serge Petrenko List-ID: 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 | 63 ++++++++++++++++++++++++++++++++-- src/lib/core/decimal.h | 12 +++++++ test/unit/decimal.c | 66 ++++++++++++++++++++++++++++++++++- test/unit/decimal.result | 74 ++++++++++++++++++++++++++++++++++++++-- third_party/decNumber | 2 +- 5 files changed, 209 insertions(+), 8 deletions(-) diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c index 2c69a773c..5a576e6e5 100644 --- a/src/lib/core/decimal.c +++ b/src/lib/core/decimal.c @@ -157,6 +157,57 @@ decimal_to_string(const decimal_t *dec) return buf; } +static decimal_t * +decimal_round_with_mode(decimal_t *dec, int scale, enum rounding mode); + +decimal_t * +decimal_to_int64(decimal_t *dec, int64_t *num) +{ + decimal_t d, z; + decNumberZero(&z); + d = *dec; + dec = &d; + + 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. + */ + dec = decimal_round_with_mode(dec, 0, DEC_ROUND_DOWN); + } + /* + * decNumberToInt64 works only with numbers with + * zero exponent. + */ + decNumberRescale(dec, dec, &z, &decimal_context); + if (decimal_check_status(dec, &decimal_context) == NULL) { + return NULL; + } + *num = decNumberToInt64(dec, &decimal_context); + return decimal_check_status(dec, &decimal_context); +} + +decimal_t * +decimal_to_uint64(decimal_t *dec, uint64_t *num) +{ + decimal_t d, z; + decNumberZero(&z); + d = *dec; + dec = &d; + + if (decimal_scale(dec) != 0) { + dec = decimal_round_with_mode(dec, 0, DEC_ROUND_DOWN); + } + decNumberRescale(dec, dec, &z, &decimal_context); + if (decimal_check_status(dec, &decimal_context) == NULL) { + return NULL; + } + *num = decNumberToUInt64(dec, &decimal_context); + return decimal_check_status(dec, &decimal_context); +} + int decimal_compare(const decimal_t *lhs, const decimal_t *rhs) { @@ -167,8 +218,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 +232,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 +243,12 @@ 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); +} + 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..a4e7683c7 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(decimal_t *dec, int64_t *num); + +/** \sa decimal_to_int64 */ +decimal_t * +decimal_to_uint64(decimal_t *dec, uint64_t *num); + /** * Compare 2 decimal values. * @return -1, lhs < rhs, diff --git a/test/unit/decimal.c b/test/unit/decimal.c index dd64b95ed..6b609140b 100644 --- a/test/unit/decimal.c +++ b/test/unit/decimal.c @@ -5,6 +5,7 @@ #include "msgpuck.h" #include #include +#include #include /* 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) { @@ -181,10 +191,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); @@ -244,6 +306,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 6d00996d3..5a9c804bc 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..146 ok 1 - decimal_len(0) ok 2 - decimal_len(0) == len(decimal_pack(0) @@ -424,7 +492,7 @@ ok 278 - decimal_sqrt(-10) - error on wrong operands. ok 144 - str(decimal_unpack(decimal_pack(-99999999999999999999999999999999999999)) == -99999999999999999999999999999999999999 ok 145 - unpack malformed decimal fails ok 146 - 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)) @@ -624,4 +692,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)