From: Serge Petrenko <sergepetrenko@tarantool.org> To: Vladimir Davydov <vdavydov.dev@gmail.com> Cc: tarantool-patches@freelists.org Subject: Re: [tarantool-patches] [PATCH v3 5/5] decimal: expose decimal type to lua. Date: Wed, 3 Jul 2019 09:50:06 +0300 [thread overview] Message-ID: <961DC344-1EB4-467B-86E5-8CA1A912E54F@tarantool.org> (raw) In-Reply-To: <4a6a31d9502db71c7aaea05ff12249843a7544af.1562087728.git.sergepetrenko@tarantool.org> A tiny fix to increase coveralls coverage a little bit more. The only red lines left are the ones that truly never get executed (with the current implementation) diff --git a/test/app/decimal.result b/test/app/decimal.result index 65973ae09..481dbdb8c 100644 --- a/test/app/decimal.result +++ b/test/app/decimal.result @@ -382,6 +382,14 @@ decimal.precision(1.234) | --- | - error: expected cdata as 1 argument | ... +decimal.scale() + | --- + | - error: 'usage: decimal.scale(decimal)' + | ... +decimal.precision() + | --- + | - error: 'usage: decimal.precisiion(decimal)' + | ... decimal.abs() | --- | - error: 'usage: decimal.abs(decimal)' diff --git a/test/app/decimal.test.lua b/test/app/decimal.test.lua index aaba694aa..4aff0f2a4 100644 --- a/test/app/decimal.test.lua +++ b/test/app/decimal.test.lua @@ -106,6 +106,8 @@ decimal.round(a) decimal.round(1, 2) decimal.scale(1.234) decimal.precision(1.234) +decimal.scale() +decimal.precision() decimal.abs() a = decimal.new('1e19') -- Serge Petrenko sergepetrenko@tarantool.org > 2 июля 2019 г., в 20:27, Serge Petrenko <sergepetrenko@tarantool.org> написал(а): > > Add a decimal library to lua. > > Part of #692 > > @TarantoolBot document > Title: Document decimal module in lua. > > First of all, you have to require the package via > `decimal = require('decimal')` > Now you can construct decimals via `new` method. > Decimals may be constructed from lua numbers, strings, unsigned and > signed 64 bit integers. > Decimal is a fixed-point type with maximum 38 digits of precision. All > the calculations are exact, so, be careful when constructing decimals > from lua numbers: they may hold only 15 decimal digits of precision. > You are advised to construct decimals from strings, since strings > represent decimals exactly, and vice versa. > > ``` > a = decimal.new(123e-7) > b = decimal.new('123.456') > c = decimal.new('123.456e2') > d = decimal.new(123ULL) > e = decimal.new(2) > ``` > The allowed operations are addition, subtraction, division, > multiplication and power. If at least one of the operands is decimal, > decimal operations are performed. The other operand may be either > decimal or string, containing a number representation, or a lua number. > > Operations only fail on an overflow, i.e. when result exceeds 10^38 - 1. > This includes division by zero. In these cases an error `Operation > failed` is raised. > Underflow is also possible, when precision needed to store the exact > result exceeds 38 digits. Underflow is not an error. When an underflow > happens, the result is rounded to 38 digits of precision. > > ``` > a = decimal.new(123e-7) > b = decimal.new('123.456') > c = decimal.new('123.456e2') > d = decimal.new(123ULL) > e = decimal.new(2) > ``` > ``` > tarantool> a + b > --- > - '123.456012300000000' > ... > > tarantool> c - d > --- > - '12222.6' > ... > > tarantool> c / b > --- > - '100' > ... > > tarantool> d * d > --- > - '15129' > ... > > tarantool> d ^ 2 > --- > - '15129' > ... > > tarantool> 2 ^ d > --- > - '10633823966279326983230456482242756608'... > > tarantool> e ^ d > --- > - '10633823966279326983230456482242756608' > ... > ``` > The following math functions are also supported: > log10, ln, exp, sqrt. When specified as > `decimal.opname()`, operations may be performed on > strings and lua numbers. > ``` > f = decimal.new(100) > > tarantool> decimal.log10(f) > --- > - '2' > ... > > tarantool> decimal.sqrt(f) > --- > - '10' > ... > > tarantool> e2 = decimal.exp(2) > --- > ... > > tarantool> decimal.ln(e2) > --- > - '2.0000000000000000000000000000000000000' > ... > > There are also `abs` and `tostring` methods, and an unary minus > operator, which are pretty self-explanatory. > > ``` > tarantool> a = decimal.new('-5') > --- > ... > > tarantool> a > --- > - '-5' > ... > > tarantool> decimal.abs(a) > --- > - '5' > ... > > tarantool> -a > --- > - '5' > ... > > tostring(a) > --- > - '-5' > ... > > ``` > > `decimal.precision`, `decimal.scale` and `decimal.round` : > The first two methods return precision, i.e. decimal digits in > number representation, and scale, i.e. decimal digits after the decimal > point in the number representation. > `decimal.round` rounds the number to the given scale. > ``` > tarantool> a = decimal.new('123.456789') > --- > ... > > tarantool> decimal.precision(a) > --- > - 9 > ... > > tarantool> decimal.scale(a) > --- > - 6 > ... > > tarantool> decimal.round(a, 4) > --- > - '123.4568' > ... > > ``` > > Comparsions: `>`, `<`, `>=`, `<=`, `==` are also legal and work as > expected. You may compare decimals with lua numbers or strings. In that > case comparsion will happen after the values are converted to decimal > type. > --- > src/CMakeLists.txt | 1 + > src/lua/decimal.c | 351 +++++++++++++++++++++++++++++ > src/lua/decimal.h | 47 ++++ > src/lua/init.c | 2 + > test/app/decimal.result | 452 ++++++++++++++++++++++++++++++++++++++ > test/app/decimal.test.lua | 128 +++++++++++ > 6 files changed, 981 insertions(+) > create mode 100644 src/lua/decimal.c > create mode 100644 src/lua/decimal.h > create mode 100644 test/app/decimal.result > create mode 100644 test/app/decimal.test.lua > > diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt > index 33b64f6a6..acd719e9b 100644 > --- a/src/CMakeLists.txt > +++ b/src/CMakeLists.txt > @@ -120,6 +120,7 @@ set (server_sources > lua/string.c > lua/buffer.c > lua/swim.c > + lua/decimal.c > ${lua_sources} > ${PROJECT_SOURCE_DIR}/third_party/lua-yaml/lyaml.cc > ${PROJECT_SOURCE_DIR}/third_party/lua-yaml/b64.c > diff --git a/src/lua/decimal.c b/src/lua/decimal.c > new file mode 100644 > index 000000000..aecd88175 > --- /dev/null > +++ b/src/lua/decimal.c > @@ -0,0 +1,351 @@ > +/* > + * Copyright 2010-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 "lua/decimal.h" > +#include "lib/core/decimal.h" > +#include "lua/utils.h" > + > +#include <lua.h> > +#include <lauxlib.h> > +#include <lualib.h> > + > +#define LDECIMAL_BINOP(name, opname) \ > +static int \ > +ldecimal_##name(struct lua_State *L) { \ > + assert(lua_gettop(L) == 2); \ > + decimal_t *lhs = lua_todecimal(L, 1); \ > + decimal_t *rhs = lua_todecimal(L, 2); \ > + decimal_t *res = lua_pushdecimal(L); \ > + if (decimal_##opname(res, lhs, rhs) == NULL) { \ > + lua_pop(L, 1); \ > + luaL_error(L, "decimal operation failed"); \ > + } \ > + return 1; \ > +} > + > +#define LDECIMAL_FUNC(name, opname) \ > +static int \ > +ldecimal_##name(struct lua_State *L) { \ > + if (lua_gettop(L) < 1) \ > + return luaL_error(L, "usage: decimal."#name"(decimal)"); \ > + decimal_t *lhs = lua_todecimal(L, 1); \ > + decimal_t *res = lua_pushdecimal(L); \ > + if (decimal_##opname(res, lhs) == NULL) { \ > + lua_pop(L, 1); \ > + luaL_error(L, "decimal operation failed"); \ > + } \ > + return 1; \ > +} > + > +#define LDECIMAL_CMPOP(name, cmp) \ > +static int \ > +ldecimal_##name(struct lua_State *L) { \ > + assert(lua_gettop(L) == 2); \ > + decimal_t *lhs = lua_todecimal(L, 1); \ > + decimal_t *rhs = lua_todecimal(L, 2); \ > + lua_pushboolean(L, decimal_compare(lhs, rhs) cmp 0); \ > + return 1; \ > +} > + > +static uint32_t CTID_DECIMAL; > + > +/** Push a new decimal on the stack and return a pointer to it. */ > +static decimal_t * > +lua_pushdecimal(struct lua_State *L) > +{ > + decimal_t *res = luaL_pushcdata(L, CTID_DECIMAL); > + return res; > +} > + > +/** Check whether a value at a given index is a decimal. */ > +static decimal_t * > +lua_checkdecimal(struct lua_State *L, int index) > +{ > + uint32_t ctypeid; > + decimal_t *res = luaL_checkcdata(L, index, &ctypeid); > + if (ctypeid != CTID_DECIMAL) > + luaL_error(L, "expected decimal as %d argument", index); > + return res; > +} > + > +/** > + * Convert the value at the given index to a decimal in place. > + * The possible conversions are string->decimal and number->decimal. > + */ > +static decimal_t * > +lua_todecimal(struct lua_State *L, int index) > +{ > + /* > + * Convert the index, if it is given relative to the top. > + * Othervise it will point to a wrong position after > + * pushdecimal(). > + */ > + if (index < 0) > + index = lua_gettop(L) + index + 1; > + decimal_t *res = lua_pushdecimal(L); > + switch(lua_type(L, index)) > + { > + case LUA_TNUMBER: > + { > + double n = lua_tonumber(L, index); > + if (decimal_from_double(res, n) == NULL) > + goto err; > + break; > + } > + case LUA_TSTRING: > + { > + const char *str = lua_tostring(L, index); > + if (decimal_from_string(res, str) == NULL) > + goto err; > + break; > + } > + case LUA_TCDATA: > + { > + uint32_t ctypeid; > + void *cdata = luaL_checkcdata(L, index, &ctypeid); > + int64_t ival; > + uint64_t uval; > + double d; > + if (ctypeid == CTID_DECIMAL) { > + /* > + * We already have a decimal at the > + * desired position. > + */ > + lua_pop(L, 1); > + return (decimal_t *) cdata; > + } > + switch (ctypeid) > + { > + case CTID_CCHAR: > + case CTID_INT8: > + ival = *(int8_t *) cdata; > + /* > + * no errors are possible in decimal from > + * (u)int construction. > + */ > + decimal_from_int64(res, ival); > + break; > + case CTID_INT16: > + ival = *(int16_t *) cdata; > + decimal_from_int64(res, ival); > + break; > + case CTID_INT32: > + ival = *(int32_t *) cdata; > + decimal_from_int64(res, ival); > + break; > + case CTID_INT64: > + ival = *(int64_t *) cdata; > + decimal_from_int64(res, ival); > + break; > + case CTID_UINT8: > + uval = *(uint8_t *) cdata; > + decimal_from_uint64(res, uval); > + break; > + case CTID_UINT16: > + uval = *(uint16_t *) cdata; > + decimal_from_uint64(res, uval); > + break; > + case CTID_UINT32: > + uval = *(uint32_t *) cdata; > + decimal_from_uint64(res, uval); > + break; > + case CTID_UINT64: > + uval = *(uint64_t *) cdata; > + decimal_from_uint64(res, uval); > + break; > + case CTID_FLOAT: > + d = *(float *) cdata; > + if (decimal_from_double(res, d) == NULL) > + goto err; > + break; > + case CTID_DOUBLE: > + d = *(double *) cdata; > + if (decimal_from_double(res, d) == NULL) > + goto err; > + break; > + default: > + lua_pop(L, 1); > + luaL_error(L, "expected decimal, number or string as " > + "%d argument", index); > + } > + break; > + } > + default: > + lua_pop(L, 1); > + luaL_error(L, "expected decimal, number or string as " > + "%d argument", index); > + } > + lua_replace(L, index); > + return res; > +err: /* pop the decimal we prepared on top of the stack. */ > + lua_pop(L, 1); > + luaL_error(L, "incorrect value to convert to decimal as %d argument", > + index); > + /* luaL_error never returns, this is to silence compiler warning. */ > + return NULL; > +} > + > +LDECIMAL_BINOP(add, add) > +LDECIMAL_BINOP(sub, sub) > +LDECIMAL_BINOP(mul, mul) > +LDECIMAL_BINOP(div, div) > +LDECIMAL_BINOP(pow, pow) > + > +LDECIMAL_FUNC(log10, log10) > +LDECIMAL_FUNC(ln, ln) > +LDECIMAL_FUNC(exp, exp) > +LDECIMAL_FUNC(sqrt, sqrt) > +LDECIMAL_FUNC(abs, abs) > + > +LDECIMAL_CMPOP(eq, ==) > +LDECIMAL_CMPOP(lt, <) > +LDECIMAL_CMPOP(le, <=) > + > +static int > +ldecimal_minus(struct lua_State *L) > +{ > + /* > + * Unary operations get a fake second operand. See > + * http://lua-users.org/lists/lua-l/2016-10/msg00351.html > + */ > + assert(lua_gettop(L) == 2); > + decimal_t *lhs = lua_todecimal(L, 1); > + decimal_t *res = lua_pushdecimal(L); > + /* _minus never fails. */ > + decimal_minus(res, lhs); > + return 1; > +} > + > +static int > +ldecimal_new(struct lua_State *L) > +{ > + if (lua_gettop(L) < 1) > + luaL_error(L, "usage: decimal.new(value)"); > + decimal_t *lhs = lua_todecimal(L, 1); > + decimal_t *res = lua_pushdecimal(L); > + *res = *lhs; > + return 1; > +} > + > +static int > +ldecimal_round(struct lua_State *L) > +{ > + if (lua_gettop(L) < 2) > + return luaL_error(L, "usage: decimal.round(decimal, scale)"); > + decimal_t *lhs = lua_checkdecimal(L, 1); > + int n = lua_tointeger(L, 2); > + decimal_t *res = lua_pushdecimal(L); > + *res = *lhs; > + decimal_round(res, n); > + return 1; > +} > + > +static int > +ldecimal_scale(struct lua_State *L) > +{ > + if (lua_gettop(L) < 1) > + return luaL_error(L, "usage: decimal.scale(decimal)"); > + decimal_t *lhs = lua_checkdecimal(L, 1); > + int scale = decimal_scale(lhs); > + lua_pushnumber(L, scale); > + return 1; > +} > + > +static int > +ldecimal_precision(struct lua_State *L) > +{ > + if (lua_gettop(L) < 1) > + return luaL_error(L, "usage: decimal.precisiion(decimal)"); > + decimal_t *lhs = lua_checkdecimal(L, 1); > + int precision = decimal_precision(lhs); > + lua_pushnumber(L, precision); > + return 1; > +} > + > +static int > +ldecimal_tostring(struct lua_State *L) > +{ > + if (lua_gettop(L) < 1) > + return luaL_error(L, "usage: decimal.tostring(decimal)"); > + decimal_t *lhs = lua_checkdecimal(L, 1); > + lua_pushstring(L, decimal_to_string(lhs)); > + return 1; > +} > + > +static const luaL_Reg ldecimal_mt[] = { > + {"__unm", ldecimal_minus}, > + {"__add", ldecimal_add}, > + {"__sub", ldecimal_sub}, > + {"__mul", ldecimal_mul}, > + {"__div", ldecimal_div}, > + {"__pow", ldecimal_pow}, > + {"__eq", ldecimal_eq}, > + {"__lt", ldecimal_lt}, > + {"__le", ldecimal_le}, > + {"__tostring", ldecimal_tostring}, > + {NULL, NULL} > +}; > + > +static const luaL_Reg ldecimal_lib[] = { > + {"log10", ldecimal_log10}, > + {"ln", ldecimal_ln}, > + {"exp", ldecimal_exp}, > + {"sqrt", ldecimal_sqrt}, > + {"round", ldecimal_round}, > + {"scale", ldecimal_scale}, > + {"precision", ldecimal_precision}, > + {"abs", ldecimal_abs}, > + {"new", ldecimal_new}, > + {NULL, NULL} > +}; > + > +void > +tarantool_lua_decimal_init(struct lua_State *L) > +{ > + int rc = luaL_cdef(L, "typedef struct {" > + "int32_t digits;" > + "int32_t exponent;" > + "uint8_t bits;" > + "uint16_t lsu[13];" > + "} decimal_t;"); > + assert(rc == 0); > + (void)rc; > + luaL_register_module(L, "decimal", ldecimal_lib); > + lua_pop(L, 1); > + /* > + * luaL_metatype is similar to luaL_ctypeid + > + * luaL_register_type. > + * The metatable is set automatically to every > + * cdata of the new ctypeid ever created via ffi. > + */ > + CTID_DECIMAL = luaL_metatype(L, "decimal_t", ldecimal_mt); > + assert(CTID_DECIMAL != 0); > +} > diff --git a/src/lua/decimal.h b/src/lua/decimal.h > new file mode 100644 > index 000000000..0485d11ef > --- /dev/null > +++ b/src/lua/decimal.h > @@ -0,0 +1,47 @@ > +/* > + * Copyright 2010-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. > + */ > +#ifndef TARANTOOL_LUA_DECIMAL_H_INCLUDED > +#define TARANTOOL_LUA_DECIMAL_H_INCLUDED > + > +#if defined(__cplusplus) > +extern "C" { > +#endif /* defined(__cplusplus) */ > + > +struct lua_State; > + > +void > +tarantool_lua_decimal_init(struct lua_State *L); > + > +#if defined(__cplusplus) > +} /* extern "C" */ > +#endif /* defined(__cplusplus) */ > + > +#endif /* TARANTOOL_LUA_DECIMAL_H_INCLUDED */ > diff --git a/src/lua/init.c b/src/lua/init.c > index 9982828d9..fbaedd4cd 100644 > --- a/src/lua/init.c > +++ b/src/lua/init.c > @@ -59,6 +59,7 @@ > #include "lua/httpc.h" > #include "lua/utf8.h" > #include "lua/swim.h" > +#include "lua/decimal.h" > #include "digest.h" > #include <small/ibuf.h> > > @@ -454,6 +455,7 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv) > tarantool_lua_pickle_init(L); > tarantool_lua_digest_init(L); > tarantool_lua_swim_init(L); > + tarantool_lua_decimal_init(L); > luaopen_http_client_driver(L); > lua_pop(L, 1); > luaopen_msgpack(L); > diff --git a/test/app/decimal.result b/test/app/decimal.result > new file mode 100644 > index 000000000..65973ae09 > --- /dev/null > +++ b/test/app/decimal.result > @@ -0,0 +1,452 @@ > +-- test-run result file version 2 > +decimal = require('decimal') > + | --- > + | ... > +test_run = require('test_run').new() > + | --- > + | ... > +ffi = require('ffi') > + | --- > + | ... > + > +-- check various constructors > +decimal.new('1234.5678') > + | --- > + | - '1234.5678' > + | ... > +decimal.new('1e6') > + | --- > + | - '1000000' > + | ... > +decimal.new('-6.234612e2') > + | --- > + | - '-623.4612' > + | ... > +-- check (u)int16/32/64_t > +decimal.new(2ULL ^ 63) > + | --- > + | - '9223372036854775808' > + | ... > +decimal.new(123456789123456789ULL) > + | --- > + | - '123456789123456789' > + | ... > +decimal.new(-123456789123456789LL) > + | --- > + | - '-123456789123456789' > + | ... > +decimal.new(ffi.new('uint8_t', 231)) > + | --- > + | - '231' > + | ... > +decimal.new(ffi.new('int8_t', -113)) > + | --- > + | - '-113' > + | ... > +decimal.new(ffi.new('uint16_t', 65535)) > + | --- > + | - '65535' > + | ... > +decimal.new(ffi.new('int16_t', -31263)) > + | --- > + | - '-31263' > + | ... > +decimal.new(ffi.new('uint32_t', 4123123123)) > + | --- > + | - '4123123123' > + | ... > +decimal.new(ffi.new('int32_t', -2123123123)) > + | --- > + | - '-2123123123' > + | ... > +decimal.new(ffi.new('float', 128.5)) > + | --- > + | - '128.5' > + | ... > +decimal.new(ffi.new('double', 128.5)) > + | --- > + | - '128.5' > + | ... > + > +decimal.new(1) > + | --- > + | - '1' > + | ... > +decimal.new(-1) > + | --- > + | - '-1' > + | ... > +decimal.new(2^64) > + | --- > + | - '18446744073709600000' > + | ... > +decimal.new(2^(-20)) > + | --- > + | - '0.00000095367431640625' > + | ... > + > +-- incorrect constructions > +decimal.new(box.NULL) > + | --- > + | - error: expected decimal, number or string as 1 argument > + | ... > +decimal.new(ffi.new('float', 1 / 0)) > + | --- > + | - error: incorrect value to convert to decimal as 1 argument > + | ... > +decimal.new(ffi.new('double', 1 / 0)) > + | --- > + | - error: incorrect value to convert to decimal as 1 argument > + | ... > +decimal.new(1 / 0) > + | --- > + | - error: incorrect value to convert to decimal as 1 argument > + | ... > +decimal.new({1, 2, 3}) > + | --- > + | - error: expected decimal, number or string as 1 argument > + | ... > +decimal.new() > + | --- > + | - error: 'usage: decimal.new(value)' > + | ... > +decimal.new('inf') > + | --- > + | - error: incorrect value to convert to decimal as 1 argument > + | ... > +decimal.new('NaN') > + | --- > + | - error: incorrect value to convert to decimal as 1 argument > + | ... > +decimal.new('not a valid number') > + | --- > + | - error: incorrect value to convert to decimal as 1 argument > + | ... > + > +a = decimal.new('10') > + | --- > + | ... > +a > + | --- > + | - '10' > + | ... > +b = decimal.new('0.1') > + | --- > + | ... > +b > + | --- > + | - '0.1' > + | ... > +a + b > + | --- > + | - '10.1' > + | ... > +a - b > + | --- > + | - '9.9' > + | ... > +a * b > + | --- > + | - '1.0' > + | ... > +a / b > + | --- > + | - '100' > + | ... > +a ^ b > + | --- > + | - '1.2589254117941672104239541063958006061' > + | ... > +b ^ a > + | --- > + | - '0.0000000001' > + | ... > +-a + -b == -(a + b) > + | --- > + | - true > + | ... > +a > + | --- > + | - '10' > + | ... > +b > + | --- > + | - '0.1' > + | ... > + > +a < b > + | --- > + | - false > + | ... > +b < a > + | --- > + | - true > + | ... > +a <= b > + | --- > + | - false > + | ... > +b <= a > + | --- > + | - true > + | ... > +a > b > + | --- > + | - true > + | ... > +b > a > + | --- > + | - false > + | ... > +a >= b > + | --- > + | - true > + | ... > +b >= a > + | --- > + | - false > + | ... > +a == b > + | --- > + | - false > + | ... > +a ~= b > + | --- > + | - true > + | ... > +a > + | --- > + | - '10' > + | ... > +b > + | --- > + | - '0.1' > + | ... > + > +decimal.sqrt(a) > + | --- > + | - '3.1622776601683793319988935444327185337' > + | ... > +decimal.ln(a) > + | --- > + | - '2.3025850929940456840179914546843642076' > + | ... > +decimal.log10(a) > + | --- > + | - '1' > + | ... > +decimal.exp(a) > + | --- > + | - '22026.465794806716516957900645284244366' > + | ... > +a == decimal.ln(decimal.exp(a)) > + | --- > + | - true > + | ... > +a == decimal.sqrt(a ^ 2) > + | --- > + | - true > + | ... > +a == decimal.log10('10' ^ a) > + | --- > + | - true > + | ... > +a == decimal.abs(-a) > + | --- > + | - true > + | ... > +a + -a == 0 > + | --- > + | - true > + | ... > +a > + | --- > + | - '10' > + | ... > + > +a = decimal.new('1.1234567891234567891234567891234567891') > + | --- > + | ... > +a > + | --- > + | - '1.1234567891234567891234567891234567891' > + | ... > +decimal.precision(a) > + | --- > + | - 38 > + | ... > +decimal.scale(a) > + | --- > + | - 37 > + | ... > +decimal.round(a, 37) == a > + | --- > + | - true > + | ... > +a > + | --- > + | - '1.1234567891234567891234567891234567891' > + | ... > +a = decimal.round(a, 36) > + | --- > + | ... > +decimal.precision(a) > + | --- > + | - 37 > + | ... > +decimal.scale(a) > + | --- > + | - 36 > + | ... > +decimal.round(a, 100) == a > + | --- > + | - true > + | ... > +-- noop > +decimal.round(a, -5) == a > + | --- > + | - true > + | ... > +decimal.round(a, 7) > + | --- > + | - '1.1234568' > + | ... > +decimal.round(a, 3) > + | --- > + | - '1.123' > + | ... > +decimal.round(a, 0) > + | --- > + | - '1' > + | ... > +a > + | --- > + | - '1.123456789123456789123456789123456789' > + | ... > + > +decimal.ln(0) > + | --- > + | - error: decimal operation failed > + | ... > +decimal.ln(-1) > + | --- > + | - error: decimal operation failed > + | ... > +decimal.ln(1) > + | --- > + | - '0' > + | ... > +decimal.log10(0) > + | --- > + | - error: decimal operation failed > + | ... > +decimal.log10(-1) > + | --- > + | - error: decimal operation failed > + | ... > +decimal.log10(1) > + | --- > + | - '0' > + | ... > +decimal.exp(88) > + | --- > + | - error: decimal operation failed > + | ... > +decimal.exp(87) > + | --- > + | - '60760302250568721495223289381302760753' > + | ... > +decimal.sqrt(-5) > + | --- > + | - error: decimal operation failed > + | ... > +decimal.sqrt(5) > + | --- > + | - '2.2360679774997896964091736687312762354' > + | ... > + > +-- various incorrect operands > +decimal.round(a) > + | --- > + | - error: 'usage: decimal.round(decimal, scale)' > + | ... > +decimal.round(1, 2) > + | --- > + | - error: expected cdata as 1 argument > + | ... > +decimal.scale(1.234) > + | --- > + | - error: expected cdata as 1 argument > + | ... > +decimal.precision(1.234) > + | --- > + | - error: expected cdata as 1 argument > + | ... > +decimal.abs() > + | --- > + | - error: 'usage: decimal.abs(decimal)' > + | ... > + > +a = decimal.new('1e19') > + | --- > + | ... > +a * '1e19' > + | --- > + | - error: '[string "return a * ''1e19'' "]:1: decimal operation failed' > + | ... > +a ^ 2 > + | --- > + | - error: '[string "return a ^ 2 "]:1: decimal operation failed' > + | ... > +a ^ 1.9 > + | --- > + | - '1258925411794167210423954106395800606.1' > + | ... > +a * '1e18' > + | --- > + | - '10000000000000000000000000000000000000' > + | ... > +a = decimal.new(string.rep('9', 38)) > + | --- > + | ... > +decimal.precision(a) > + | --- > + | - 38 > + | ... > +a + 1 > + | --- > + | - error: '[string "return a + 1 "]:1: decimal operation failed' > + | ... > +a + '0.9' > + | --- > + | - error: '[string "return a + ''0.9'' "]:1: decimal operation failed' > + | ... > +a + '0.5' > + | --- > + | - error: '[string "return a + ''0.5'' "]:1: decimal operation failed' > + | ... > +a + '0.4' > + | --- > + | - '99999999999999999999999999999999999999' > + | ... > +a / 0.5 > + | --- > + | - error: '[string "return a / 0.5 "]:1: decimal operation failed' > + | ... > +1 / decimal.new('0') > + | --- > + | - error: '[string "return 1 / decimal.new(''0'') "]:1: decimal operation failed' > + | ... > + > +a = decimal.new('-13') > + | --- > + | ... > +a ^ 2 > + | --- > + | - '169' > + | ... > +-- fractional powers are allowed only for positive numbers > +a ^ 2.5 > + | --- > + | - error: '[string "return a ^ 2.5 "]:1: decimal operation failed' > + | ... > diff --git a/test/app/decimal.test.lua b/test/app/decimal.test.lua > new file mode 100644 > index 000000000..aaba694aa > --- /dev/null > +++ b/test/app/decimal.test.lua > @@ -0,0 +1,128 @@ > +decimal = require('decimal') > +test_run = require('test_run').new() > +ffi = require('ffi') > + > +-- check various constructors > +decimal.new('1234.5678') > +decimal.new('1e6') > +decimal.new('-6.234612e2') > +-- check (u)int16/32/64_t > +decimal.new(2ULL ^ 63) > +decimal.new(123456789123456789ULL) > +decimal.new(-123456789123456789LL) > +decimal.new(ffi.new('uint8_t', 231)) > +decimal.new(ffi.new('int8_t', -113)) > +decimal.new(ffi.new('uint16_t', 65535)) > +decimal.new(ffi.new('int16_t', -31263)) > +decimal.new(ffi.new('uint32_t', 4123123123)) > +decimal.new(ffi.new('int32_t', -2123123123)) > +decimal.new(ffi.new('float', 128.5)) > +decimal.new(ffi.new('double', 128.5)) > + > +decimal.new(1) > +decimal.new(-1) > +decimal.new(2^64) > +decimal.new(2^(-20)) > + > +-- incorrect constructions > +decimal.new(box.NULL) > +decimal.new(ffi.new('float', 1 / 0)) > +decimal.new(ffi.new('double', 1 / 0)) > +decimal.new(1 / 0) > +decimal.new({1, 2, 3}) > +decimal.new() > +decimal.new('inf') > +decimal.new('NaN') > +decimal.new('not a valid number') > + > +a = decimal.new('10') > +a > +b = decimal.new('0.1') > +b > +a + b > +a - b > +a * b > +a / b > +a ^ b > +b ^ a > +-a + -b == -(a + b) > +a > +b > + > +a < b > +b < a > +a <= b > +b <= a > +a > b > +b > a > +a >= b > +b >= a > +a == b > +a ~= b > +a > +b > + > +decimal.sqrt(a) > +decimal.ln(a) > +decimal.log10(a) > +decimal.exp(a) > +a == decimal.ln(decimal.exp(a)) > +a == decimal.sqrt(a ^ 2) > +a == decimal.log10('10' ^ a) > +a == decimal.abs(-a) > +a + -a == 0 > +a > + > +a = decimal.new('1.1234567891234567891234567891234567891') > +a > +decimal.precision(a) > +decimal.scale(a) > +decimal.round(a, 37) == a > +a > +a = decimal.round(a, 36) > +decimal.precision(a) > +decimal.scale(a) > +decimal.round(a, 100) == a > +-- noop > +decimal.round(a, -5) == a > +decimal.round(a, 7) > +decimal.round(a, 3) > +decimal.round(a, 0) > +a > + > +decimal.ln(0) > +decimal.ln(-1) > +decimal.ln(1) > +decimal.log10(0) > +decimal.log10(-1) > +decimal.log10(1) > +decimal.exp(88) > +decimal.exp(87) > +decimal.sqrt(-5) > +decimal.sqrt(5) > + > +-- various incorrect operands > +decimal.round(a) > +decimal.round(1, 2) > +decimal.scale(1.234) > +decimal.precision(1.234) > +decimal.abs() > + > +a = decimal.new('1e19') > +a * '1e19' > +a ^ 2 > +a ^ 1.9 > +a * '1e18' > +a = decimal.new(string.rep('9', 38)) > +decimal.precision(a) > +a + 1 > +a + '0.9' > +a + '0.5' > +a + '0.4' > +a / 0.5 > +1 / decimal.new('0') > + > +a = decimal.new('-13') > +a ^ 2 > +-- fractional powers are allowed only for positive numbers > +a ^ 2.5 > -- > 2.20.1 (Apple Git-117) > >
next prev parent reply other threads:[~2019-07-03 6:50 UTC|newest] Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-07-02 17:27 [PATCH v3 0/5] decimal: expose decimal module to Lua Serge Petrenko 2019-07-02 17:27 ` [PATCH v3 1/5] decimal: fix ln hang on values between ~ 0.9 and 1.1 Serge Petrenko 2019-07-02 17:27 ` [PATCH v3 2/5] decimal: diallow infinity and NaN entirely Serge Petrenko 2019-07-02 17:27 ` [PATCH v3 3/5] decimal: fix string formatting on construction from double Serge Petrenko 2019-07-02 17:27 ` [PATCH v3 4/5] lua/utils: add a function to register FFI metatypes Serge Petrenko 2019-07-02 17:27 ` [PATCH v3 5/5] decimal: expose decimal type to lua Serge Petrenko 2019-07-03 6:50 ` Serge Petrenko [this message] 2019-07-08 14:25 ` [PATCH v3 0/5] decimal: expose decimal module to Lua Vladimir Davydov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=961DC344-1EB4-467B-86E5-8CA1A912E54F@tarantool.org \ --to=sergepetrenko@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=vdavydov.dev@gmail.com \ --subject='Re: [tarantool-patches] [PATCH v3 5/5] decimal: expose decimal type to lua.' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox