[PATCH v3 2/4] lib/core: introduce decimal type to tarantool
Serge Petrenko
sergepetrenko at tarantool.org
Wed Jun 5 13:03:12 MSK 2019
> 5 июня 2019 г., в 11:11, Георгий Кириченко <georgy at tarantool.org> написал(а):
>
> Overall it looks great but please check travis has an error:
> /tarantool/src/lib/core/decimal.c:319:8: error: unused variable ‘tmp’ [-
> Werror=unused-variable]
> char *tmp = (char *)decPackedFromNumber((uint8_t *)data, len, &scale, dec);
>
> Perhaps, it was caused by assert elimination because of the Release-type
> build.
Hi! Thank you for review.
Fixed. mp_encode_decimal() is actually from the fourth patch.
diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c
index 7b3b1d8c9..fab3eb866 100644
--- a/src/lib/core/decimal.c
+++ b/src/lib/core/decimal.c
@@ -319,6 +319,7 @@ mp_encode_decimal(char *data, decimal *dec)
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;
}
>
> On Tuesday, June 4, 2019 7:04:17 PM MSK Serge Petrenko wrote:
>> 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.
>>
>> 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].
>>
>> Every mathematic function has precision as one of its parameters, and
>> the results are rounded to fit the desired precision.
>>
>> Part of #692
>> ---
>> CMakeLists.txt | 7 +
>> cmake/BuildDecNumber.cmake | 13 ++
>> src/CMakeLists.txt | 1 +
>> src/lib/core/CMakeLists.txt | 3 +-
>> src/lib/core/decimal.c | 293 ++++++++++++++++++++++++++++++++++++
>> src/lib/core/decimal.h | 159 +++++++++++++++++++
>> test/unit/CMakeLists.txt | 2 +
>> test/unit/decimal.c | 168 +++++++++++++++++++++
>> test/unit/decimal.result | 51 +++++++
>> 9 files changed, 696 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..80942fe05
>> --- /dev/null
>> +++ b/cmake/BuildDecNumber.cmake
>> @@ -0,0 +1,13 @@
>> +#
>> +# 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
>> + )
>> +
>> + 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 183cc04ef..9d196755f 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..12250da95
>> --- /dev/null
>> +++ b/src/lib/core/decimal.c
>> @@ -0,0 +1,293 @@
>> +/*
>> + * 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 <stdlib.h>
>> +#include <assert.h>
>> +
>> +#define MAX(a, b) ((a) > (b) ? (a) : (b))
>> +#define MIN(a, b) ((a) > (b) ? (b) : (a))
>> +
>> +/** A single context for all the arithmetic operations. */
>> +static decContext decimal_context = {
>> + /* Maximum precision during operations. */
>> + TARANTOOL_MAX_DECIMAL_DIGITS,
>> + /*
>> + * Maximum decimal lagarithm of the number.
>> + * Allows for precision = TARANTOOL_MAX_DECIMAL_DIGITS
>> + */
>> + TARANTOOL_MAX_DECIMAL_DIGITS - 1,
>> + /*
>> + * Minimal adjusted exponent. The smallest absolute value will be
>> + * exp((1 - TARANTOOL_MAX_DECIMAL_DIGITS) - 1) =
>> + * exp(-TARANTOOL_MAX_DECIMAL_DIGITS) allowing for scale =
>> + * TARANTOOL_MAX_DECIMAL_DIGITS
>> + */
>> + -1,
>> + /* Rounding mode: .5 rounds away from 0. */
>> + DEC_ROUND_HALF_UP,
>> + /* Turn off signalling for failed operations. */
>> + 0,
>> + /* Status holding occured events. Initially empty. */
>> + 0,
>> + /* Turn off exponent clamping. */
>> + 0
>> +};
>> +
>> +/**
>> + * Return operation status and clear it for future checks.
>> + *
>> + * @return 0 if ok, bitwise or of decNumber errors, if any.
>> + */
>> +static inline uint32_t
>> +decimal_get_op_status(decContext *context)
>> +{
>> + uint32_t status = decContextGetStatus(context);
>> + decContextZeroStatus(&decimal_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.
>> + */
>> + return status & ~(uint32_t)(DEC_Inexact | DEC_Rounded |
> DEC_Underflow |
>> + DEC_Subnormal | DEC_Clamped);
>> +}
>> +
>> +/**
>> + * A finalizer for all the operations.
>> + * Check the operation context status and empty it.
>> + *
>> + * @return NULL if finalization failed.
>> + * result pointer otherwise.
>> + */
>> +static inline decimal *
>> +decimal_finalize(decimal *res, decContext *context)
>> +{
>> + uint32_t status = decimal_get_op_status(context);
>> + if (status || ! decNumberIsFinite(res)) {
>> + return NULL;
>> + }
>> + return res;
>> +}
>> +
>> +uint8_t decimal_precision(const decimal *dec) {
>> + return dec->exponent <= 0 ? MAX(dec->digits, -dec->exponent) :
>> + dec->digits + dec->exponent;
>> +}
>> +
>> +uint8_t decimal_scale(const decimal *dec) {
>> + return dec->exponent < 0 ? -dec->exponent : 0;
>> +}
>> +
>> +decimal *
>> +decimal_zero(decimal *dec)
>> +{
>> + decNumberZero(dec);
>> + return dec;
>> +}
>> +
>> +decimal *
>> +decimal_from_string(decimal *dec, const char *str)
>> +{
>> + decNumberFromString(dec, str, &decimal_context);
>> + return decimal_finalize(dec, &decimal_context);
>> +}
>> +
>> +decimal *
>> +decimal_from_int(decimal *dec, int32_t num)
>> +{
>> + decNumberFromInt32(dec, num);
>> + return decimal_finalize(dec, &decimal_context);
>> +}
>> +
>> +decimal *
>> +decimal_from_uint(decimal *dec, uint32_t num)
>> +{
>> + decNumberFromUInt32(dec, num);
>> + return decimal_finalize(dec, &decimal_context);
>> +}
>> +
>> +char *
>> +decimal_to_string(const decimal *dec, char *buf)
>> +{
>> + return decNumberToString(dec, buf);
>> +}
>> +
>> +int32_t
>> +decimal_to_int(const decimal *dec)
>> +{
>> + decNumber res;
>> + decNumberToIntegralValue(&res, dec, &decimal_context);
>> + return decNumberToInt32(&res, &decimal_context);
>> +}
>> +
>> +uint32_t
>> +decimal_to_uint(const decimal *dec)
>> +{
>> + decNumber res;
>> + decNumberToIntegralValue(&res, dec, &decimal_context);
>> + return decNumberToUInt32(&res, &decimal_context);
>> +}
>> +
>> +int
>> +decimal_compare(const decimal *lhs, const decimal *rhs)
>> +{
>> + decNumber res;
>> + decNumberCompare(&res, lhs, rhs, &decimal_context);
>> + return decNumberToInt32(&res, &decimal_context);
>> +}
>> +
>> +decimal *
>> +decimal_abs(decimal *res, const decimal *dec)
>> +{
>> + decNumberAbs(res, dec, &decimal_context);
>> + return res;
>> +}
>> +
>> +decimal *
>> +decimal_add(decimal *res, const decimal *lhs, const decimal *rhs)
>> +{
>> + decNumberAdd(res, lhs, rhs, &decimal_context);
>> +
>> + return decimal_finalize(res, &decimal_context);
>> +}
>> +
>> +decimal *
>> +decimal_sub(decimal *res, const decimal *lhs, const decimal *rhs)
>> +{
>> + decNumberSubtract(res, lhs, rhs, &decimal_context);
>> +
>> + return decimal_finalize(res, &decimal_context);
>> +}
>> +
>> +decimal *
>> +decimal_mul(decimal *res, const decimal *lhs, const decimal *rhs)
>> +{
>> + decNumberMultiply(res, lhs, rhs, &decimal_context);
>> +
>> + return decimal_finalize(res, &decimal_context);
>> +}
>> +
>> +decimal *
>> +decimal_div(decimal *res, const decimal *lhs, const decimal *rhs)
>> +{
>> + decNumberDivide(res, lhs, rhs, &decimal_context);
>> +
>> + return decimal_finalize(res, &decimal_context);
>> +}
>> +
>> +decimal *
>> +decimal_log10(decimal *res, const decimal *lhs, uint32_t precision)
>> +{
>> + if (precision >= TARANTOOL_MAX_DECIMAL_DIGITS)
>> + return NULL;
>> +
>> + /* A separate context to enforce desired precision. */
>> + static decContext op_context;
>> + op_context.digits = precision;
>> + op_context.emax = op_context.digits - 1;
>> + op_context.emin = -1;
>> +
>> + decNumberLog10(res, lhs, &op_context);
>> +
>> + return decimal_finalize(res, &op_context);
>> +}
>> +
>> +decimal *
>> +decimal_ln(decimal *res, const decimal *lhs, uint32_t precision)
>> +{
>> + if (precision >= TARANTOOL_MAX_DECIMAL_DIGITS)
>> + return NULL;
>> +
>> + /* A separate context to enforce desired precision. */
>> + static decContext op_context;
>> + op_context.digits = precision;
>> + op_context.emax = op_context.digits - 1;
>> + op_context.emin = -1;
>> +
>> + decNumberLn(res, lhs, &op_context);
>> +
>> + return decimal_finalize(res, &op_context);
>> +}
>> +
>> +decimal *
>> +decimal_pow(decimal *res, const decimal *lhs, const decimal *rhs, uint32_t
>> precision) +{
>> + if (precision >= TARANTOOL_MAX_DECIMAL_DIGITS)
>> + return NULL;
>> +
>> + /* A separate context to enforce desired precision. */
>> + static decContext op_context;
>> + op_context.digits = precision;
>> + op_context.emax = op_context.digits - 1;
>> + op_context.emin = -1;
>> +
>> + decNumberPower(res, lhs, rhs, &op_context);
>> +
>> + return decimal_finalize(res, &op_context);
>> +}
>> +
>> +decimal *
>> +decimal_exp(decimal *res, const decimal *lhs, uint32_t precision)
>> +{
>> + if (precision >= TARANTOOL_MAX_DECIMAL_DIGITS)
>> + return NULL;
>> +
>> + /* A separate context to enforce desired precision. */
>> + static decContext op_context;
>> + op_context.digits = precision;
>> + op_context.emax = op_context.digits - 1;
>> + op_context.emin = -1;
>> +
>> + decNumberExp(res, lhs, &op_context);
>> +
>> + return decimal_finalize(res, &op_context);
>> +}
>> +
>> +decimal *
>> +decimal_sqrt(decimal *res, const decimal *lhs, uint32_t precision)
>> +{
>> + if (precision >= TARANTOOL_MAX_DECIMAL_DIGITS)
>> + return NULL;
>> +
>> + /* A separate context to enforce desired precision. */
>> + static decContext op_context;
>> + op_context.digits = precision;
>> + op_context.emax = op_context.digits - 1;
>> + op_context.emin = -1;
>> +
>> + decNumberSquareRoot(res, lhs, &op_context);
>> +
>> + return decimal_finalize(res, &op_context);
>> +}
>> diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h
>> new file mode 100644
>> index 000000000..f2812f500
>> --- /dev/null
>> +++ b/src/lib/core/decimal.h
>> @@ -0,0 +1,159 @@
>> +#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.
>> + */
>> +#define TARANTOOL_MAX_DECIMAL_DIGITS 38
>> +#define DECNUMDIGITS TARANTOOL_MAX_DECIMAL_DIGITS
>> +#define DECSTRING_NO_EXPONENT
>> +#include "third_party/decNumber/decNumber.h"
>> +
>> +typedef decNumber decimal;
>> +
>> +/**
>> + * @return decimal precision,
>> + * i.e. the amount of decimal digits in
>> + * its representation.
>> + */
>> +uint8_t
>> +decimal_precision(const decimal *dec);
>> +
>> +/**
>> + * @return decimal scale,
>> + * i.e. the number of decimal digits after
>> + * the decimal separator.
>> + */
>> +uint8_t
>> +decimal_scale(const decimal *dec);
>> +
>> +/**
>> + * Initialize a zero decimal number.
>> + */
>> +decimal *
>> +decimal_zero(decimal *dec);
>> +
>> +/**
>> + * Initialize a decimal with a value from the string.
>> + *
>> + * If the number is less, than 10^TARANTOOL_MAX_DECIMAL_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^TARANTOOL_MAX_DECIMAL_DIGITS)
>> + */
>> +decimal *
>> +decimal_from_string(decimal *dec, const char *str);
>> +
>> +/**
>> + * Initialize a decimal with an integer value.
>> + *
>> + * @return NULL if precicion is insufficient to hold
>> + * the value or precision/scale are out of bounds.
>> +*/
>> +decimal *
>> +decimal_from_int(decimal *dec, int32_t num);
>> +
>> +/** @copydoc decimal_from_int */
>> +decimal *
>> +decimal_from_uint(decimal *dec, uint32_t num);
>> +
>> +/**
>> + * Write the decimal to a string.
>> + * A string has to be at least dec->digits + 3 bytes in size.
>> + */
>> +char *
>> +decimal_to_string(const decimal *dec, char *buf);
>> +
>> +/**
>> + * Cast decimal to an integer value. The number will be rounded
>> + * if it has a fractional part.
>> + */
>> +int32_t
>> +decimal_to_int(const decimal *dec);
>> +
>> +/** @copydoc decimal_to_int */
>> +uint32_t
>> +decimal_to_uint(const decimal *dec);
>> +
>> +/**
>> + * Compare 2 decimal values.
>> + * @return -1, lhs < rhs,
>> + * 0, lhs = rhs,
>> + * 1, lhs > rhs
>> + */
>> +int
>> +decimal_compare(const decimal *lhs, const decimal *rhs);
>> +
>> +/**
>> + * res is set to the absolute value of dec
>> + * decimal_abs(&a, &a) is allowed.
>> + */
>> +decimal *
>> +decimal_abs(decimal *res, const decimal *dec);
>> +
>> +/*
>> + * Arithmetic ops: add, subtract, multiply and divide.
>> + * Return result pointer on success, NULL on an error (overflow).
>> + */
>> +
>> +decimal *
>> +decimal_add(decimal *res, const decimal *lhs, const decimal *rhs);
>> +
>> +decimal *
>> +decimal_sub(decimal *res, const decimal *lhs, const decimal *rhs);
>> +
>> +decimal *
>> +decimal_mul(decimal *res, const decimal *lhs, const decimal *rhs);
>> +
>> +decimal *
>> +decimal_div(decimal *res, const decimal *lhs, const decimal *rhs);
>> +
>> +/*
>> + * log10, ln, pow, exp, sqrt.
>> + * Calculate the appropriate function and then round the result
>> + * to the desired precision.
>> + * Return result pointer on success, NULL on an error (overflow).
>> + */
>> +decimal *
>> +decimal_log10(decimal *res, const decimal *lhs, uint32_t precision);
>> +
>> +decimal *
>> +decimal_ln(decimal *res, const decimal *lhs, uint32_t precision);
>> +
>> +decimal *
>> +decimal_pow(decimal *res, const decimal *lhs, const decimal *rhs, uint32_t
>> precision); +
>> +decimal *
>> +decimal_exp(decimal *res, const decimal *lhs, uint32_t precision);
>> +
>> +decimal *
>> +decimal_sqrt(decimal *res, const decimal *lhs, uint32_t precision);
>> +
>> +#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..adfbead91
>> --- /dev/null
>> +++ b/test/unit/decimal.c
>> @@ -0,0 +1,168 @@
>> +#include "unit.h"
>> +#include "decimal.h"
>> +#include <limits.h>
>> +#include <string.h>
>> +
>> +int
>> +main(void)
>> +{
>> + plan(50);
>> +
>> + char buf[TARANTOOL_MAX_DECIMAL_DIGITS + 3];
>> + char buf2[TARANTOOL_MAX_DECIMAL_DIGITS + 3];
>> + char *b = "2.718281828";
>> +
>> + decimal s;
>> + decimal *ret;
>> +
>> + ret = decimal_from_string(&s, b);
>> + isnt(ret, NULL, "Basic construction from string.");
>> + decimal_to_string(&s, buf);
>> + is(strcmp(buf, b), 0, "Correct construction and to_string
> conversion.");
>> +
>> + ret = decimal_from_int(&s, INT_MAX);
>> + decimal_to_string(&s, buf);
>> + sprintf(buf2, "%d", INT_MAX);
>> + is(strcmp(buf, buf2), 0, "Correct construction from INT_MAX.");
>> + is(decimal_to_int(&s), INT_MAX, "Simple conversion back to int
> INT_MAX");
>> +
>> + ret = decimal_from_int(&s, INT_MIN);
>> + decimal_to_string(&s, buf);
>> + sprintf(buf2, "%d", INT_MIN);
>> + is(strcmp(buf, buf2), 0, "Correct construction from INT_MIN.");
>> + is(decimal_to_int(&s), INT_MIN, "Simple conversion bact to int
> INT_MIN");
>> +
>> + char *up1 = "2.5";
>> + char *down1 = "2.49";
>> + decimal_from_string(&s, up1);
>> + is(decimal_to_int(&s), 3, ".5 Rounds up");
>> + decimal_from_string(&s, down1);
>> + is(decimal_to_int(&s), 2, ".49 Rounds down");
>> +
>> + char *l = "1.23456789123456789123456789123456789123";
>> + ret = decimal_from_string(&s, l);
>> + isnt(ret, NULL, "Precision too high. Rounding happens.");
>> + ok(decimal_precision(&s) == 38 && decimal_scale(&s) == 37 ,
> "Construction
>> is correct."); + char *ll =
> "123456789123456789123456789123456789123";
>> + ret = decimal_from_string(&s, ll);
>> + is(ret, NULL, "Precision too high and scale = 0. Cannot round");
>> +
>> + /* 38 digits. */
>> + char *long_str = "0.0000000000000000000000000000000000001";
>> + ret = decimal_from_string(&s, long_str);
>> + isnt(ret, NULL, "Construncting the smallest possible number from
> string");
>> + decimal_to_string(&s, buf);
>> + is(strcmp(buf, long_str), 0, "Correct representation of smallest
> possible
>> number"); +
>> + /* Comparsions. */
>> + char *max_str = "3.11", *min_str = "3.0999";
>> + decimal max, min;
>> + decimal_from_string(&max, max_str);
>> + decimal_from_string(&min, min_str);
>> + is(decimal_compare(&max, &min), 1, "max > min");
>> + is(decimal_compare(&min, &max), -1, "min < max");
>> + is(decimal_compare(&max, &max), 0, "max == max");
>> +
>> + ret = decimal_from_string(&s, "-3.456");
>> + isnt(ret, NULL, "Construction from negative numbers");
>> + decimal_to_string(&s, buf);
>> + is(strcmp(buf, "-3.456"), 0, "Correct construction for negatives");
>> + ret = decimal_abs(&s, &s);
>> + isnt(ret, NULL, "Abs");
>> + decimal_to_string(&s, buf);
>> + is(strcmp(buf, "3.456"), 0, "Correct abs");
>> +
>> + /* Arithmetic ops. */
>> + decimal d, check;
>> + ret = decimal_from_string(&s, b);
>> + ret = decimal_from_string(&d, "1.25");
>> + sprintf(buf2, "%.9f", 1.25 + 2.718281828);
>> + ret = decimal_add(&d, &d, &s);
>> + isnt(ret, NULL, "Simple addition");
>> + decimal_to_string(&d, buf);
>> + is(strcmp(buf, buf2), 0, "Simple addition is correct");
>> +
>> + ret = decimal_sub(&d, &d, &s);
>> + isnt(ret, NULL, "Simple subtraction");
>> + decimal_from_string(&check, "1.25");
>> + is(decimal_compare(&d, &check), 0, "Simple subtraction is
> correct");
>> +
>> + decimal_from_int(&s, 4);
>> + ret = decimal_mul(&s, &s, &d);
>> + isnt(ret, NULL, "Simple multiplication");
>> + decimal_from_string(&check, "5.0");
>> + is(decimal_compare(&s, &check), 0 , "Simple multiplication is
> correct");
>> +
>> + ret = decimal_div(&s, &s, &d);
>> + isnt(ret, NULL, "Simple division");
>> + decimal_from_string(&check, "4.0");
>> + is(decimal_compare(&s, &check), 0, "Simple division is correct");
>> +
>> + /* Math. */
>> + ret = decimal_from_string(&s, "40.96");
>> + ret = decimal_from_string(&check, "6.4");
>> + ret = decimal_sqrt(&s, &s, 2);
>> + isnt(ret, NULL, "sqrt");
>> + is(decimal_compare(&s, &check), 0, "sqrt is correct");
>> +
>> + ret = decimal_from_string(&s, "40.96");
>> + ret = decimal_from_string(&d, "0.5");
>> + ret = decimal_pow(&s, &s, &d, 2);
>> + isnt(ret, NULL, "pow");
>> + is(decimal_compare(&s, &check), 0, "pow is correct");
>> +
>> + ret = decimal_from_string(&s, "3.000");
>> + ret = decimal_exp(&d, &s, 4);
>> + isnt(ret, NULL, "exp");
>> +
>> + ret = decimal_from_string(&check, "20.09");
>> + is(decimal_compare(&d, &check), 0, "exp is correct")
>> + ret = decimal_ln(&d, &d, 4);
>> + isnt(ret, NULL, "ln");
>> + /*
>> + * ln is wrong by 1 unit in last position.
>> + * (3.001 instead of 3.000, 3.0001 instead of 3.0000 and so on)
>> + */
>> + decimal_from_string(&s, "3.001");
>> + is(decimal_compare(&d, &s), 0, "ln is correct");
>> +
>> + /* 10^3.5 */
>> + ret = decimal_from_string(&s, "3162.27766");
>> + ret = decimal_log10(&d, &s, 2);
>> + isnt(ret, NULL, "log10");
>> + ret = decimal_from_string(&check, "3.5");
>> + is(decimal_compare(&d, &check), 0, "log10 is correct");
>> +
>> + /* Advanced test. */
>> + /* 38 digits. */
>> + char *bignum = "33.333333333333333333333333333333333333";
>> + char *test = "133.33333333333333333333333333333333333";
>> + ret = decimal_from_string(&s, bignum);
>> + ret = decimal_from_int(&d, 4);
>> + ret = decimal_mul(&s, &s, &d);
>> + isnt(ret, NULL, "Rounding when more than
> TARANTOOL_MAX_DECIMAL_DIGITS
>> digits"); + ret = decimal_from_string(&check, test);
>> + is(decimal_compare(&s, &check), 0, "Rounding is correct");
>> + is(decimal_precision(&s), 38, "Correct precision");
>> + is(decimal_scale(&s), 35, "Correct scale");
>> +
>> + char *small = "0.000000000000000000000000001";
>> + ret = decimal_from_string(&s, small);
>> + ret = decimal_mul(&s, &s, &s);
>> + isnt(ret, NULL, "Rounding too small number to zero");
>> + ret = decimal_from_int(&check, 0);
>> + is(decimal_compare(&s, &check), 0, "Rounding is correct");
>> + is(decimal_precision(&s), 38, "Correct precision");
>> + is(decimal_scale(&s), 38, "Correct scale");
>> +
>> + decimal_from_string(&s, small);
>> + decimal_from_string(&d, "10000000000000000000");
>> + ret = decimal_div(&s, &s, &d);
>> + isnt(ret, NULL, "Rounding too small number to zero");
>> + is(decimal_compare(&s, &check), 0, "Rounding is correct");
>> + decimal_to_string(&s, buf);
>> + is(decimal_precision(&s), 38, "Correct precision");
>> + is(decimal_scale(&s), 38, "Correct scale");
>> +
>> + check_plan();
>> +}
>> diff --git a/test/unit/decimal.result b/test/unit/decimal.result
>> new file mode 100644
>> index 000000000..02bee118a
>> --- /dev/null
>> +++ b/test/unit/decimal.result
>> @@ -0,0 +1,51 @@
>> +1..50
>> +ok 1 - Basic construction from string.
>> +ok 2 - Correct construction and to_string conversion.
>> +ok 3 - Correct construction from INT_MAX.
>> +ok 4 - Simple conversion back to int INT_MAX
>> +ok 5 - Correct construction from INT_MIN.
>> +ok 6 - Simple conversion bact to int INT_MIN
>> +ok 7 - .5 Rounds up
>> +ok 8 - .49 Rounds down
>> +ok 9 - Precision too high. Rounding happens.
>> +ok 10 - Construction is correct.
>> +ok 11 - Precision too high and scale = 0. Cannot round
>> +ok 12 - Construncting the smallest possible number from string
>> +ok 13 - Correct representation of smallest possible number
>> +ok 14 - max > min
>> +ok 15 - min < max
>> +ok 16 - max == max
>> +ok 17 - Construction from negative numbers
>> +ok 18 - Correct construction for negatives
>> +ok 19 - Abs
>> +ok 20 - Correct abs
>> +ok 21 - Simple addition
>> +ok 22 - Simple addition is correct
>> +ok 23 - Simple subtraction
>> +ok 24 - Simple subtraction is correct
>> +ok 25 - Simple multiplication
>> +ok 26 - Simple multiplication is correct
>> +ok 27 - Simple division
>> +ok 28 - Simple division is correct
>> +ok 29 - sqrt
>> +ok 30 - sqrt is correct
>> +ok 31 - pow
>> +ok 32 - pow is correct
>> +ok 33 - exp
>> +ok 34 - exp is correct
>> +ok 35 - ln
>> +ok 36 - ln is correct
>> +ok 37 - log10
>> +ok 38 - log10 is correct
>> +ok 39 - Rounding when more than TARANTOOL_MAX_DECIMAL_DIGITS digits
>> +ok 40 - Rounding is correct
>> +ok 41 - Correct precision
>> +ok 42 - Correct scale
>> +ok 43 - Rounding too small number to zero
>> +ok 44 - Rounding is correct
>> +ok 45 - Correct precision
>> +ok 46 - Correct scale
>> +ok 47 - Rounding too small number to zero
>> +ok 48 - Rounding is correct
>> +ok 49 - Correct precision
>> +ok 50 - Correct scale
>
More information about the Tarantool-patches
mailing list