From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Serge Petrenko Subject: [PATCH v4 2/2] lib/core: introduce decimal type to tarantool Date: Tue, 11 Jun 2019 18:56:48 +0300 Message-Id: <44257abefbafcb742f6757c4befa271517dfb4f4.1560268286.git.sergepetrenko@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: vdavydov.dev@gmail.com Cc: georgy@tarantool.org, kostja@tarantool.org, tarantool-patches@freelists.org, Serge Petrenko List-ID: Add fixed-point decimal type to tarantool core. Adapt decNumber floating-point decimal library for the purpose, write a small wrapper and add unit tests. A new decimal type is an alias for decNumber numbers from the decNumber library. Arithmetic operations (+, -, *, /) and some mathematic functions (ln, log10, exp, pow, sqrt) are available together with methods to pack and unpack decimal to and from its packed representation (useful for serialization). We introduce a single context for all the arithmetic operations on decimals, which enforces both number precision and scale to be in range [0, 38]. NaNs and Infinities are restricted. Part of #692 --- CMakeLists.txt | 7 + cmake/BuildDecNumber.cmake | 14 ++ src/CMakeLists.txt | 1 + src/lib/core/CMakeLists.txt | 3 +- src/lib/core/decimal.c | 354 +++++++++++++++++++++++++++++++ src/lib/core/decimal.h | 206 ++++++++++++++++++ test/unit/CMakeLists.txt | 2 + test/unit/decimal.c | 174 ++++++++++++++++ test/unit/decimal.result | 406 ++++++++++++++++++++++++++++++++++++ 9 files changed, 1166 insertions(+), 1 deletion(-) create mode 100644 cmake/BuildDecNumber.cmake create mode 100644 src/lib/core/decimal.c create mode 100644 src/lib/core/decimal.h create mode 100644 test/unit/decimal.c create mode 100644 test/unit/decimal.result diff --git a/CMakeLists.txt b/CMakeLists.txt index 7658fc6c9..bfb15effb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,6 +431,13 @@ else() find_package(MsgPuck) endif() +# +# decNumber +# + +include(BuildDecNumber) +decnumber_build() + # # LibYAML # diff --git a/cmake/BuildDecNumber.cmake b/cmake/BuildDecNumber.cmake new file mode 100644 index 000000000..abc6c64c4 --- /dev/null +++ b/cmake/BuildDecNumber.cmake @@ -0,0 +1,14 @@ +# +# A macro to build the bundled decNumber lisbrary. +macro(decnumber_build) + set(decnumber_src + ${PROJECT_SOURCE_DIR}/third_party/decNumber/decNumber.c + ${PROJECT_SOURCE_DIR}/third_party/decNumber/decContext.c + ${PROJECT_SOURCE_DIR}/third_party/decNumber/decPacked.c + ) + + add_library(decNumber STATIC ${decnumber_src}) + + set(DECNUMBER_INCLUDE_DIR ${PROJECT_BINARY_DIR}/third_party/decNumber) + unset(decnumber_src) +endmacro(decnumber_build) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54ac12106..33b64f6a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,7 @@ include_directories(${MSGPUCK_INCLUDE_DIRS}) include_directories(${CURL_INCLUDE_DIRS}) include_directories(${ICU_INCLUDE_DIRS}) include_directories(${ICONV_INCLUDE_DIRS}) +include_directories(${DECNUMBER_INCLUDE_DIR}) set(LIBUTIL_FREEBSD_SRC ${CMAKE_SOURCE_DIR}/third_party/libutil_freebsd) include_directories(${LIBUTIL_FREEBSD_SRC}) diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt index eb10b11c3..66e430a25 100644 --- a/src/lib/core/CMakeLists.txt +++ b/src/lib/core/CMakeLists.txt @@ -26,6 +26,7 @@ set(core_sources trigger.cc mpstream.c port.c + decimal.c ) if (TARGET_OS_NETBSD) @@ -37,7 +38,7 @@ endif() add_library(core STATIC ${core_sources}) -target_link_libraries(core salad small uri ${LIBEV_LIBRARIES} +target_link_libraries(core salad small uri decNumber ${LIBEV_LIBRARIES} ${LIBEIO_LIBRARIES} ${LIBCORO_LIBRARIES} ${MSGPUCK_LIBRARIES}) diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c new file mode 100644 index 000000000..a9a63c08f --- /dev/null +++ b/src/lib/core/decimal.c @@ -0,0 +1,354 @@ +/* + * Copyright 2019, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "decimal.h" +#include "third_party/decNumber/decContext.h" +#include "third_party/decNumber/decPacked.h" +#include "lib/core/tt_static.h" +#include +#include +#include /* DBL_DIG */ +#include /* isnan(), isinf(). */ +#include "trivia/util.h" + +#define DECIMAL_ROUNDING DEC_ROUND_HALF_UP + +/** A single context for all the arithmetic operations. */ +static __thread decContext decimal_context = { + /* Maximum precision during operations. */ + DECIMAL_MAX_DIGITS, + /* + * Maximum decimal lagarithm of the number. + * Allows for precision = DECIMAL_MAX_DIGITS + */ + DECIMAL_MAX_DIGITS - 1, + /* + * Minimal adjusted exponent. The smallest absolute value will be + * exp((1 - DECIMAL_MAX_DIGITS) - 1) = + * exp(-DECIMAL_MAX_DIGITS) allowing for scale = + * DECIMAL_MAX_DIGITS + */ + -1, + /* Rounding mode: .5 rounds away from 0. */ + DECIMAL_ROUNDING, + /* Turn off signalling for failed operations. */ + 0, + /* Status holding occured events. Initially empty. */ + 0, + /* Turn off exponent clamping. */ + 0 +}; + +/** + * A finalizer for all the operations. + * Check the operation context status and empty it. + * + * @return NULL if finalization failed. + * result pointer otherwise. + */ +static inline int +decimal_check_status(decContext *context) +{ + uint32_t status = decContextGetStatus(context); + decContextZeroStatus(context); + /* + * Clear warnings. Every value less than 0.1 is + * subnormal, DEC_Inexact and DEC_Rounded result + * from rounding. DEC_Inexact with DEC_Subnormal + * together result in DEC_Underflow. DEC_Clamped + * happens after underflow if rounding to zero. + */ + status &= ~(uint32_t)(DEC_Inexact | DEC_Rounded | DEC_Underflow | + DEC_Subnormal | DEC_Clamped); + return status ? -1 : 0; +} + +int decimal_precision(const decimal_t *dec) { + return dec->exponent <= 0 ? MAX(dec->digits, -dec->exponent) : + dec->digits + dec->exponent; +} + +int decimal_scale(const decimal_t *dec) { + return dec->exponent < 0 ? -dec->exponent : 0; +} + +decimal_t * +decimal_zero(decimal_t *dec) +{ + decNumberZero(dec); + return dec; +} + +decimal_t * +decimal_from_string(decimal_t *dec, const char *str) +{ + decNumberFromString(dec, str, &decimal_context); + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return dec; + } +} + +decimal_t * +decimal_from_double(decimal_t *dec, double d) +{ + char buf[DECIMAL_MAX_DIGITS+3]; + if (isinf(d) || isnan(d)) + return NULL; + snprintf(buf, DECIMAL_MAX_DIGITS+3, "%.*f", DBL_DIG, d); + return decimal_from_string(dec, buf); +} + +decimal_t * +decimal_from_int64(decimal_t *dec, int64_t num) +{ + return decNumberFromInt64(dec, num); +} + +decimal_t * +decimal_from_uint64(decimal_t *dec, uint64_t num) +{ + return decNumberFromUInt64(dec, num); +} + +const char * +decimal_to_string(const decimal_t *dec) +{ + char *buf = tt_static_buf(); + /* No errors are possible. */ + char *tmp = decNumberToString(dec, buf); + assert(buf == tmp); + (void)tmp; + return buf; +} + +double +decimal_to_double(const decimal_t *dec) +{ + const char *buf = decimal_to_string(dec); + char *end; + double d = strtod(buf, &end); + return d; +} + +int +decimal_compare(const decimal_t *lhs, const decimal_t *rhs) +{ + decNumber res; + decNumberCompare(&res, lhs, rhs, &decimal_context); + int r = decNumberToInt32(&res, &decimal_context); + assert(decimal_check_status(&decimal_context) == 0); + return r; +} + +decimal_t * +decimal_round(decimal_t *dec, int scale) +{ + if (scale < 0 || scale > DECIMAL_MAX_DIGITS) + return NULL; + + if (scale > decimal_scale(dec)) + return dec; + + int ndig = decimal_precision(dec) - decimal_scale(dec) + scale; + decContext context = { + ndig, /* Precision */ + ndig - 1, /* emax */ + -1, /* emin */ + DECIMAL_ROUNDING, /* rounding */ + 0, /* no traps */ + 0, /* zero status */ + 0 /* no clamping */ + }; + + decNumberPlus(dec, dec, &context); + assert(decimal_check_status(&context) == 0); + return dec; +} + +decimal_t * +decimal_abs(decimal_t *res, const decimal_t *dec) +{ + decNumberAbs(res, dec, &decimal_context); + assert(decimal_check_status(&decimal_context) == 0); + return res; +} + +decimal_t * +decimal_minus(decimal_t *res, const decimal_t *dec) +{ + decNumberMinus(res, dec, &decimal_context); + assert(decimal_check_status(&decimal_context) == 0); + return res; +} + +decimal_t * +decimal_add(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs) +{ + decNumberAdd(res, lhs, rhs, &decimal_context); + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_sub(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs) +{ + decNumberSubtract(res, lhs, rhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_mul(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs) +{ + decNumberMultiply(res, lhs, rhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_div(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs) +{ + decNumberDivide(res, lhs, rhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_log10(decimal_t *res, const decimal_t *lhs) +{ + decNumberLog10(res, lhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_ln(decimal_t *res, const decimal_t *lhs) +{ + decNumberLn(res, lhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_pow(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs) +{ + decNumberPower(res, lhs, rhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_exp(decimal_t *res, const decimal_t *lhs) +{ + decNumberExp(res, lhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +decimal_t * +decimal_sqrt(decimal_t *res, const decimal_t *lhs) +{ + decNumberSquareRoot(res, lhs, &decimal_context); + + if (decimal_check_status(&decimal_context) != 0) { + return NULL; + } else { + return res; + } +} + +uint32_t +decimal_len(decimal_t *dec) +{ + /* 1 + ceil((digits + 1) / 2) */ + return 2 + dec->digits / 2; +} + +char * +decimal_pack(char *data, decimal_t *dec) +{ + uint32_t len = decimal_len(dec); + *data++ = decimal_scale(dec); + len--; + int32_t scale; + char *tmp = (char *)decPackedFromNumber((uint8_t *)data, len, &scale, dec); + assert(tmp == data); + assert(scale == (int32_t)decimal_scale(dec)); + (void)tmp; + data += len; + return data; +} + +decimal_t * +decimal_unpack(const char **data, decimal_t *dec, uint32_t len) +{ + int32_t scale = *((*data)++); + len--; + decimal_t *res = decPackedToNumber((uint8_t *)*data, len, &scale, dec); + if (res) + *data += len; + else + (*data)--; + return res; +} diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h new file mode 100644 index 000000000..0397097a7 --- /dev/null +++ b/src/lib/core/decimal.h @@ -0,0 +1,206 @@ +#ifndef TARANTOOL_LIB_CORE_DECIMAL_H_INCLUDED +#define TARANTOOL_LIB_CORE_DECIMAL_H_INCLUDED +/* + * Copyright 2019, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** Maximum decimal digigts taken by a decimal representation. */ +#define DECIMAL_MAX_DIGITS 38 +#define DECNUMDIGITS DECIMAL_MAX_DIGITS +#include "third_party/decNumber/decNumber.h" +#include + +typedef decNumber decimal_t; + +/** + * @return decimal precision, + * i.e. the amount of decimal digits in + * its representation. + */ +int +decimal_precision(const decimal_t *dec); + +/** + * @return decimal scale, + * i.e. the number of decimal digits after + * the decimal separator. + */ +int +decimal_scale(const decimal_t *dec); + +/** + * Initialize a zero decimal number. + */ +decimal_t * +decimal_zero(decimal_t *dec); + +/** + * Initialize a decimal with a value from the string. + * + * If the number is less, than 10^DECIMAL_MAX_DIGITS, + * but has excess digits in fractional part, it will be rounded. + * + * @return NULL if string is invalid or + * the number is too big (>= 10^DECIMAL_MAX_DIGITS) + */ +decimal_t * +decimal_from_string(decimal_t *dec, const char *str); + +/** + * Initialize a decimal from double. + * + * @return NULL is double is NaN or Infinity, + * or is greater than 10^DECIMAL_MAX_DIGITS. + * \a dec otherwise. + */ +decimal_t * +decimal_from_double(decimal_t *dec, double d); + +/** + * Initialize a decimal with an integer value. + * +*/ +decimal_t * +decimal_from_int64(decimal_t *dec, int64_t num); + +/** @copydoc decimal_from_int */ +decimal_t * +decimal_from_uint64(decimal_t *dec, uint64_t num); + +/** + * Write the decimal to a string. + * Returns a statically allocated buffer containing + * the decimal representation. + */ +const char * +decimal_to_string(const decimal_t *dec); + +/** + * Convert \a dec to double. + */ +double +decimal_to_double(const decimal_t *dec); + +/** + * Compare 2 decimal values. + * @return -1, lhs < rhs, + * 0, lhs = rhs, + * 1, lhs > rhs + */ +int +decimal_compare(const decimal_t *lhs, const decimal_t *rhs); + + +/** + * Round a given decimal to have not more than + * scale digits after the decimal point. + * If scale if greater than current dec scale, do nothing. + * Scale must be in range [0, DECIMAL_MAX_DIGITS] + * + * @return NULL, scale is out of bounds. + * + */ +decimal_t * +decimal_round(decimal_t *dec, int scale); + +/** + * res is set to the absolute value of dec + * decimal_abs(&a, &a) is allowed. + */ +decimal_t * +decimal_abs(decimal_t *res, const decimal_t *dec); + +/** res is set to -dec. */ +decimal_t * +decimal_minus(decimal_t *res, const decimal_t *dec); + +/* + * Arithmetic ops: add, subtract, multiply and divide. + * Return result pointer on success, NULL on an error (overflow). + */ + +decimal_t * +decimal_add(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs); + +decimal_t * +decimal_sub(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs); + +decimal_t * +decimal_mul(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs); + +decimal_t * +decimal_div(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs); + +/* + * log10, ln, pow, exp, sqrt. + * Calculate the appropriate function with maximum precision + * (DECIMAL_MAX_DIGITS) + * Return result pointer on success, NULL on an error (overflow). + */ +decimal_t * +decimal_log10(decimal_t *res, const decimal_t *lhs); + +decimal_t * +decimal_ln(decimal_t *res, const decimal_t *lhs); + +decimal_t * +decimal_pow(decimal_t *res, const decimal_t *lhs, const decimal_t *rhs); + +decimal_t * +decimal_exp(decimal_t *res, const decimal_t *lhs); + +decimal_t * +decimal_sqrt(decimal_t *res, const decimal_t *lhs); + +/** @return The length in bytes decimal packed representation will take. */ +uint32_t +decimal_len(decimal_t *dec); + +/** + * Convert a decimal \a dec to its packed representation. + * + * @return data + decimal_len(dec); + */ +char * +decimal_pack(char *data, decimal_t *dec); + +/** + * Using a packed representation of size \a len pointed to by + * *data, unpack it to \a dec. + * + * \post *data = *data + decimal_len(dec); + * + * @return NULL if value encoding is incorrect + * dec otherwise. + */ +decimal_t * +decimal_unpack(const char **data, decimal_t *dec, uint32_t len); + +#endif /* TARANTOOL_LIB_CORE_DECIMAL_H_INCLUDED */ diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 70be6366c..e3de34b16 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -68,6 +68,8 @@ add_executable(vclock.test vclock.cc) target_link_libraries(vclock.test vclock unit) add_executable(xrow.test xrow.cc) target_link_libraries(xrow.test xrow unit) +add_executable(decimal.test decimal.c) +target_link_libraries(decimal.test core unit) add_executable(fiber.test fiber.cc) set_source_files_properties(fiber.cc PROPERTIES COMPILE_FLAGS -O0) diff --git a/test/unit/decimal.c b/test/unit/decimal.c new file mode 100644 index 000000000..db2082785 --- /dev/null +++ b/test/unit/decimal.c @@ -0,0 +1,174 @@ +#include "unit.h" +#include "decimal.h" +#include +#include +#include /* DBL_DIG */ + +#define success(x) x +#define failure(x) NULL + +#define dectest(a, b, type, cast) ({\ + decimal_t t, u, v, w;\ + is(decimal_from_##type(&u, (a)), &u, "decimal("#a")");\ + is(decimal_from_##type(&v, (b)), &v, "decimal("#b")");\ + \ + is(decimal_add(&t, &u, &v), &t, "decimal("#a") + decimal("#b")");\ + is(decimal_from_##type(&w, (cast)(a) + (cast)(b)), &w, "decimal(("#a") + ("#b"))");\ + is(decimal_compare(&t, &w), 0, "decimal("#a") + decimal("#b") == ("#a") + ("#b")");\ + \ + is(decimal_sub(&t, &u, &v), &t, "decimal("#a") - decimal("#b")");\ + is(decimal_from_##type(&w, (cast)(a) - (cast)(b)), &w, "decimal(("#a") - ("#b"))");\ + is(decimal_compare(&t, &w), 0, "decimal("#a") - decimal("#b") == ("#a") - ("#b")");\ + \ + is(decimal_mul(&t, &u, &v), &t, "decimal("#a") * decimal("#b")");\ + is(decimal_from_##type(&w, (cast)(a) * (cast)(b)), &w, "decimal(("#a") * ("#b"))");\ + is(decimal_round(&t, DBL_DIG), &t, "decimal_round(("#a") * ("#b"), %d)", DBL_DIG);\ + is(decimal_compare(&t, &w), 0, "decimal("#a") * decimal("#b") == ("#a") * ("#b")");\ + \ + is(decimal_div(&t, &u, &v), &t, "decimal("#a") / decimal("#b")");\ + is(decimal_from_double(&w, (double)((a)) / (b)), &w, "decimal(("#a") / ("#b"))");\ + is(decimal_round(&t, DBL_DIG), &t, "decimal_round(("#a")/("#b"), %d)", DBL_DIG);\ + is(decimal_compare(&t, &w), 0, "decimal("#a") / decimal("#b") == ("#a") / ("#b")");\ +}) + +#define dectest_op(op, stra, strb, expected) ({\ + decimal_t a, b, c, d;\ + is(decimal_from_string(&a, #stra), &a, "decimal_from_string("#stra")");\ + is(decimal_from_string(&b, #strb), &b, "decimal_from_string("#strb")");\ + is(decimal_from_string(&d, #expected), &d, "decimal_from_string("#expected")");\ + is(decimal_##op(&c, &a, &b), &c, "decimal_"#op"("#stra", "#strb")");\ + is(decimal_compare(&c, &d), 0, "decimal_compare("#expected")");\ +}) + +#define dectest_op1(op, stra, expected, scale) ({\ + decimal_t a, c, d;\ + is(decimal_from_string(&a, #stra), &a, "decimal_from_string("#stra")");\ + is(decimal_from_string(&d, #expected), &d, "decimal_from_string("#expected")");\ + is(decimal_##op(&c, &a), &c, "decimal_"#op"("#stra")");\ + if (scale > 0)\ + decimal_round(&c, scale);\ + is(decimal_compare(&c, &d), 0, "decimal_compare("#expected")");\ +}) + +#define dectest_construct(type, a, expect) ({\ + decimal_t dec;\ + is(decimal_from_##type(&dec, a), expect(&dec), "decimal construction from "#a" "#expect);\ +}) + +#define dectest_op_fail(op, stra, strb) ({\ + decimal_t a, b, c;\ + is(decimal_from_string(&a, #stra), &a, "decimal_from_string("#stra")");\ + is(decimal_from_string(&b, #strb), &b, "decimal_from_string("#strb")");\ + is(decimal_##op(&c, &a, &b), NULL, "decimal_"#op"("#stra", "#strb") - overflow");\ +}) + +char buf[32]; + +#define test_decpack(str) ({\ + decimal_t dec;\ + decimal_from_string(&dec, str);\ + uint32_t l1 = decimal_len(&dec);\ + ok(l1 <= 21 && l1 >= 2, "decimal_len("str")");\ + char *b1 = decimal_pack(buf, &dec);\ + is(b1, buf + l1, "decimal_len("str") == len(decimal_pack("str")");\ + const char *b2 = buf;\ + decimal_t d2;\ + is(decimal_unpack(&b2, &d2, l1), &d2, "decimal_unpack(decimal_pack("str"))");\ + is(b1, b2, "decimal_unpack(decimal_pack("str")) len");\ + is(decimal_compare(&dec, &d2), 0, "decimal_unpack(decimal_pack("str")) value");\ + is(decimal_scale(&dec), decimal_scale(&d2), "decimal_unpack(decimal_pack("str")) scale");\ + is(decimal_precision(&dec), decimal_precision(&d2), "decimal_unpack(decimal_pack("str")) precision");\ + is(strcmp(decimal_to_string(&d2), str), 0, "str(decimal_unpack(decimal_pack("str")) == "str);\ +}) + +static int +test_pack_unpack(void) +{ + plan(146); + + test_decpack("0"); + test_decpack("-0"); + test_decpack("1"); + test_decpack("-1"); + test_decpack("0.1"); + test_decpack("-0.1"); + test_decpack("2.718281828459045"); + test_decpack("-2.718281828459045"); + test_decpack("3.141592653589793"); + test_decpack("-3.141592653589793"); + test_decpack("1234567891234567890.0987654321987654321"); + test_decpack("-1234567891234567890.0987654321987654321"); + test_decpack("0.0000000000000000000000000000000000001"); + test_decpack("-0.0000000000000000000000000000000000001"); + test_decpack("0.00000000000000000000000000000000000001"); + test_decpack("-0.00000000000000000000000000000000000001"); + test_decpack("99999999999999999999999999999999999999"); + test_decpack("-99999999999999999999999999999999999999"); + + /* Pack an invalid decimal. */ + char *b = buf; + *b++ = 1; + *b++ = '\xab'; + *b++ = '\xcd'; + const char *bb = buf; + decimal_t dec; + is(decimal_unpack(&bb, &dec, 3), NULL, "unpack malformed decimal fails"); + is(bb, buf, "decode malformed decimal preserves buffer position"); + + return check_plan(); +} + +int +main(void) +{ + plan(258); + + dectest(314, 271, uint64, uint64_t); + dectest(65535, 23456, uint64, uint64_t); + + dectest(0, 1, int64, int64_t); + dectest(0, -1, int64, int64_t); + dectest(-1, 1, int64, int64_t); + dectest(INT_MIN, INT_MAX, int64, int64_t); + dectest(-314, -271, int64, int64_t); + dectest(-159615516, 172916921, int64, int64_t); + + dectest(1.1, 2.3, double, double); + dectest(1e10, 1e10, double, double); + dectest(1.23456789, 4.567890123, double, double); + + dectest_op(add, 1e-38, 1e-38, 2e-38); + dectest_op(add, -1e-38, 1e-38, 0); + /* Check that maximum scale == 38. Otherwise rounding occurs. */ + dectest_op(add, 1e-39, 0, 0); + dectest_op(add, 1e-39, 1e-38, 1e-38); + dectest_op(mul, 1e-19, 1e-19, 1e-38); + dectest_op(add, 1e37, 0, 1e37); + dectest_op(mul, 1e18, 1e18, 1e36); + + dectest_op(pow, 10, 2, 100); + dectest_op(pow, 2, 10, 1024); + dectest_op(pow, 100, 0.5, 10); + + dectest_op1(log10, 100, 2, 0); + dectest_op1(ln, 10, 2.3, 2); + dectest_op1(exp, 2, 7.39, 2); + dectest_op1(sqrt, 100, 10, 0); + + /* 39 digits > DECIMAL_MAX_DIGITS (== 38) */ + dectest_construct(double, 2e38, failure); + dectest_construct(string, "1e38", failure); + dectest_construct(string, "100000000000000000000000000000000000000", failure); + + dectest_construct(int64, LONG_MIN, success); + dectest_construct(int64, LONG_MAX, success); + dectest_construct(uint64, ULONG_MAX, success); + + dectest_op_fail(add, 9e37, 1e37); + dectest_op_fail(mul, 1e19, 1e19); + dectest_op_fail(div, 1e19, 1e-19); + + test_pack_unpack(); + + return check_plan(); +} diff --git a/test/unit/decimal.result b/test/unit/decimal.result new file mode 100644 index 000000000..051dc7960 --- /dev/null +++ b/test/unit/decimal.result @@ -0,0 +1,406 @@ +1..258 +ok 1 - decimal(314) +ok 2 - decimal(271) +ok 3 - decimal(314) + decimal(271) +ok 4 - decimal((314) + (271)) +ok 5 - decimal(314) + decimal(271) == (314) + (271) +ok 6 - decimal(314) - decimal(271) +ok 7 - decimal((314) - (271)) +ok 8 - decimal(314) - decimal(271) == (314) - (271) +ok 9 - decimal(314) * decimal(271) +ok 10 - decimal((314) * (271)) +ok 11 - decimal_round((314) * (271), 15) +ok 12 - decimal(314) * decimal(271) == (314) * (271) +ok 13 - decimal(314) / decimal(271) +ok 14 - decimal((314) / (271)) +ok 15 - decimal_round((314)/(271), 15) +ok 16 - decimal(314) / decimal(271) == (314) / (271) +ok 17 - decimal(65535) +ok 18 - decimal(23456) +ok 19 - decimal(65535) + decimal(23456) +ok 20 - decimal((65535) + (23456)) +ok 21 - decimal(65535) + decimal(23456) == (65535) + (23456) +ok 22 - decimal(65535) - decimal(23456) +ok 23 - decimal((65535) - (23456)) +ok 24 - decimal(65535) - decimal(23456) == (65535) - (23456) +ok 25 - decimal(65535) * decimal(23456) +ok 26 - decimal((65535) * (23456)) +ok 27 - decimal_round((65535) * (23456), 15) +ok 28 - decimal(65535) * decimal(23456) == (65535) * (23456) +ok 29 - decimal(65535) / decimal(23456) +ok 30 - decimal((65535) / (23456)) +ok 31 - decimal_round((65535)/(23456), 15) +ok 32 - decimal(65535) / decimal(23456) == (65535) / (23456) +ok 33 - decimal(0) +ok 34 - decimal(1) +ok 35 - decimal(0) + decimal(1) +ok 36 - decimal((0) + (1)) +ok 37 - decimal(0) + decimal(1) == (0) + (1) +ok 38 - decimal(0) - decimal(1) +ok 39 - decimal((0) - (1)) +ok 40 - decimal(0) - decimal(1) == (0) - (1) +ok 41 - decimal(0) * decimal(1) +ok 42 - decimal((0) * (1)) +ok 43 - decimal_round((0) * (1), 15) +ok 44 - decimal(0) * decimal(1) == (0) * (1) +ok 45 - decimal(0) / decimal(1) +ok 46 - decimal((0) / (1)) +ok 47 - decimal_round((0)/(1), 15) +ok 48 - decimal(0) / decimal(1) == (0) / (1) +ok 49 - decimal(0) +ok 50 - decimal(-1) +ok 51 - decimal(0) + decimal(-1) +ok 52 - decimal((0) + (-1)) +ok 53 - decimal(0) + decimal(-1) == (0) + (-1) +ok 54 - decimal(0) - decimal(-1) +ok 55 - decimal((0) - (-1)) +ok 56 - decimal(0) - decimal(-1) == (0) - (-1) +ok 57 - decimal(0) * decimal(-1) +ok 58 - decimal((0) * (-1)) +ok 59 - decimal_round((0) * (-1), 15) +ok 60 - decimal(0) * decimal(-1) == (0) * (-1) +ok 61 - decimal(0) / decimal(-1) +ok 62 - decimal((0) / (-1)) +ok 63 - decimal_round((0)/(-1), 15) +ok 64 - decimal(0) / decimal(-1) == (0) / (-1) +ok 65 - decimal(-1) +ok 66 - decimal(1) +ok 67 - decimal(-1) + decimal(1) +ok 68 - decimal((-1) + (1)) +ok 69 - decimal(-1) + decimal(1) == (-1) + (1) +ok 70 - decimal(-1) - decimal(1) +ok 71 - decimal((-1) - (1)) +ok 72 - decimal(-1) - decimal(1) == (-1) - (1) +ok 73 - decimal(-1) * decimal(1) +ok 74 - decimal((-1) * (1)) +ok 75 - decimal_round((-1) * (1), 15) +ok 76 - decimal(-1) * decimal(1) == (-1) * (1) +ok 77 - decimal(-1) / decimal(1) +ok 78 - decimal((-1) / (1)) +ok 79 - decimal_round((-1)/(1), 15) +ok 80 - decimal(-1) / decimal(1) == (-1) / (1) +ok 81 - decimal(INT_MIN) +ok 82 - decimal(INT_MAX) +ok 83 - decimal(INT_MIN) + decimal(INT_MAX) +ok 84 - decimal((INT_MIN) + (INT_MAX)) +ok 85 - decimal(INT_MIN) + decimal(INT_MAX) == (INT_MIN) + (INT_MAX) +ok 86 - decimal(INT_MIN) - decimal(INT_MAX) +ok 87 - decimal((INT_MIN) - (INT_MAX)) +ok 88 - decimal(INT_MIN) - decimal(INT_MAX) == (INT_MIN) - (INT_MAX) +ok 89 - decimal(INT_MIN) * decimal(INT_MAX) +ok 90 - decimal((INT_MIN) * (INT_MAX)) +ok 91 - decimal_round((INT_MIN) * (INT_MAX), 15) +ok 92 - decimal(INT_MIN) * decimal(INT_MAX) == (INT_MIN) * (INT_MAX) +ok 93 - decimal(INT_MIN) / decimal(INT_MAX) +ok 94 - decimal((INT_MIN) / (INT_MAX)) +ok 95 - decimal_round((INT_MIN)/(INT_MAX), 15) +ok 96 - decimal(INT_MIN) / decimal(INT_MAX) == (INT_MIN) / (INT_MAX) +ok 97 - decimal(-314) +ok 98 - decimal(-271) +ok 99 - decimal(-314) + decimal(-271) +ok 100 - decimal((-314) + (-271)) +ok 101 - decimal(-314) + decimal(-271) == (-314) + (-271) +ok 102 - decimal(-314) - decimal(-271) +ok 103 - decimal((-314) - (-271)) +ok 104 - decimal(-314) - decimal(-271) == (-314) - (-271) +ok 105 - decimal(-314) * decimal(-271) +ok 106 - decimal((-314) * (-271)) +ok 107 - decimal_round((-314) * (-271), 15) +ok 108 - decimal(-314) * decimal(-271) == (-314) * (-271) +ok 109 - decimal(-314) / decimal(-271) +ok 110 - decimal((-314) / (-271)) +ok 111 - decimal_round((-314)/(-271), 15) +ok 112 - decimal(-314) / decimal(-271) == (-314) / (-271) +ok 113 - decimal(-159615516) +ok 114 - decimal(172916921) +ok 115 - decimal(-159615516) + decimal(172916921) +ok 116 - decimal((-159615516) + (172916921)) +ok 117 - decimal(-159615516) + decimal(172916921) == (-159615516) + (172916921) +ok 118 - decimal(-159615516) - decimal(172916921) +ok 119 - decimal((-159615516) - (172916921)) +ok 120 - decimal(-159615516) - decimal(172916921) == (-159615516) - (172916921) +ok 121 - decimal(-159615516) * decimal(172916921) +ok 122 - decimal((-159615516) * (172916921)) +ok 123 - decimal_round((-159615516) * (172916921), 15) +ok 124 - decimal(-159615516) * decimal(172916921) == (-159615516) * (172916921) +ok 125 - decimal(-159615516) / decimal(172916921) +ok 126 - decimal((-159615516) / (172916921)) +ok 127 - decimal_round((-159615516)/(172916921), 15) +ok 128 - decimal(-159615516) / decimal(172916921) == (-159615516) / (172916921) +ok 129 - decimal(1.1) +ok 130 - decimal(2.3) +ok 131 - decimal(1.1) + decimal(2.3) +ok 132 - decimal((1.1) + (2.3)) +ok 133 - decimal(1.1) + decimal(2.3) == (1.1) + (2.3) +ok 134 - decimal(1.1) - decimal(2.3) +ok 135 - decimal((1.1) - (2.3)) +ok 136 - decimal(1.1) - decimal(2.3) == (1.1) - (2.3) +ok 137 - decimal(1.1) * decimal(2.3) +ok 138 - decimal((1.1) * (2.3)) +ok 139 - decimal_round((1.1) * (2.3), 15) +ok 140 - decimal(1.1) * decimal(2.3) == (1.1) * (2.3) +ok 141 - decimal(1.1) / decimal(2.3) +ok 142 - decimal((1.1) / (2.3)) +ok 143 - decimal_round((1.1)/(2.3), 15) +ok 144 - decimal(1.1) / decimal(2.3) == (1.1) / (2.3) +ok 145 - decimal(1e10) +ok 146 - decimal(1e10) +ok 147 - decimal(1e10) + decimal(1e10) +ok 148 - decimal((1e10) + (1e10)) +ok 149 - decimal(1e10) + decimal(1e10) == (1e10) + (1e10) +ok 150 - decimal(1e10) - decimal(1e10) +ok 151 - decimal((1e10) - (1e10)) +ok 152 - decimal(1e10) - decimal(1e10) == (1e10) - (1e10) +ok 153 - decimal(1e10) * decimal(1e10) +ok 154 - decimal((1e10) * (1e10)) +ok 155 - decimal_round((1e10) * (1e10), 15) +ok 156 - decimal(1e10) * decimal(1e10) == (1e10) * (1e10) +ok 157 - decimal(1e10) / decimal(1e10) +ok 158 - decimal((1e10) / (1e10)) +ok 159 - decimal_round((1e10)/(1e10), 15) +ok 160 - decimal(1e10) / decimal(1e10) == (1e10) / (1e10) +ok 161 - decimal(1.23456789) +ok 162 - decimal(4.567890123) +ok 163 - decimal(1.23456789) + decimal(4.567890123) +ok 164 - decimal((1.23456789) + (4.567890123)) +ok 165 - decimal(1.23456789) + decimal(4.567890123) == (1.23456789) + (4.567890123) +ok 166 - decimal(1.23456789) - decimal(4.567890123) +ok 167 - decimal((1.23456789) - (4.567890123)) +ok 168 - decimal(1.23456789) - decimal(4.567890123) == (1.23456789) - (4.567890123) +ok 169 - decimal(1.23456789) * decimal(4.567890123) +ok 170 - decimal((1.23456789) * (4.567890123)) +ok 171 - decimal_round((1.23456789) * (4.567890123), 15) +ok 172 - decimal(1.23456789) * decimal(4.567890123) == (1.23456789) * (4.567890123) +ok 173 - decimal(1.23456789) / decimal(4.567890123) +ok 174 - decimal((1.23456789) / (4.567890123)) +ok 175 - decimal_round((1.23456789)/(4.567890123), 15) +ok 176 - decimal(1.23456789) / decimal(4.567890123) == (1.23456789) / (4.567890123) +ok 177 - decimal_from_string(1e-38) +ok 178 - decimal_from_string(1e-38) +ok 179 - decimal_from_string(2e-38) +ok 180 - decimal_add(1e-38, 1e-38) +ok 181 - decimal_compare(2e-38) +ok 182 - decimal_from_string(-1e-38) +ok 183 - decimal_from_string(1e-38) +ok 184 - decimal_from_string(0) +ok 185 - decimal_add(-1e-38, 1e-38) +ok 186 - decimal_compare(0) +ok 187 - decimal_from_string(1e-39) +ok 188 - decimal_from_string(0) +ok 189 - decimal_from_string(0) +ok 190 - decimal_add(1e-39, 0) +ok 191 - decimal_compare(0) +ok 192 - decimal_from_string(1e-39) +ok 193 - decimal_from_string(1e-38) +ok 194 - decimal_from_string(1e-38) +ok 195 - decimal_add(1e-39, 1e-38) +ok 196 - decimal_compare(1e-38) +ok 197 - decimal_from_string(1e-19) +ok 198 - decimal_from_string(1e-19) +ok 199 - decimal_from_string(1e-38) +ok 200 - decimal_mul(1e-19, 1e-19) +ok 201 - decimal_compare(1e-38) +ok 202 - decimal_from_string(1e37) +ok 203 - decimal_from_string(0) +ok 204 - decimal_from_string(1e37) +ok 205 - decimal_add(1e37, 0) +ok 206 - decimal_compare(1e37) +ok 207 - decimal_from_string(1e18) +ok 208 - decimal_from_string(1e18) +ok 209 - decimal_from_string(1e36) +ok 210 - decimal_mul(1e18, 1e18) +ok 211 - decimal_compare(1e36) +ok 212 - decimal_from_string(10) +ok 213 - decimal_from_string(2) +ok 214 - decimal_from_string(100) +ok 215 - decimal_pow(10, 2) +ok 216 - decimal_compare(100) +ok 217 - decimal_from_string(2) +ok 218 - decimal_from_string(10) +ok 219 - decimal_from_string(1024) +ok 220 - decimal_pow(2, 10) +ok 221 - decimal_compare(1024) +ok 222 - decimal_from_string(100) +ok 223 - decimal_from_string(0.5) +ok 224 - decimal_from_string(10) +ok 225 - decimal_pow(100, 0.5) +ok 226 - decimal_compare(10) +ok 227 - decimal_from_string(100) +ok 228 - decimal_from_string(2) +ok 229 - decimal_log10(100) +ok 230 - decimal_compare(2) +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 + 1..146 + ok 1 - decimal_len(0) + ok 2 - decimal_len(0) == len(decimal_pack(0) + ok 3 - decimal_unpack(decimal_pack(0)) + ok 4 - decimal_unpack(decimal_pack(0)) len + ok 5 - decimal_unpack(decimal_pack(0)) value + ok 6 - decimal_unpack(decimal_pack(0)) scale + ok 7 - decimal_unpack(decimal_pack(0)) precision + ok 8 - str(decimal_unpack(decimal_pack(0)) == 0 + ok 9 - decimal_len(-0) + ok 10 - decimal_len(-0) == len(decimal_pack(-0) + ok 11 - decimal_unpack(decimal_pack(-0)) + ok 12 - decimal_unpack(decimal_pack(-0)) len + ok 13 - decimal_unpack(decimal_pack(-0)) value + ok 14 - decimal_unpack(decimal_pack(-0)) scale + ok 15 - decimal_unpack(decimal_pack(-0)) precision + ok 16 - str(decimal_unpack(decimal_pack(-0)) == -0 + ok 17 - decimal_len(1) + ok 18 - decimal_len(1) == len(decimal_pack(1) + ok 19 - decimal_unpack(decimal_pack(1)) + ok 20 - decimal_unpack(decimal_pack(1)) len + ok 21 - decimal_unpack(decimal_pack(1)) value + ok 22 - decimal_unpack(decimal_pack(1)) scale + ok 23 - decimal_unpack(decimal_pack(1)) precision + ok 24 - str(decimal_unpack(decimal_pack(1)) == 1 + ok 25 - decimal_len(-1) + ok 26 - decimal_len(-1) == len(decimal_pack(-1) + ok 27 - decimal_unpack(decimal_pack(-1)) + ok 28 - decimal_unpack(decimal_pack(-1)) len + ok 29 - decimal_unpack(decimal_pack(-1)) value + ok 30 - decimal_unpack(decimal_pack(-1)) scale + ok 31 - decimal_unpack(decimal_pack(-1)) precision + ok 32 - str(decimal_unpack(decimal_pack(-1)) == -1 + ok 33 - decimal_len(0.1) + ok 34 - decimal_len(0.1) == len(decimal_pack(0.1) + ok 35 - decimal_unpack(decimal_pack(0.1)) + ok 36 - decimal_unpack(decimal_pack(0.1)) len + ok 37 - decimal_unpack(decimal_pack(0.1)) value + ok 38 - decimal_unpack(decimal_pack(0.1)) scale + ok 39 - decimal_unpack(decimal_pack(0.1)) precision + ok 40 - str(decimal_unpack(decimal_pack(0.1)) == 0.1 + ok 41 - decimal_len(-0.1) + ok 42 - decimal_len(-0.1) == len(decimal_pack(-0.1) + ok 43 - decimal_unpack(decimal_pack(-0.1)) + ok 44 - decimal_unpack(decimal_pack(-0.1)) len + ok 45 - decimal_unpack(decimal_pack(-0.1)) value + ok 46 - decimal_unpack(decimal_pack(-0.1)) scale + ok 47 - decimal_unpack(decimal_pack(-0.1)) precision + ok 48 - str(decimal_unpack(decimal_pack(-0.1)) == -0.1 + ok 49 - decimal_len(2.718281828459045) + ok 50 - decimal_len(2.718281828459045) == len(decimal_pack(2.718281828459045) + ok 51 - decimal_unpack(decimal_pack(2.718281828459045)) + ok 52 - decimal_unpack(decimal_pack(2.718281828459045)) len + ok 53 - decimal_unpack(decimal_pack(2.718281828459045)) value + ok 54 - decimal_unpack(decimal_pack(2.718281828459045)) scale + ok 55 - decimal_unpack(decimal_pack(2.718281828459045)) precision + ok 56 - str(decimal_unpack(decimal_pack(2.718281828459045)) == 2.718281828459045 + ok 57 - decimal_len(-2.718281828459045) + ok 58 - decimal_len(-2.718281828459045) == len(decimal_pack(-2.718281828459045) + ok 59 - decimal_unpack(decimal_pack(-2.718281828459045)) + ok 60 - decimal_unpack(decimal_pack(-2.718281828459045)) len + ok 61 - decimal_unpack(decimal_pack(-2.718281828459045)) value + ok 62 - decimal_unpack(decimal_pack(-2.718281828459045)) scale + ok 63 - decimal_unpack(decimal_pack(-2.718281828459045)) precision + ok 64 - str(decimal_unpack(decimal_pack(-2.718281828459045)) == -2.718281828459045 + ok 65 - decimal_len(3.141592653589793) + ok 66 - decimal_len(3.141592653589793) == len(decimal_pack(3.141592653589793) + ok 67 - decimal_unpack(decimal_pack(3.141592653589793)) + ok 68 - decimal_unpack(decimal_pack(3.141592653589793)) len + ok 69 - decimal_unpack(decimal_pack(3.141592653589793)) value + ok 70 - decimal_unpack(decimal_pack(3.141592653589793)) scale + ok 71 - decimal_unpack(decimal_pack(3.141592653589793)) precision + ok 72 - str(decimal_unpack(decimal_pack(3.141592653589793)) == 3.141592653589793 + ok 73 - decimal_len(-3.141592653589793) + ok 74 - decimal_len(-3.141592653589793) == len(decimal_pack(-3.141592653589793) + ok 75 - decimal_unpack(decimal_pack(-3.141592653589793)) + ok 76 - decimal_unpack(decimal_pack(-3.141592653589793)) len + ok 77 - decimal_unpack(decimal_pack(-3.141592653589793)) value + ok 78 - decimal_unpack(decimal_pack(-3.141592653589793)) scale + ok 79 - decimal_unpack(decimal_pack(-3.141592653589793)) precision + ok 80 - str(decimal_unpack(decimal_pack(-3.141592653589793)) == -3.141592653589793 + ok 81 - decimal_len(1234567891234567890.0987654321987654321) + ok 82 - decimal_len(1234567891234567890.0987654321987654321) == len(decimal_pack(1234567891234567890.0987654321987654321) + ok 83 - decimal_unpack(decimal_pack(1234567891234567890.0987654321987654321)) + ok 84 - decimal_unpack(decimal_pack(1234567891234567890.0987654321987654321)) len + ok 85 - decimal_unpack(decimal_pack(1234567891234567890.0987654321987654321)) value + ok 86 - decimal_unpack(decimal_pack(1234567891234567890.0987654321987654321)) scale + ok 87 - decimal_unpack(decimal_pack(1234567891234567890.0987654321987654321)) precision + ok 88 - str(decimal_unpack(decimal_pack(1234567891234567890.0987654321987654321)) == 1234567891234567890.0987654321987654321 + ok 89 - decimal_len(-1234567891234567890.0987654321987654321) + ok 90 - decimal_len(-1234567891234567890.0987654321987654321) == len(decimal_pack(-1234567891234567890.0987654321987654321) + ok 91 - decimal_unpack(decimal_pack(-1234567891234567890.0987654321987654321)) + ok 92 - decimal_unpack(decimal_pack(-1234567891234567890.0987654321987654321)) len + ok 93 - decimal_unpack(decimal_pack(-1234567891234567890.0987654321987654321)) value + ok 94 - decimal_unpack(decimal_pack(-1234567891234567890.0987654321987654321)) scale + ok 95 - decimal_unpack(decimal_pack(-1234567891234567890.0987654321987654321)) precision + ok 96 - str(decimal_unpack(decimal_pack(-1234567891234567890.0987654321987654321)) == -1234567891234567890.0987654321987654321 + ok 97 - decimal_len(0.0000000000000000000000000000000000001) + ok 98 - decimal_len(0.0000000000000000000000000000000000001) == len(decimal_pack(0.0000000000000000000000000000000000001) + ok 99 - decimal_unpack(decimal_pack(0.0000000000000000000000000000000000001)) + ok 100 - decimal_unpack(decimal_pack(0.0000000000000000000000000000000000001)) len + ok 101 - decimal_unpack(decimal_pack(0.0000000000000000000000000000000000001)) value + ok 102 - decimal_unpack(decimal_pack(0.0000000000000000000000000000000000001)) scale + ok 103 - decimal_unpack(decimal_pack(0.0000000000000000000000000000000000001)) precision + ok 104 - str(decimal_unpack(decimal_pack(0.0000000000000000000000000000000000001)) == 0.0000000000000000000000000000000000001 + ok 105 - decimal_len(-0.0000000000000000000000000000000000001) + ok 106 - decimal_len(-0.0000000000000000000000000000000000001) == len(decimal_pack(-0.0000000000000000000000000000000000001) + ok 107 - decimal_unpack(decimal_pack(-0.0000000000000000000000000000000000001)) + ok 108 - decimal_unpack(decimal_pack(-0.0000000000000000000000000000000000001)) len + ok 109 - decimal_unpack(decimal_pack(-0.0000000000000000000000000000000000001)) value + ok 110 - decimal_unpack(decimal_pack(-0.0000000000000000000000000000000000001)) scale + ok 111 - decimal_unpack(decimal_pack(-0.0000000000000000000000000000000000001)) precision + ok 112 - str(decimal_unpack(decimal_pack(-0.0000000000000000000000000000000000001)) == -0.0000000000000000000000000000000000001 + ok 113 - decimal_len(0.00000000000000000000000000000000000001) + ok 114 - decimal_len(0.00000000000000000000000000000000000001) == len(decimal_pack(0.00000000000000000000000000000000000001) + ok 115 - decimal_unpack(decimal_pack(0.00000000000000000000000000000000000001)) + ok 116 - decimal_unpack(decimal_pack(0.00000000000000000000000000000000000001)) len + ok 117 - decimal_unpack(decimal_pack(0.00000000000000000000000000000000000001)) value + ok 118 - decimal_unpack(decimal_pack(0.00000000000000000000000000000000000001)) scale + ok 119 - decimal_unpack(decimal_pack(0.00000000000000000000000000000000000001)) precision + ok 120 - str(decimal_unpack(decimal_pack(0.00000000000000000000000000000000000001)) == 0.00000000000000000000000000000000000001 + ok 121 - decimal_len(-0.00000000000000000000000000000000000001) + ok 122 - decimal_len(-0.00000000000000000000000000000000000001) == len(decimal_pack(-0.00000000000000000000000000000000000001) + ok 123 - decimal_unpack(decimal_pack(-0.00000000000000000000000000000000000001)) + ok 124 - decimal_unpack(decimal_pack(-0.00000000000000000000000000000000000001)) len + ok 125 - decimal_unpack(decimal_pack(-0.00000000000000000000000000000000000001)) value + ok 126 - decimal_unpack(decimal_pack(-0.00000000000000000000000000000000000001)) scale + ok 127 - decimal_unpack(decimal_pack(-0.00000000000000000000000000000000000001)) precision + ok 128 - str(decimal_unpack(decimal_pack(-0.00000000000000000000000000000000000001)) == -0.00000000000000000000000000000000000001 + ok 129 - decimal_len(99999999999999999999999999999999999999) + ok 130 - decimal_len(99999999999999999999999999999999999999) == len(decimal_pack(99999999999999999999999999999999999999) + ok 131 - decimal_unpack(decimal_pack(99999999999999999999999999999999999999)) + ok 132 - decimal_unpack(decimal_pack(99999999999999999999999999999999999999)) len + ok 133 - decimal_unpack(decimal_pack(99999999999999999999999999999999999999)) value + ok 134 - decimal_unpack(decimal_pack(99999999999999999999999999999999999999)) scale + ok 135 - decimal_unpack(decimal_pack(99999999999999999999999999999999999999)) precision + ok 136 - str(decimal_unpack(decimal_pack(99999999999999999999999999999999999999)) == 99999999999999999999999999999999999999 + ok 137 - decimal_len(-99999999999999999999999999999999999999) + ok 138 - decimal_len(-99999999999999999999999999999999999999) == len(decimal_pack(-99999999999999999999999999999999999999) + ok 139 - decimal_unpack(decimal_pack(-99999999999999999999999999999999999999)) + ok 140 - decimal_unpack(decimal_pack(-99999999999999999999999999999999999999)) len + ok 141 - decimal_unpack(decimal_pack(-99999999999999999999999999999999999999)) value + ok 142 - decimal_unpack(decimal_pack(-99999999999999999999999999999999999999)) scale + ok 143 - decimal_unpack(decimal_pack(-99999999999999999999999999999999999999)) precision + 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 -- 2.20.1 (Apple Git-117)