[PATCH v4 2/2] lib/core: introduce decimal type to tarantool

Serge Petrenko sergepetrenko at tarantool.org
Tue Jun 11 18:56:48 MSK 2019


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)




More information about the Tarantool-patches mailing list