From: Serge Petrenko <sergepetrenko@tarantool.org> To: vdavydov.dev@gmail.com Cc: tarantool-patches@freelists.org, kostja@tarantool.org, Serge Petrenko <sergepetrenko@tarantool.org> Subject: [PATCH v2 7/8] decimal: add conversions to (u)int64_t Date: Thu, 8 Aug 2019 14:55:58 +0300 [thread overview] Message-ID: <cc5664f36b3d27c2a990d589686dac1b465c7b7e.1565263272.git.sergepetrenko@tarantool.org> (raw) In-Reply-To: <cover.1565263272.git.sergepetrenko@tarantool.org> 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 840aa5dfe..19b5096ed 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 6e2cd3ce7..cf41a5052 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 a70fe9a9e..9afe17432 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)
next prev parent reply other threads:[~2019-08-08 11:55 UTC|newest] Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-08-08 11:55 [PATCH v2 0/8] Decimal indices Serge Petrenko 2019-08-08 11:55 ` [PATCH v2 1/8] lua: fix decimal comparison with nil Serge Petrenko 2019-08-12 21:16 ` Konstantin Osipov 2019-08-14 11:00 ` Vladimir Davydov 2019-08-14 22:17 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 2/8] decimal: fix encoding numbers with positive exponent Serge Petrenko 2019-08-12 21:18 ` Konstantin Osipov 2019-08-13 9:00 ` [tarantool-patches] " Serge Petrenko 2019-08-14 22:21 ` Konstantin Osipov 2019-08-14 11:56 ` Vladimir Davydov 2019-08-08 11:55 ` [PATCH v2 3/8] lua/pickle: fix a typo Serge Petrenko 2019-08-12 21:18 ` Konstantin Osipov 2019-08-14 11:12 ` Vladimir Davydov 2019-08-14 11:15 ` Serge Petrenko 2019-08-08 11:55 ` [PATCH v2 4/8] lua: rework luaL_field types to support msgpack extensions Serge Petrenko 2019-08-12 21:23 ` Konstantin Osipov 2019-08-13 13:15 ` [tarantool-patches] " Serge Petrenko 2019-08-14 22:23 ` Konstantin Osipov 2019-08-15 8:27 ` Serge Petrenko 2019-08-16 8:06 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 5/8] box: rework field_def and tuple_compare to work with mp_field_type instead of mp_type Serge Petrenko 2019-08-12 21:28 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 6/8] decimal: allow to encode/decode decimals as MsgPack Serge Petrenko 2019-08-12 21:29 ` Konstantin Osipov 2019-08-12 21:34 ` Konstantin Osipov 2019-08-13 14:01 ` Serge Petrenko 2019-08-14 22:25 ` Konstantin Osipov 2019-08-08 11:55 ` Serge Petrenko [this message] 2019-08-12 21:39 ` [PATCH v2 7/8] decimal: add conversions to (u)int64_t Konstantin Osipov 2019-08-13 14:18 ` Serge Petrenko 2019-08-14 22:26 ` Konstantin Osipov 2019-08-14 22:29 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 8/8] decimal: allow to index decimals Serge Petrenko 2019-08-08 13:42 ` Serge Petrenko 2019-08-12 21:41 ` Konstantin Osipov
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=cc5664f36b3d27c2a990d589686dac1b465c7b7e.1565263272.git.sergepetrenko@tarantool.org \ --to=sergepetrenko@tarantool.org \ --cc=kostja@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=vdavydov.dev@gmail.com \ --subject='Re: [PATCH v2 7/8] decimal: add conversions to (u)int64_t' \ /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