From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) Subject: Re: [PATCH 2/2] decimal: expose decimal type to lua. From: Serge Petrenko In-Reply-To: <89c41adbdf7452c1ef4a8f478363fe2ccb95a848.1560958964.git.sergepetrenko@tarantool.org> Date: Thu, 20 Jun 2019 14:02:11 +0300 Content-Transfer-Encoding: quoted-printable Message-Id: <716DBE4A-7768-4242-B0DE-AC3140B2B88C@tarantool.org> References: <89c41adbdf7452c1ef4a8f478363fe2ccb95a848.1560958964.git.sergepetrenko@tarantool.org> To: Vladimir Davydov Cc: tarantool-patches@freelists.org List-ID: A minor fix for the release build. Pushed on the branch diff --git a/src/lua/decimal.c b/src/lua/decimal.c index 72b458519..9e2fd41b5 100644 --- a/src/lua/decimal.c +++ b/src/lua/decimal.c @@ -314,6 +314,7 @@ tarantool_lua_decimal_init(struct lua_State *L) "uint16_t lsu[13];" "} decimal_t;"); assert(rc =3D=3D 0); + (void)rc; luaL_register_module(L, "decimal", ldecimal_lib); lua_pop(L, 1); /* -- Serge Petrenko sergepetrenko@tarantool.org > 19 =D0=B8=D1=8E=D0=BD=D1=8F 2019 =D0=B3., =D0=B2 18:58, Serge Petrenko = =D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BB(=D0= =B0): >=20 > Add a decimal library to lua. >=20 > Part of #692 >=20 > @TarantoolBot document > Title: Document decimal module in lua. >=20 > First of all, you have to require the package via > `decimal =3D require('decimal')` > Now you can construct decimals via `tonumber` 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. >=20 > ``` > a =3D decimal.tonumber(123e-7) > b =3D decimal.tonumber('123.456') > c =3D decimal.tonumber('123.456e2') > d =3D decimal.tonumber(123ULL) > e =3D decimal.tonumber(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. > When the operation is called as `decimal.opname`, both operands may be > strings or lua numbers, e.g. `decimal.add(23, '123.456') =3D=3D = 146.456`: > ``` > tarantool> a + b > --- > - '123.456012300000000' > ... >=20 > tarantool> decimal.add(a,b) > --- > - '123.456012300000000' > ... >=20 > tarantool> c - d > --- > - '12222.6' > ... >=20 > tarantool> decimal.sub(c,d) > --- > - '12222.6' > ... >=20 > tarantool> c / b > --- > - '100' > ... >=20 > tarantool> decimal.div(c, b) > --- > - '100' > ... >=20 > tarantool> d * d > --- > - '15129' > ... >=20 > tarantool> decimal.mul(d, d) > --- > - '15129' > ... >=20 > tarantool> d ^ 2 > --- > - '15129' > ... >=20 > tarantool> 2 ^ d > --- > - '10633823966279326983230456482242756608' > ... >=20 > tarantool> e ^ d > --- > - '10633823966279326983230456482242756608' > ... > ``` > The following math functions are also supported: > log10, ln, exp, sqrt, pow. When specified as > `decimal.opname()`, operations may be performed on > strings and lua numbers. > ``` > f =3D decimal.tonumber(100) > tarantool> f:log10() > --- > - '2' > ... >=20 > tarantool> decimal.log10(f) > --- > - '2' > ... >=20 > tarantool> f:sqrt() > --- > - '10' > ... >=20 > tarantool> decimal.sqrt(f) > --- > - '10' > ... >=20 > tarantool> e2 =3D decimal.exp(2) > --- > ... >=20 > tarantool> e2:ln() > --- > - '2.0000000000000000000000000000000000000' > ... >=20 > tarantool> decimal.ln(e2) > --- > - '2.0000000000000000000000000000000000000' > ... >=20 > tarantool> e:exp() =3D=3D e2 > --- > - true > ... >=20 > tarantool> e:exp():ln() =3D=3D e > --- > - true > ... >=20 > ``` >=20 > There are also `abs`, `minus` and `tostring` methods, which are pretty > self-explanatory. >=20 > ``` > tarantool> a =3D decimal.tonumber(-5) > --- > ... >=20 > tarantool> a > --- > - '-5.000000000000000' > ... >=20 > tarantool> a:abs() > --- > - '5.000000000000000' > ... >=20 > tarantool> decimal.abs(a) > --- > - '5.000000000000000' > ... >=20 > tarantool> a:minus() > --- > - '5.000000000000000' > ... >=20 > tarantool> -a > --- > - '5.000000000000000' > ... >=20 > tarantool> decimal.minus(a) > --- > - '5.000000000000000' > ... >=20 > tarantool> a:tostring() > --- > - '-5.000000000000000' > ... >=20 > tarantool> decimal.tostring(a) > --- > - '-5.000000000000000' > ... >=20 > ``` >=20 > Comparsions: `>`, `<`, `>=3D`, `<=3D`, `=3D=3D` 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 | 327 ++++++++++++++++++++++++++++++++++++++ > src/lua/decimal.h | 39 +++++ > src/lua/init.c | 2 + > test/app/decimal.result | 172 ++++++++++++++++++++ > test/app/decimal.test.lua | 50 ++++++ > 6 files changed, 591 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 >=20 > 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..72b458519 > --- /dev/null > +++ b/src/lua/decimal.c > @@ -0,0 +1,327 @@ > +/* > + * 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 ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED > + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL > + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, > + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR > + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF > + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + */ > + > +#include "lua/decimal.h" > +#include "lib/core/decimal.h" > +#include "lua/utils.h" > + > +#include > +#include > +#include > + > +#define LDECIMAL_OP2(name, opname)\ > +static int \ > +ldecimal_##name(struct lua_State *L) {\ > + if (lua_gettop(L) < 2)\ > + return luaL_error(L, "Usage: decimal."#name"(decimal, = decimal)");\ > + decimal_t *lhs =3D lua_todecimal(L, 1);\ > + decimal_t *rhs =3D lua_todecimal(L, 2);\ > + decimal_t *res =3D lua_pushdecimal(L);\ > + if (decimal_##opname(res, lhs, rhs) =3D=3D NULL) {\ > + lua_pop(L, 1);\ > + luaL_error(L, "Operation failed.");\ > + }\ > + return 1;\ > +} > +#define LDECIMAL_UNOP(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 =3D lua_todecimal(L, 1);\ > + decimal_t *res =3D lua_pushdecimal(L);\ > + if (decimal_##opname(res, lhs) =3D=3D NULL) {\ > + lua_pop(L, 1);\ > + luaL_error(L, "Operation failed");\ > + }\ > + return 1;\ > +} > + > +#define LDECIMAL_CMPOP(name, cmp)\ > +static int \ > +ldecimal_##name(struct lua_State *L) {\ > + if (lua_gettop(L) < 2)\ > + return luaL_error(L, "Usage: decimal.__"#name"(decimal, = decimal)");\ > + decimal_t *lhs =3D lua_todecimal(L, 1);\ > + decimal_t *rhs =3D lua_todecimal(L, 2);\ > + lua_pushboolean(L, decimal_compare(lhs, rhs) cmp 0);\ > + return 1;\ > +} > + > +uint32_t CTID_DECIMAL; > + > +static decimal_t * > +lua_pushdecimal(struct lua_State *L) > +{ > + decimal_t *res =3D luaL_pushcdata(L, CTID_DECIMAL); > + return res; > +} > + > +static decimal_t * > +lua_checkdecimal(struct lua_State *L, int index) > +{ > + uint32_t ctypeid; > + decimal_t *res =3D luaL_checkcdata(L, index, &ctypeid); > + if (ctypeid !=3D CTID_DECIMAL) > + luaL_error(L, "Expected decimal as %d argument", index); > + return res; > +} > + > +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 =3D lua_gettop(L) + index + 1; > + decimal_t *res =3D lua_pushdecimal(L); > + switch(lua_type(L, index)) > + { > + case LUA_TNUMBER: > + { > + double n =3D lua_tonumber(L, index); > + if (decimal_from_double(res, n) =3D=3D NULL) > + goto err; > + break; > + } > + case LUA_TSTRING: > + { > + const char *str =3D lua_tostring(L, index); > + if (decimal_from_string(res, str) =3D=3D NULL) > + goto err; > + break; > + } > + case LUA_TCDATA: > + { > + uint32_t ctypeid; > + void *cdata =3D luaL_checkcdata(L, index, &ctypeid); > + int64_t ival; > + uint64_t uval; > + double d; > + if (ctypeid =3D=3D 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 =3D *(int8_t *) cdata; > + if (decimal_from_int64(res, ival) =3D=3D NULL) > + goto err; > + break; > + case CTID_INT16: > + ival =3D *(int16_t *) cdata; > + if (decimal_from_int64(res, ival) =3D=3D NULL) > + goto err; > + break; > + case CTID_INT32: > + ival =3D *(int32_t *) cdata; > + if (decimal_from_int64(res, ival) =3D=3D NULL) > + goto err; > + break; > + case CTID_INT64: > + ival =3D *(int64_t *) cdata; > + if (decimal_from_int64(res, ival) =3D=3D NULL) > + goto err; > + break; > + case CTID_UINT8: > + uval =3D *(uint8_t *) cdata; > + if (decimal_from_uint64(res, uval) =3D=3D NULL) > + goto err; > + break; > + case CTID_UINT16: > + uval =3D *(uint16_t *) cdata; > + if (decimal_from_uint64(res, uval) =3D=3D NULL) > + goto err; > + break; > + case CTID_UINT32: > + uval =3D *(uint32_t *) cdata; > + if (decimal_from_uint64(res, uval) =3D=3D NULL) > + goto err; > + break; > + case CTID_UINT64: > + uval =3D *(uint64_t *) cdata; > + if (decimal_from_uint64(res, uval) =3D=3D NULL) > + goto err; > + break; > + case CTID_FLOAT: > + d =3D *(float *) cdata; > + if (decimal_from_double(res, d) =3D=3D NULL) > + goto err; > + break; > + case CTID_DOUBLE: > + d =3D *(double *) cdata; > + if (decimal_from_double(res, d) =3D=3D 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_OP2(add, add); > +LDECIMAL_OP2(sub, sub); > +LDECIMAL_OP2(mul, mul); > +LDECIMAL_OP2(div, div); > +LDECIMAL_OP2(pow, pow); > + > +LDECIMAL_UNOP(log10, log10); > +LDECIMAL_UNOP(ln, ln); > +LDECIMAL_UNOP(exp, exp); > +LDECIMAL_UNOP(sqrt, sqrt); > +LDECIMAL_UNOP(minus, minus); > +LDECIMAL_UNOP(abs, abs); > + > +LDECIMAL_CMPOP(eq, =3D=3D); > +LDECIMAL_CMPOP(lt, <); > +LDECIMAL_CMPOP(le, <=3D); > + > +static int > +ldecimal_tonumber(struct lua_State *L) > +{ > + if (lua_gettop(L) < 1) > + luaL_error(L, "Usage: decimal.tonumber(value)"); > + lua_todecimal(L, 1); > + decimal_t *lhs =3D lua_checkdecimal(L, 1); > + decimal_t *res =3D lua_pushdecimal(L); > + *res =3D *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 =3D lua_checkdecimal(L, 1); > + int n =3D lua_tointeger(L, 2); > + decimal_t *res =3D lua_pushdecimal(L); > + *res =3D *lhs; > + decimal_round(res, n); > + 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 =3D lua_checkdecimal(L, 1); > + lua_pushstring(L, decimal_to_string(lhs)); > + return 1; > +} > + > +static const luaL_Reg ldecimal_mt[] =3D { > + {"log10", ldecimal_log10}, > + {"ln", ldecimal_ln}, > + {"exp", ldecimal_exp}, > + {"sqrt", ldecimal_sqrt}, > + {"round", ldecimal_round}, > + {"minus", ldecimal_minus}, > + {"abs", ldecimal_abs}, > + {"tostring", ldecimal_tostring}, > + {"__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[] =3D { > + {"eq", ldecimal_eq}, > + {"lt", ldecimal_lt}, > + {"le", ldecimal_le}, > + {"add", ldecimal_add}, > + {"sub", ldecimal_sub}, > + {"mul", ldecimal_mul}, > + {"div", ldecimal_div}, > + {"log10", ldecimal_log10}, > + {"ln", ldecimal_ln}, > + {"pow", ldecimal_pow}, > + {"exp", ldecimal_exp}, > + {"sqrt", ldecimal_sqrt}, > + {"round", ldecimal_round}, > + {"minus", ldecimal_minus}, > + {"abs", ldecimal_abs}, > + {"tostring", ldecimal_tostring}, > + {"tonumber", ldecimal_tonumber}, > + {NULL, NULL} > +}; > + > +void > +tarantool_lua_decimal_init(struct lua_State *L) > +{ > + int rc =3D luaL_cdef(L, "typedef struct {" > + "int32_t digits;" > + "int32_t exponent;" > + "uint8_t bits;" > + "uint16_t lsu[13];" > + "} decimal_t;"); > + assert(rc =3D=3D 0); > + 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 =3D luaL_metatype(L, "decimal_t", ldecimal_mt); > + assert(CTID_DECIMAL !=3D 0); > +} > diff --git a/src/lua/decimal.h b/src/lua/decimal.h > new file mode 100644 > index 000000000..12b29d392 > --- /dev/null > +++ b/src/lua/decimal.h > @@ -0,0 +1,39 @@ > +/* > + * 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 ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED > + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL > + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, > + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR > + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF > + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + */ > +#ifndef TARANTOOL_LUA_DECIMAL_H_INCLUDED > +#define TARANTOOL_LUA_DECIMAL_H_INCLUDED > + > +struct lua_State; > + > +void > +tarantool_lua_decimal_init(struct lua_State *L); > + > +#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 >=20 > @@ -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..93c1c7557 > --- /dev/null > +++ b/test/app/decimal.result > @@ -0,0 +1,172 @@ > +decimal =3D require('decimal') > +--- > +... > +test_run =3D require('test_run').new() > +--- > +... > +-- check various constructors > +decimal.tonumber('1234.5678') > +--- > +- '1234.5678' > +... > +decimal.tonumber('1e6') > +--- > +- '1000000' > +... > +decimal.tonumber('-6.234612e2') > +--- > +- '-623.4612' > +... > +decimal.tonumber(tonumber64(2^63)) > +--- > +- '9223372036854775808.000000000000000' > +... > +decimal.tonumber(12345678ULL) > +--- > +- '12345678' > +... > +decimal.tonumber(-12345678LL) > +--- > +- '-12345678' > +... > +decimal.tonumber(1) > +--- > +- '1.000000000000000' > +... > +decimal.tonumber(-1) > +--- > +- '-1.000000000000000' > +... > +decimal.tonumber(2^64) > +--- > +- '18446744073709551616.000000000000000' > +... > +decimal.tonumber(2^(-20)) > +--- > +- '0.000000953674316' > +... > +a =3D decimal.tonumber('100') > +--- > +... > +a:log10() > +--- > +- '2' > +... > +a:ln() > +--- > +- '4.6051701859880913680359829093687284152' > +... > +-- overflow, e^100 > 10^38 > +a:exp() > +--- > +- error: Operation failed > +... > +-- e^87 < 10^38, no overflow, but rounds > +-- to 0 digits after the decimal point. > +decimal.tonumber(87):exp() > +--- > +- '60760302250568721495223289381302760753' > +... > +a:sqrt() > +--- > +- '10' > +... > +a > +--- > +- '100' > +... > +a =3D decimal.tonumber('-123.456') > +--- > +... > +a:round(2) > +--- > +- '-123.46' > +... > +a:round(1) > +--- > +- '-123.5' > +... > +a:round(0) > +--- > +- '-123' > +... > +a:abs() > +--- > +- '123.456' > +... > +a:tostring() > +--- > +- '-123.456' > +... > +-a > +--- > +- '123.456' > +... > +a / 10 > +--- > +- '-12.3456' > +... > +a * 5 > +--- > +- '-617.280000000000000000' > +... > +a + 17 > +--- > +- '-106.456000000000000' > +... > +a - 0.0001 > +--- > +- '-123.456100000000000' > +... > +a ^ 2 > +--- > +- '15241.383936' > +... > +a:abs() ^ 0.5 > +--- > +- '11.111075555498666484621494041182192341' > +... > +a:abs() ^ 0.5 =3D=3D a:abs():sqrt() > +--- > +- true > +... > +a - 2 < a - 1 > +--- > +- true > +... > +a + 1e-10 > a > +--- > +- true > +... > +a <=3D a > +--- > +- true > +... > +a >=3D a > +--- > +- true > +... > +a =3D=3D a > +--- > +- true > +... > +decimal.tostring(a) > +--- > +- '-123.456' > +... > +a:tostring() > +--- > +- '-123.456' > +... > +decimal.abs(a) > +--- > +- '123.456' > +... > +decimal.minus(a) > +--- > +- '123.456' > +... > +decimal.round(a, 2) > +--- > +- '-123.46' > +... > diff --git a/test/app/decimal.test.lua b/test/app/decimal.test.lua > new file mode 100644 > index 000000000..4751c1500 > --- /dev/null > +++ b/test/app/decimal.test.lua > @@ -0,0 +1,50 @@ > +decimal =3D require('decimal') > +test_run =3D require('test_run').new() > + > +-- check various constructors > +decimal.tonumber('1234.5678') > +decimal.tonumber('1e6') > +decimal.tonumber('-6.234612e2') > +decimal.tonumber(tonumber64(2^63)) > +decimal.tonumber(12345678ULL) > +decimal.tonumber(-12345678LL) > +decimal.tonumber(1) > +decimal.tonumber(-1) > +decimal.tonumber(2^64) > +decimal.tonumber(2^(-20)) > + > +a =3D decimal.tonumber('100') > +a:log10() > +a:ln() > +-- overflow, e^100 > 10^38 > +a:exp() > +-- e^87 < 10^38, no overflow, but rounds > +-- to 0 digits after the decimal point. > +decimal.tonumber(87):exp() > +a:sqrt() > +a > +a =3D decimal.tonumber('-123.456') > +a:round(2) > +a:round(1) > +a:round(0) > +a:abs() > +a:tostring() > +-a > +a / 10 > +a * 5 > +a + 17 > +a - 0.0001 > +a ^ 2 > +a:abs() ^ 0.5 > +a:abs() ^ 0.5 =3D=3D a:abs():sqrt() > +a - 2 < a - 1 > +a + 1e-10 > a > +a <=3D a > +a >=3D a > +a =3D=3D a > + > +decimal.tostring(a) > +a:tostring() > +decimal.abs(a) > +decimal.minus(a) > +decimal.round(a, 2) > --=20 > 2.20.1 (Apple Git-117) >=20