* [PATCH v4 2/2] lib/core: introduce decimal type to tarantool
2019-06-11 15:56 [PATCH v4 0/2] Introduce decimal type to tarantool core Serge Petrenko
2019-06-11 15:56 ` [PATCH v4 1/2] third-party: add decNumber library Serge Petrenko
@ 2019-06-11 15:56 ` Serge Petrenko
2019-06-13 16:07 ` Vladimir Davydov
2019-06-11 16:01 ` [tarantool-patches] [PATCH v4 0/2] Introduce decimal type to tarantool core Serge Petrenko
2 siblings, 1 reply; 6+ messages in thread
From: Serge Petrenko @ 2019-06-11 15:56 UTC (permalink / raw)
To: vdavydov.dev; +Cc: georgy, kostja, tarantool-patches, Serge Petrenko
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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 <stddef.h>
+#include <stdlib.h>
+#include <float.h> /* DBL_DIG */
+#include <math.h> /* 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 <stdint.h>
+
+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 <limits.h>
+#include <string.h>
+#include <float.h> /* 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)
^ permalink raw reply [flat|nested] 6+ messages in thread