From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Serge Petrenko Subject: [PATCH v3 1/5] decimal: fix ln hang on values between ~ 0.9 and 1.1 Date: Tue, 2 Jul 2019 20:27:48 +0300 Message-Id: <2536cd7f0c6aec76d62c2c0696b3449500e2c726.1562087728.git.sergepetrenko@tarantool.org> 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: Turns out decNumberLn hangs when result is subnormal, according to the current context settings. To fix this, reset minimal allowed exponent to a smaller value during the ln operation and round the result afterwards. Follow-up 6d62c6c19ed418932ead1bba44fcd7cd84c78a19 --- src/lib/core/decimal.c | 19 +++++++++++++ test/unit/decimal.c | 5 +++- test/unit/decimal.result | 58 +++++++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c index 1f9b5838d..c76387d76 100644 --- a/src/lib/core/decimal.c +++ b/src/lib/core/decimal.c @@ -264,11 +264,30 @@ decimal_log10(decimal_t *res, const decimal_t *lhs) decimal_t * decimal_ln(decimal_t *res, const decimal_t *lhs) { + /* + * ln hangs in an infinite loop when result is + * between -10 ^ emin and 10 ^ emin. + * For small x, ln(1 + x) = x. Say, we take the + * smallest allowed value for + * (1 + x) = 1 + 10 ^ -(DECIMAL_MAX_DIGITS - 1). + * For ln to work for this value we need to set emin to + * -DECIMAL_MAX_DIGITS. + */ + int32_t emin = decimal_context.emin; + decimal_context.emin = -DECIMAL_MAX_DIGITS; + decNumberLn(res, lhs, &decimal_context); + decimal_context.emin = emin; if (decimal_check_status(&decimal_context) != 0) { return NULL; } else { + /* + * The increased EMIN allows for scale up to + * 2 * (DECIMAL_MAX_DIGITS - 1). + * Round back to DECIMAL_MAX_DIGITS - 1. + */ + decimal_round(res, DECIMAL_MAX_DIGITS - 1); return res; } } diff --git a/test/unit/decimal.c b/test/unit/decimal.c index 7453d13ca..327a06d72 100644 --- a/test/unit/decimal.c +++ b/test/unit/decimal.c @@ -121,7 +121,7 @@ test_pack_unpack(void) int main(void) { - plan(258); + plan(266); dectest(314, 271, uint64, uint64_t); dectest(65535, 23456, uint64, uint64_t); @@ -152,6 +152,9 @@ main(void) dectest_op1(log10, 100, 2, 0); dectest_op1(ln, 10, 2.3, 2); + dectest_op1(ln, 1.1, 0.1, 1); + dectest_op1(ln, 1.0000000000000000000000000000000000001, + 0.0000000000000000000000000000000000001, 0); dectest_op1(exp, 2, 7.39, 2); dectest_op1(sqrt, 100, 10, 0); diff --git a/test/unit/decimal.result b/test/unit/decimal.result index 051dc7960..7e767dcd2 100644 --- a/test/unit/decimal.result +++ b/test/unit/decimal.result @@ -1,4 +1,4 @@ -1..258 +1..266 ok 1 - decimal(314) ok 2 - decimal(271) ok 3 - decimal(314) + decimal(271) @@ -233,29 +233,37 @@ ok 231 - decimal_from_string(10) ok 232 - decimal_from_string(2.3) ok 233 - decimal_ln(10) ok 234 - decimal_compare(2.3) -ok 235 - decimal_from_string(2) -ok 236 - decimal_from_string(7.39) -ok 237 - decimal_exp(2) -ok 238 - decimal_compare(7.39) -ok 239 - decimal_from_string(100) -ok 240 - decimal_from_string(10) -ok 241 - decimal_sqrt(100) -ok 242 - decimal_compare(10) -ok 243 - decimal construction from 2e38 failure -ok 244 - decimal construction from "1e38" failure -ok 245 - decimal construction from "100000000000000000000000000000000000000" failure -ok 246 - decimal construction from LONG_MIN success -ok 247 - decimal construction from LONG_MAX success -ok 248 - decimal construction from ULONG_MAX success -ok 249 - decimal_from_string(9e37) -ok 250 - decimal_from_string(1e37) -ok 251 - decimal_add(9e37, 1e37) - overflow -ok 252 - decimal_from_string(1e19) -ok 253 - decimal_from_string(1e19) -ok 254 - decimal_mul(1e19, 1e19) - overflow -ok 255 - decimal_from_string(1e19) -ok 256 - decimal_from_string(1e-19) -ok 257 - decimal_div(1e19, 1e-19) - overflow +ok 235 - decimal_from_string(1.1) +ok 236 - decimal_from_string(0.1) +ok 237 - decimal_ln(1.1) +ok 238 - decimal_compare(0.1) +ok 239 - decimal_from_string(1.0000000000000000000000000000000000001) +ok 240 - decimal_from_string(0.0000000000000000000000000000000000001) +ok 241 - decimal_ln(1.0000000000000000000000000000000000001) +ok 242 - decimal_compare(0.0000000000000000000000000000000000001) +ok 243 - decimal_from_string(2) +ok 244 - decimal_from_string(7.39) +ok 245 - decimal_exp(2) +ok 246 - decimal_compare(7.39) +ok 247 - decimal_from_string(100) +ok 248 - decimal_from_string(10) +ok 249 - decimal_sqrt(100) +ok 250 - decimal_compare(10) +ok 251 - decimal construction from 2e38 failure +ok 252 - decimal construction from "1e38" failure +ok 253 - decimal construction from "100000000000000000000000000000000000000" failure +ok 254 - decimal construction from LONG_MIN success +ok 255 - decimal construction from LONG_MAX success +ok 256 - decimal construction from ULONG_MAX success +ok 257 - decimal_from_string(9e37) +ok 258 - decimal_from_string(1e37) +ok 259 - decimal_add(9e37, 1e37) - overflow +ok 260 - decimal_from_string(1e19) +ok 261 - decimal_from_string(1e19) +ok 262 - decimal_mul(1e19, 1e19) - overflow +ok 263 - decimal_from_string(1e19) +ok 264 - decimal_from_string(1e-19) +ok 265 - decimal_div(1e19, 1e-19) - overflow 1..146 ok 1 - decimal_len(0) ok 2 - decimal_len(0) == len(decimal_pack(0) @@ -403,4 +411,4 @@ ok 257 - decimal_div(1e19, 1e-19) - overflow ok 144 - str(decimal_unpack(decimal_pack(-99999999999999999999999999999999999999)) == -99999999999999999999999999999999999999 ok 145 - unpack malformed decimal fails ok 146 - decode malformed decimal preserves buffer position -ok 258 - subtests +ok 266 - subtests -- 2.20.1 (Apple Git-117)