decimal add test cases
This commit is contained in:
parent
76e849b5cf
commit
fe9d297cba
|
@ -177,7 +177,7 @@ typedef struct SColumnDataAgg {
|
||||||
struct {
|
struct {
|
||||||
uint64_t decimal128Sum[2];
|
uint64_t decimal128Sum[2];
|
||||||
uint64_t decimal128Max[2];
|
uint64_t decimal128Max[2];
|
||||||
uint64_t decimal128Min[2];
|
uint64_t decimal128Min[2]; // TODO wjm 1. use deicmal128Sum for decimal64, 2. add overflow flag
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
} SColumnDataAgg;
|
} SColumnDataAgg;
|
||||||
|
|
|
@ -4253,7 +4253,7 @@ static FORCE_INLINE void tColDataCalcSMADecimal64Type(SColData* pColData, SColum
|
||||||
pAggs->numOfNull++;
|
pAggs->numOfNull++;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
CALC_DECIMAL_SUM_MAX_MIN(Decimal64, pOps, pColData, pSum, pMax, pMin);
|
CALC_DECIMAL_SUM_MAX_MIN(Decimal64, pOps, pColData, pSum, pMax, pMin);// TODO wjm what if overflow
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -27,6 +27,10 @@ typedef struct _UInt128 {
|
||||||
uint64_t high;
|
uint64_t high;
|
||||||
} _UInt128;
|
} _UInt128;
|
||||||
|
|
||||||
|
typedef struct Int256 {
|
||||||
|
char data[32];
|
||||||
|
} Int256;
|
||||||
|
|
||||||
// TODO wjm use cmake to check if the compiler supports __int128_t
|
// TODO wjm use cmake to check if the compiler supports __int128_t
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
// #if 0
|
// #if 0
|
||||||
|
|
|
@ -170,8 +170,7 @@ static int32_t decimalVarFromStr(const char* str, int32_t len, DecimalVar* resul
|
||||||
if (afterPoint) {
|
if (afterPoint) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// TODO wjm value too large
|
return TSDB_CODE_DECIMAL_OVERFLOW;
|
||||||
return TSDB_CODE_INVALID_DATA_FMT;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result->precision += places;
|
result->precision += places;
|
||||||
|
@ -250,9 +249,6 @@ static int32_t decimalGetWhole(const DecimalType* pDec, DecimalInternalType type
|
||||||
DECIMAL64_CLONE(pWhole, pDec);
|
DECIMAL64_CLONE(pWhole, pDec);
|
||||||
Decimal64 scaleMul = SCALE_MULTIPLIER_64[scale];
|
Decimal64 scaleMul = SCALE_MULTIPLIER_64[scale];
|
||||||
pOps->divide(pWhole, &scaleMul, 1, NULL);
|
pOps->divide(pWhole, &scaleMul, 1, NULL);
|
||||||
if (TSDB_CODE_SUCCESS != 0) {
|
|
||||||
// TODO wjm
|
|
||||||
}
|
|
||||||
pOps->abs(pWhole);
|
pOps->abs(pWhole);
|
||||||
} else {
|
} else {
|
||||||
memcpy(pWhole, pDec, DECIMAL_GET_WORD_NUM(type) * sizeof(DecimalWord));
|
memcpy(pWhole, pDec, DECIMAL_GET_WORD_NUM(type) * sizeof(DecimalWord));
|
||||||
|
@ -646,7 +642,7 @@ static void extractDecimal128Digits(const Decimal128* pDec, uint64_t* digits, in
|
||||||
*digitNum = 0;
|
*digitNum = 0;
|
||||||
makeUInt128(&a, DECIMAL128_HIGH_WORD(pDec), DECIMAL128_LOW_WORD(pDec));
|
makeUInt128(&a, DECIMAL128_HIGH_WORD(pDec), DECIMAL128_LOW_WORD(pDec));
|
||||||
while (!uInt128Eq(&a, &uInt128Zero)) {
|
while (!uInt128Eq(&a, &uInt128Zero)) {
|
||||||
uint64_t hi = a >> 64; // TODO wjm ???
|
uint64_t hi = a >> 64; // TODO wjm use function, UInt128 may be a struct.
|
||||||
uint64_t lo = a;
|
uint64_t lo = a;
|
||||||
|
|
||||||
uint64_t hiQuotient = hi / k1e18;
|
uint64_t hiQuotient = hi / k1e18;
|
||||||
|
@ -901,6 +897,7 @@ int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pR
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case OP_TYPE_ADD:
|
case OP_TYPE_ADD:
|
||||||
|
// TODO wjm check overflow
|
||||||
decimalAdd(&left, <, &right, &rt, pOutT);
|
decimalAdd(&left, <, &right, &rt, pOutT);
|
||||||
break;
|
break;
|
||||||
case OP_TYPE_SUB:
|
case OP_TYPE_SUB:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "decimal.h"
|
#include "decimal.h"
|
||||||
#include "tdatablock.h"
|
#include "tdatablock.h"
|
||||||
|
@ -124,6 +125,7 @@ struct NumericType<64> {
|
||||||
using Type = Numeric64;
|
using Type = Numeric64;
|
||||||
static constexpr int8_t dataType = TSDB_DATA_TYPE_DECIMAL64;
|
static constexpr int8_t dataType = TSDB_DATA_TYPE_DECIMAL64;
|
||||||
static constexpr int8_t maxPrec = TSDB_DECIMAL64_MAX_PRECISION;
|
static constexpr int8_t maxPrec = TSDB_DECIMAL64_MAX_PRECISION;
|
||||||
|
static constexpr int8_t bytes = DECIMAL64_BYTES;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -131,6 +133,7 @@ struct NumericType<128> {
|
||||||
using Type = Numeric128;
|
using Type = Numeric128;
|
||||||
static constexpr int8_t dataType = TSDB_DATA_TYPE_DECIMAL;
|
static constexpr int8_t dataType = TSDB_DATA_TYPE_DECIMAL;
|
||||||
static constexpr int8_t maxPrec = TSDB_DECIMAL_MAX_PRECISION;
|
static constexpr int8_t maxPrec = TSDB_DECIMAL_MAX_PRECISION;
|
||||||
|
static constexpr int8_t bytes = DECIMAL128_BYTES;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -174,15 +177,19 @@ class Numeric {
|
||||||
throw std::string(tstrerror(code));
|
throw std::string(tstrerror(code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Numeric() = default;
|
||||||
Numeric(const Numeric& o) = default;
|
Numeric(const Numeric& o) = default;
|
||||||
~Numeric() = default;
|
~Numeric() = default;
|
||||||
Numeric& operator=(const Numeric& o) = default;
|
Numeric& operator=(const Numeric& o) = default;
|
||||||
|
|
||||||
SDataType getRetType(EOperatorType op, const SDataType& lt, const SDataType& rt) const {
|
static SDataType getRetType(EOperatorType op, const SDataType& lt, const SDataType& rt) {
|
||||||
SDataType ot = {0};
|
SDataType ot = {0};
|
||||||
decimalGetRetType(<, &rt, op, &ot);
|
decimalGetRetType(<, &rt, op, &ot);
|
||||||
return ot;
|
return ot;
|
||||||
}
|
}
|
||||||
|
SDataType type() const {
|
||||||
|
return {.type = NumericType<BitNum>::dataType, .precision = prec(), .scale = scale(), .bytes = NumericType<BitNum>::bytes};
|
||||||
|
}
|
||||||
uint8_t prec() const { return prec_; }
|
uint8_t prec() const { return prec_; }
|
||||||
uint8_t scale() const { return scale_; }
|
uint8_t scale() const { return scale_; }
|
||||||
const Type& dec() const { return dec_; }
|
const Type& dec() const { return dec_; }
|
||||||
|
@ -195,8 +202,8 @@ class Numeric {
|
||||||
|
|
||||||
template <int BitNum2, int BitNumO>
|
template <int BitNum2, int BitNumO>
|
||||||
Numeric<BitNumO> binaryOp(const Numeric<BitNum2>& r, EOperatorType op) {
|
Numeric<BitNumO> binaryOp(const Numeric<BitNum2>& r, EOperatorType op) {
|
||||||
SDataType lt{.type = NumericType<BitNum>::dataType, .precision = prec_, .scale = scale_, .bytes = BitNum};
|
SDataType lt{.type = NumericType<BitNum>::dataType, .precision = prec_, .scale = scale_, .bytes = NumericType<BitNum>::bytes};
|
||||||
SDataType rt{.type = NumericType<BitNum2>::dataType, .precision = r.prec(), .scale = r.scale(), .bytes = BitNum2};
|
SDataType rt{.type = NumericType<BitNum2>::dataType, .precision = r.prec(), .scale = r.scale(), .bytes = NumericType<BitNum2>::bytes};
|
||||||
SDataType ot = getRetType(op, lt, rt);
|
SDataType ot = getRetType(op, lt, rt);
|
||||||
Numeric<BitNumO> out{ot.precision, ot.scale, "0"};
|
Numeric<BitNumO> out{ot.precision, ot.scale, "0"};
|
||||||
int32_t code = decimalOp(op, <, &rt, &ot, &dec_, &r.dec(), &out);
|
int32_t code = decimalOp(op, <, &rt, &ot, &dec_, &r.dec(), &out);
|
||||||
|
@ -207,12 +214,13 @@ class Numeric {
|
||||||
template <int BitNumO, typename T>
|
template <int BitNumO, typename T>
|
||||||
Numeric<BitNumO> binaryOp(const T& r, EOperatorType op) {
|
Numeric<BitNumO> binaryOp(const T& r, EOperatorType op) {
|
||||||
using TypeInfo = TrivialTypeInfo<T>;
|
using TypeInfo = TrivialTypeInfo<T>;
|
||||||
SDataType lt{.type = NumericType<BitNum>::dataType, .precision = prec_, .scale = scale_, .bytes = BitNum};
|
SDataType lt{.type = NumericType<BitNum>::dataType, .precision = prec_, .scale = scale_, .bytes = NumericType<BitNum>::bytes};
|
||||||
SDataType rt{.type = TypeInfo::dataType, .precision = 0, .scale = 0, .bytes = TypeInfo::bytes};
|
SDataType rt{.type = TypeInfo::dataType, .precision = 0, .scale = 0, .bytes = TypeInfo::bytes};
|
||||||
SDataType ot = getRetType(op, lt, rt);
|
SDataType ot = getRetType(op, lt, rt);
|
||||||
Numeric<BitNumO> out{ot.precision, ot.scale, "0"};
|
Numeric<BitNumO> out{ot.precision, ot.scale, "0"};
|
||||||
int32_t code = decimalOp(op, <, &rt, &ot, &dec_, &r, &out);
|
int32_t code = decimalOp(op, <, &rt, &ot, &dec_, &r, &out);
|
||||||
if (code != 0) throw std::overflow_error(tstrerror(code));
|
if (code == TSDB_CODE_DECIMAL_OVERFLOW) throw std::overflow_error(tstrerror(code));
|
||||||
|
if (code != 0) throw std::runtime_error(tstrerror(code));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
#define DEFINE_OPERATOR(op, op_type) \
|
#define DEFINE_OPERATOR(op, op_type) \
|
||||||
|
@ -233,7 +241,13 @@ class Numeric {
|
||||||
template <typename T, int BitNumO = 128> \
|
template <typename T, int BitNumO = 128> \
|
||||||
Numeric<BitNumO> operator op(const T & r) { \
|
Numeric<BitNumO> operator op(const T & r) { \
|
||||||
cout << *this << " " #op " " << r << "(" << typeid(T).name() << ")" << " = "; \
|
cout << *this << " " #op " " << r << "(" << typeid(T).name() << ")" << " = "; \
|
||||||
auto res = binaryOp<BitNumO, T>(r, op_type); \
|
Numeric<BitNumO> res = {}; \
|
||||||
|
try { \
|
||||||
|
res = binaryOp<BitNumO, T>(r, op_type); \
|
||||||
|
} catch (...) { \
|
||||||
|
cout << "Exception caught during binaryOp" << endl; \
|
||||||
|
throw; \
|
||||||
|
} \
|
||||||
cout << res << endl; \
|
cout << res << endl; \
|
||||||
return res; \
|
return res; \
|
||||||
}
|
}
|
||||||
|
@ -306,7 +320,7 @@ class Numeric {
|
||||||
DEFINE_OPERATOR_T(float);
|
DEFINE_OPERATOR_T(float);
|
||||||
DEFINE_OPERATOR_T(double);
|
DEFINE_OPERATOR_T(double);
|
||||||
|
|
||||||
Numeric operator=(const char* str) {
|
Numeric& operator=(const char* str) {
|
||||||
std::string s = str;
|
std::string s = str;
|
||||||
int32_t code = 0;
|
int32_t code = 0;
|
||||||
if (BitNum == 64) {
|
if (BitNum == 64) {
|
||||||
|
@ -330,7 +344,7 @@ class Numeric {
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEFINE_OPERATOR_EQ_T(type) \
|
#define DEFINE_OPERATOR_EQ_T(type) \
|
||||||
Numeric operator=(type v) { \
|
Numeric& operator=(type v) { \
|
||||||
int32_t code = 0; \
|
int32_t code = 0; \
|
||||||
if (BitNum == 64) { \
|
if (BitNum == 64) { \
|
||||||
DEFINE_OPERATOR_FROM_FOR_BITNUM(type, 64); \
|
DEFINE_OPERATOR_FROM_FOR_BITNUM(type, 64); \
|
||||||
|
@ -352,6 +366,23 @@ class Numeric {
|
||||||
DEFINE_OPERATOR_EQ_T(bool);
|
DEFINE_OPERATOR_EQ_T(bool);
|
||||||
DEFINE_OPERATOR_EQ_T(double);
|
DEFINE_OPERATOR_EQ_T(double);
|
||||||
DEFINE_OPERATOR_EQ_T(float);
|
DEFINE_OPERATOR_EQ_T(float);
|
||||||
|
|
||||||
|
Numeric& operator=(const Decimal128& d) {
|
||||||
|
SDataType inputDt = {.type = TSDB_DATA_TYPE_DECIMAL, .precision = prec(), .scale = scale(), .bytes = DECIMAL128_BYTES};
|
||||||
|
SDataType outputDt = {.type = NumericType<BitNum>::dataType, .precision = prec(), .scale = scale(), .bytes = NumericType<BitNum>::bytes};
|
||||||
|
int32_t code = convertToDecimal(&d, &inputDt, &dec_, &outputDt);
|
||||||
|
if (code == TSDB_CODE_DECIMAL_OVERFLOW) throw std::overflow_error(tstrerror(code));
|
||||||
|
if (code != 0) throw std::runtime_error(tstrerror(code));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
Numeric& operator=(const Decimal64& d) {
|
||||||
|
SDataType inputDt = {.type = TSDB_DATA_TYPE_DECIMAL64, .precision = prec(), .scale = scale(), .bytes = DECIMAL64_BYTES};
|
||||||
|
SDataType outputDt = {.type = NumericType<BitNum>::dataType, .precision = prec_, .scale = scale_, .bytes = NumericType<BitNum>::bytes};
|
||||||
|
int32_t code = convertToDecimal(&d, &inputDt, &dec_, &outputDt);
|
||||||
|
if (code == TSDB_CODE_DECIMAL_OVERFLOW) throw std::overflow_error(tstrerror(code));
|
||||||
|
if (code != 0) throw std::runtime_error(tstrerror(code));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int BitNum>
|
template <int BitNum>
|
||||||
|
@ -897,6 +928,69 @@ TEST(decimal, randomGenerator) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(deicmal, decimalFromStr_all) {
|
||||||
|
// TODO test e/E
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASSERT_OVERFLOW(op) \
|
||||||
|
try { \
|
||||||
|
auto res = op; \
|
||||||
|
} catch (std::overflow_error & e) { \
|
||||||
|
} catch (std::exception & e) { \
|
||||||
|
FAIL(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(decimal, op_overflow) {
|
||||||
|
// divide 0 error
|
||||||
|
Numeric<128> dec{38, 2, string(36, '9') + ".99"};
|
||||||
|
ASSERT_OVERFLOW(dec / 0); // TODO wjm add divide by 0 error code
|
||||||
|
|
||||||
|
// test decimal128Max
|
||||||
|
Numeric<128> max{38, 10, "0"};
|
||||||
|
max = decimal128Max;
|
||||||
|
ASSERT_EQ(max.toString(), "9999999999999999999999999999.9999999999");
|
||||||
|
|
||||||
|
{
|
||||||
|
// multiply overflow
|
||||||
|
ASSERT_OVERFLOW(max * 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// multiply not overflow, no trim scale
|
||||||
|
Numeric<64> dec64{18, 10, "99999999.9999999999"};
|
||||||
|
Numeric<128> dec128{19, 10, "999999999.9999999999"};
|
||||||
|
|
||||||
|
auto rett = Numeric<64>::getRetType(OP_TYPE_MULTI, dec64.type(), dec128.type());
|
||||||
|
ASSERT_EQ(rett.precision, 38);
|
||||||
|
ASSERT_EQ(rett.type, TSDB_DATA_TYPE_DECIMAL);
|
||||||
|
ASSERT_EQ(rett.scale, dec64.scale() + dec128.scale());
|
||||||
|
|
||||||
|
auto res = dec64 * dec128;
|
||||||
|
ASSERT_EQ(res.toString(), "99999999999999999.89000000000000000001");
|
||||||
|
|
||||||
|
// multiply not overflow, trim scale from 20 - 19
|
||||||
|
Numeric<128> dec128_2{20, 10, "9999999999.9999999999"};
|
||||||
|
rett = Numeric<128>::getRetType(OP_TYPE_MULTI, dec64.type(), dec128_2.type());
|
||||||
|
ASSERT_EQ(rett.scale, 19);
|
||||||
|
res = dec64 * dec128_2;
|
||||||
|
ASSERT_EQ(res.toString(), "999999999999999998.9900000000000000000");
|
||||||
|
|
||||||
|
// trim scale from 20 - 18
|
||||||
|
dec128_2 = {21, 10, "99999999999.9999999999"};
|
||||||
|
rett = Numeric<128>::getRetType(OP_TYPE_MULTI, dec64.type(), dec128_2.type());
|
||||||
|
ASSERT_EQ(rett.scale, 18);
|
||||||
|
res = dec64 * dec128_2;
|
||||||
|
ASSERT_EQ(res.toString(), "9999999999999999990.000000000000000000");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// multiply middle res overflow, but final res not overflow
|
||||||
|
// same scale multiply
|
||||||
|
// different scale multiply
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
|
|
Loading…
Reference in New Issue