unit test for decimal

This commit is contained in:
wangjiaming0909 2025-01-09 18:02:34 +08:00
parent 1ef210980f
commit c1f49fee95
2 changed files with 108 additions and 13 deletions

View File

@ -52,20 +52,28 @@ static uint8_t maxPrecision(DecimalInternalType type) {
int32_t decimalGetRetType(const SDataType* pLeftT, const SDataType* pRightT, EOperatorType opType,
SDataType* pOutType) {
if (IS_FLOAT_TYPE(pLeftT->type) || IS_FLOAT_TYPE(pRightT->type)) {
if (IS_FLOAT_TYPE(pLeftT->type) || IS_FLOAT_TYPE(pRightT->type) || IS_VAR_DATA_TYPE(pLeftT->type) ||
IS_VAR_DATA_TYPE(pRightT->type)) {
pOutType->type = TSDB_DATA_TYPE_DOUBLE;
pOutType->bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes;
return 0;
}
if (IS_NULL_TYPE(pLeftT->type) || IS_NULL_TYPE(pRightT->type)) {
pOutType->type = TSDB_DATA_TYPE_NULL;
pOutType->bytes = tDataTypes[TSDB_DATA_TYPE_NULL].bytes;
return 0;
}
// TODO wjm check not supported types
uint8_t p1 = pLeftT->precision, s1 = pLeftT->scale, p2 = pRightT->precision, s2 = pRightT->scale;
if (IS_DECIMAL_TYPE(pLeftT->type)) {
p2 = TSDB_DECIMAL128_MAX_PRECISION;
if (!IS_DECIMAL_TYPE(pLeftT->type)) {
p1 = TSDB_DECIMAL_MAX_PRECISION;
s1 = s2; // TODO wjm take which scale? Maybe use default DecimalMax
} else {
p1 = TSDB_DECIMAL128_MAX_PRECISION;
}
if (!IS_DECIMAL_TYPE(pRightT->type)) {
p1 = TSDB_DECIMAL_MAX_PRECISION;
s2 = s1;
}
@ -90,8 +98,10 @@ int32_t decimalGetRetType(const SDataType* pLeftT, const SDataType* pRightT, EOp
default:
return TSDB_CODE_TSC_INVALID_OPERATION;
}
pOutType->type = TSDB_DATA_TYPE_DECIMAL;
pOutType->bytes = tDataTypes[TSDB_DATA_TYPE_DECIMAL].bytes;
pOutType->precision = TMIN(pOutType->precision, TSDB_DECIMAL_MAX_PRECISION);
pOutType->scale = TMIN(pOutType->scale, TSDB_DECIMAL_MAX_SCALE);
pOutType->type = pOutType->precision > TSDB_DECIMAL64_MAX_PRECISION ? TSDB_DATA_TYPE_DECIMAL : TSDB_DATA_TYPE_DECIMAL64;
pOutType->bytes = tDataTypes[pOutType->type].bytes;
return 0;
}
@ -458,6 +468,7 @@ static Decimal128 SCALE_MULTIPLIER_128[38 + 1] = {
#define DECIMAL128_ONE SCALE_MULTIPLIER_128[0]
#define DECIMAL128_TEN SCALE_MULTIPLIER_128[1]
// TODO wjm pre define it?? actually, its MAX_INTEGER, not MAX
#define DECIMAL128_GET_MAX(precision, pMax) \
do { \
*(pMax) = SCALE_MULTIPLIER_128[precision]; \
@ -641,7 +652,6 @@ static int32_t decimal128ToStr(const DecimalType* pInt, uint8_t scale, char* pBu
return 0;
}
for (int32_t i = digitNum - 1; i >= 0; --i) {
// TODO wjm test 0.0000000000000000000000000000000001
len += snprintf(buf + len, 64 - len, i == digitNum - 1 ? "%" PRIu64 : "%018" PRIu64, segments[i]);
}
int32_t wholeLen = len - scale;
@ -673,15 +683,18 @@ int32_t decimalToStr(const DecimalType* pDec, int8_t dataType, int8_t precision,
int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pRightT, const SDataType* pOutT,
const void* pLeftData, const void* pRightData, void* pOutputData) {
int32_t code = 0;
if (pOutT->type != TSDB_DATA_TYPE_DECIMAL) return TSDB_CODE_INTERNAL_ERROR;
// TODO wjm if output precision <= 18, no need to convert to decimal128
Decimal pLeft = *(Decimal*)pLeftData, pRight = *(Decimal*)pRightData;
Decimal pLeft = {0}, pRight = {0};
SDataType tmpType = *pOutT;
tmpType.type = TSDB_DATA_TYPE_DECIMAL;
tmpType.precision = TSDB_DECIMAL_MAX_PRECISION;
if (TSDB_DATA_TYPE_DECIMAL != pLeftT->type || pLeftT->scale != pOutT->scale) {
code = convertToDecimal(pLeftData, pLeftT, &pLeft, pOutT);
code = convertToDecimal(pLeftData, pLeftT, &pLeft, &tmpType);
if (TSDB_CODE_SUCCESS != code) return code;
}
if (pRightT && (TSDB_DATA_TYPE_DECIMAL != pRightT->type || pRightT->scale != pOutT->scale)) {
code = convertToDecimal(pRightData, pRightT, &pRight, pOutT);
code = convertToDecimal(pRightData, pRightT, &pRight, &tmpType);
if (TSDB_CODE_SUCCESS != code) return code;
}
@ -693,7 +706,7 @@ int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pR
default:
break;
}
return code;
return convertToDecimal(&pLeft, &tmpType, pOutputData, pOutT);
}
#define ABS_INT64(v) (v) == INT64_MIN ? (uint64_t)INT64_MAX + 1 : (uint64_t)llabs(v)
@ -796,8 +809,12 @@ static int32_t decimal128FromDecimal64(DecimalType* pDec, uint8_t prec, uint8_t
decimal128ScaleTo(pDec, valScale, scale);
return 0;
}
static int32_t decimal128FromDecimal128(DecimalType* pDec, uint8_t prec, uint8_t scale, const DecimalType* pVal,
uint8_t valPrec, uint8_t valScale) {
Decimal128 max = {0};
DECIMAL128_GET_MAX(prec - scale, &max);
return 0;
}
#define CHECK_OVERFLOW_AND_MAKE_DECIMAL128(pDec, v, max, ABS) \

View File

@ -230,6 +230,12 @@ TEST(decimal, decimalFromStr) {
resExpect += 1;
resExpect *= 100;
ASSERT_EQ(res, resExpect);
char buf[64] = "999.999";
Decimal64 dec64 = {0};
code = decimal64FromStr(buf, strlen(buf), 6, 3, &dec64);
ASSERT_EQ(code, 0);
ASSERT_EQ(999999, DECIMAL64_GET_VALUE(&dec64));
}
TEST(decimal, toStr) {
@ -245,6 +251,78 @@ TEST(decimal, toStr) {
ASSERT_STREQ(buf, "0");
}
SDataType getDecimalType(uint8_t prec, uint8_t scale) {
if (prec <= 18) {
return {.type = TSDB_DATA_TYPE_DECIMAL64, .precision = prec, .scale = scale, .bytes = 8};
} else if (prec <= 38) {
return {.type = TSDB_DATA_TYPE_DECIMAL, .precision = prec, .scale = scale, .bytes = 16};
}
return {};
}
bool operator==(const SDataType& lt, const SDataType& rt) {
return lt.type == rt.type && lt.precision == rt.precision && lt.scale == rt.scale && lt.bytes == rt.bytes;
}
TEST(decimal, decimalOpRetType) {
EOperatorType op = OP_TYPE_ADD;
auto ta = getDecimalType(10, 2);
auto tb = getDecimalType(10, 2);
SDataType tc{}, tExpect = {.type = TSDB_DATA_TYPE_DECIMAL64, .precision = 11, .scale = 2, .bytes = sizeof(Decimal64)};
int32_t code = decimalGetRetType(&ta, &tb, op, &tc);
ASSERT_EQ(code, 0);
ASSERT_EQ(tExpect, tc);
ta.bytes = 8;
ta.type = TSDB_DATA_TYPE_TIMESTAMP;
code = decimalGetRetType(&ta, &tb, op, &tc);
ASSERT_EQ(code, 0);
tExpect.type = TSDB_DATA_TYPE_DECIMAL;
tExpect.precision = TSDB_DECIMAL_MAX_PRECISION;
tExpect.scale = 2;
tExpect.bytes = sizeof(Decimal);
ASSERT_EQ(tExpect, tc);
ta.bytes = 8;
ta.type = TSDB_DATA_TYPE_DOUBLE;
tc = {0};
code = decimalGetRetType(&ta, &tb, op, &tc);
ASSERT_EQ(code, 0);
tExpect.type = TSDB_DATA_TYPE_DOUBLE;
tExpect.precision = 0;
tExpect.scale = 0;
tExpect.bytes = 8;
ASSERT_EQ(tExpect, tc);
}
TEST(decimal, op) {
const char* stra = "123.99", * strb = "456.12";
EOperatorType op = OP_TYPE_ADD;
auto ta = getDecimalType(10, 2);
Decimal64 a = {0};
int32_t code = decimal64FromStr(stra, strlen(stra), ta.precision, ta.scale, &a);
ASSERT_EQ(code, 0);
auto tb = getDecimalType(10, 2);
Decimal64 b{0};
code = decimal64FromStr(strb, strlen(strb), tb.precision, tb.scale, &b);
ASSERT_EQ(code, 0);
SDataType tc{}, tExpect{.type = TSDB_DATA_TYPE_DECIMAL64, .precision = 11, .scale = 2, .bytes = sizeof(Decimal64)};
code = decimalGetRetType(&ta, &tb, op, &tc);
ASSERT_EQ(code, 0);
ASSERT_EQ(tc, tExpect);
Decimal64 res{};
code = decimalOp(op, &ta, &tb, &tc, &a, &b, &res);
ASSERT_EQ(code, 0);
char buf[64] = {0};
code = decimalToStr(&res, TSDB_DATA_TYPE_DECIMAL64, tc.precision, tc.scale, buf, 64);
ASSERT_EQ(code, 0);
ASSERT_STREQ(buf, "580.11");
}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();