From 2481c768b6481486a47bb4acef8f202104cd0f75 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 Date: Fri, 10 Jan 2025 10:23:49 +0800 Subject: [PATCH] decimal conversion and unit tests --- source/libs/decimal/src/decimal.c | 63 ++++++++++++++++----- source/libs/decimal/test/decimalTest.cpp | 70 +++++++++++++----------- 2 files changed, 87 insertions(+), 46 deletions(-) diff --git a/source/libs/decimal/src/decimal.c b/source/libs/decimal/src/decimal.c index 2608be30e0..08a2151846 100644 --- a/source/libs/decimal/src/decimal.c +++ b/source/libs/decimal/src/decimal.c @@ -100,7 +100,8 @@ int32_t decimalGetRetType(const SDataType* pLeftT, const SDataType* pRightT, EOp } 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->type = + pOutType->precision > TSDB_DECIMAL64_MAX_PRECISION ? TSDB_DATA_TYPE_DECIMAL : TSDB_DATA_TYPE_DECIMAL64; pOutType->bytes = tDataTypes[pOutType->type].bytes; return 0; } @@ -286,6 +287,7 @@ static bool decimal64Eq(const DecimalType* pLeft, const DecimalType* pRight, static int32_t decimal64ToStr(const DecimalType* pInt, uint8_t scale, char* pBuf, int32_t bufLen); static void decimal64ScaleDown(Decimal64* pDec, uint8_t scaleDown); static void decimal64ScaleUp(Decimal64* pDec, uint8_t scaleUp); +void decimal64ScaleTo(Decimal64* pDec, uint8_t oldScale, uint8_t newScale); static void decimal128Negate(DecimalType* pInt); static void decimal128Abs(DecimalType* pWord); @@ -752,18 +754,44 @@ static int32_t decimal64FromUint64(DecimalType* pDec, uint8_t prec, uint8_t scal static int32_t decimal64FromDouble(DecimalType* pDec, uint8_t prec, uint8_t scale, double val) { return 0; } -static int32_t decimal64FromDecimal128(DecimalType* pDec, uint8_t pec, uint8_t scale, const DecimalType* pVal, +static int32_t decimal64FromDecimal128(DecimalType* pDec, uint8_t prec, uint8_t scale, const DecimalType* pVal, uint8_t valPrec, uint8_t valScale) { + Decimal128 dec128 = *(Decimal128*)pVal, tmpDec128 = {0}; + bool negative = DECIMAL128_SIGN(&dec128) == -1; + if (negative) decimal128Negate(&dec128); + tmpDec128 = dec128; + + Decimal64 max = {0}; + DECIMAL64_GET_MAX(prec - scale, &max); + decimal128ScaleTo(&dec128, valScale, 0); + if (decimal128Gt(&dec128, &max, WORD_NUM(Decimal64))) { + return TSDB_CODE_DECIMAL_OVERFLOW; + } + decimal128ScaleTo(&tmpDec128, valScale, scale); + DECIMAL64_SET_VALUE((Decimal64*)pDec, DECIMAL128_LOW_WORD(&tmpDec128)); + if (negative) decimal64Negate(pDec); return 0; } -static int32_t decimal64FromDecimal64(DecimalType* pDec, uint8_t pec, uint8_t scale, const DecimalType* pVal, +static int32_t decimal64FromDecimal64(DecimalType* pDec, uint8_t prec, uint8_t scale, const DecimalType* pVal, uint8_t valPrec, uint8_t valScale) { + Decimal64 dec64 = *(Decimal64*)pVal, max = {0}; + bool negative = DECIMAL64_SIGN(&dec64) == -1; + if (negative) decimal64Negate(&dec64); + *(Decimal64*)pDec = dec64; + + DECIMAL64_GET_MAX(prec - scale, &max); + decimal64ScaleTo(&dec64, valScale, 0); + if (decimal64Lt(&max, &dec64, WORD_NUM(Decimal64))) { + return TSDB_CODE_DECIMAL_OVERFLOW; + } + decimal64ScaleTo(pDec, valScale, scale); + if (negative) decimal64Negate(pDec); return 0; } static int32_t decimal128FromInt64(DecimalType* pDec, uint8_t prec, uint8_t scale, int64_t val) { - if (prec - scale <= 18) { + if (prec - scale <= 18) {// TODO wjm test int64 with 19 digits. Decimal64 max = {0}; DECIMAL64_GET_MAX(prec - scale, &max); if (DECIMAL64_GET_VALUE(&max) < val || -DECIMAL64_GET_VALUE(&max) > val) return TSDB_CODE_DECIMAL_OVERFLOW; @@ -794,27 +822,36 @@ static int32_t decimal128FromDouble(DecimalType* pDec, uint8_t prec, uint8_t sca static int32_t decimal128FromDecimal64(DecimalType* pDec, uint8_t prec, uint8_t scale, const DecimalType* pVal, uint8_t valPrec, uint8_t valScale) { Decimal64 dec64 = *(Decimal64*)pVal; - bool negative = false; - if (DECIMAL64_SIGN(&dec64) == -1) { - decimal64Abs(&dec64); - negative = true; - } + bool negative = DECIMAL64_SIGN(&dec64) == -1; + if (negative) decimal64Negate(&dec64); + + makeDecimal128(pDec, 0, DECIMAL64_GET_VALUE(&dec64)); Decimal128 max = {0}; DECIMAL128_GET_MAX(prec - scale, &max); + decimal64ScaleTo(&dec64, valScale, 0); if (decimal128Lt(&max, &dec64, WORD_NUM(Decimal64))) { return TSDB_CODE_DECIMAL_OVERFLOW; } - makeDecimal128(pDec, 0, DECIMAL64_GET_VALUE(&dec64)); - if (negative) decimal128Negate(pDec); decimal128ScaleTo(pDec, valScale, scale); + if (negative) decimal128Negate(pDec); return 0; } static int32_t decimal128FromDecimal128(DecimalType* pDec, uint8_t prec, uint8_t scale, const DecimalType* pVal, uint8_t valPrec, uint8_t valScale) { + bool negative = DECIMAL128_SIGN((Decimal128*)pVal) == -1; + Decimal128 tmpDec = *(Decimal128*)pVal; + if (negative) decimal128Negate(&tmpDec); + *(Decimal128*)pDec = tmpDec; + Decimal128 max = {0}; DECIMAL128_GET_MAX(prec - scale, &max); - + decimal128ScaleTo(&tmpDec, valScale, 0); + if (decimal128Lt(&max, &tmpDec, WORD_NUM(Decimal128))) { + return TSDB_CODE_DECIMAL_OVERFLOW; + } + decimal128ScaleTo(pDec, valScale, scale); + if (negative) decimal128Negate(pDec); return 0; } #define CHECK_OVERFLOW_AND_MAKE_DECIMAL128(pDec, v, max, ABS) \ @@ -972,7 +1009,7 @@ static int32_t decimal128FromDecimal128(DecimalType* pDec, uint8_t prec, uint8_t }) int32_t convertToDecimal(const void* pData, const SDataType* pInputType, void* pOut, const SDataType* pOutType) { - if (pInputType->type == pOutType->type) return 0; + //if (pInputType->type == pOutType->type) return 0; int32_t code = 0; switch (pOutType->type) { diff --git a/source/libs/decimal/test/decimalTest.cpp b/source/libs/decimal/test/decimalTest.cpp index 11888707b7..7632f3279d 100644 --- a/source/libs/decimal/test/decimalTest.cpp +++ b/source/libs/decimal/test/decimalTest.cpp @@ -41,9 +41,10 @@ void extractWideInteger(__int128 a) { printArray<38 / DIGIT_NUM>(segments); } -void printDecimal128(const Decimal128* pDec, int8_t precision, int8_t scale) { +void printDecimal(const DecimalType* pDec, uint8_t type, uint8_t prec, uint8_t scale) { char buf[64] = {0}; - decimalToStr(pDec->words, TSDB_DATA_TYPE_DECIMAL, precision, scale, buf, 64); + int32_t code = decimalToStr(pDec, type, prec, scale, buf, 64); + ASSERT_EQ(code, 0); cout << buf; } @@ -56,6 +57,16 @@ __int128 generate_big_int128(uint32_t digitNum) { return a; } +void checkDecimal(const DecimalType* pDec, uint8_t t, uint8_t prec, uint8_t scale, const char* valExpect) { + ASSERT_TRUE(t == prec > 18 ? TSDB_DATA_TYPE_DECIMAL : TSDB_DATA_TYPE_DECIMAL64); + ASSERT_TRUE(scale <= prec); + char buf[64] = {0}; + int32_t code = decimalToStr(pDec, t, prec, scale, buf, 64); + ASSERT_EQ(code, 0); + ASSERT_STREQ(buf, valExpect); + cout << "decimal" << (prec > 18 ? 128 : 64) << " " << (int32_t)prec << ":" << (int32_t)scale << " -> " << buf << endl; +} + TEST(decimal, a) { __int128 a = generate_big_int128(37); extractWideInteger<9>(a); @@ -93,17 +104,17 @@ TEST(decimal128, divide) { int8_t out_scale = 25; int8_t out_precision = std::min(precision1 - scale1 + scale2 + out_scale, 38); int8_t delta_scale = out_scale + scale2 - scale1; - printDecimal128(&d, precision1, scale1); + printDecimal(&d, TSDB_DATA_TYPE_DECIMAL, precision1, scale1); __int128 a = 1; while (delta_scale-- > 0) a *= 10; Decimal128 multiplier = {0}; makeDecimal128(&multiplier, a >> 64, a); ops->multiply(d.words, multiplier.words, 2); cout << " / "; - printDecimal128(&d2, precision2, scale2); + printDecimal(&d2, TSDB_DATA_TYPE_DECIMAL, precision2, scale2); cout << " = "; ops->divide(d.words, d2.words, 2, remainder.words); - printDecimal128(&d, out_precision, out_scale); + printDecimal(&d, TSDB_DATA_TYPE_DECIMAL, out_precision, out_scale); } TEST(decimal, cpi_taos_fetch_rows) { @@ -158,12 +169,6 @@ TEST(decimal, cpi_taos_fetch_rows) { taos_cleanup(); } -void printDecimal(const char* pDecStr, uint8_t type, uint8_t prec, uint8_t scale) { - ASSERT_TRUE(type == prec > 18 ? TSDB_DATA_TYPE_DECIMAL : TSDB_DATA_TYPE_DECIMAL64); - ASSERT_TRUE(scale <= prec); - - cout << "decimal" << (prec > 18 ? 128 : 64) << " " << (int32_t)prec << ":" << (int32_t)scale << " -> " << pDecStr << endl; -} TEST(decimal, conversion) { // convert uint8 to decimal @@ -175,11 +180,8 @@ TEST(decimal, conversion) { Decimal64 dec64 = {0}; int32_t code = convertToDecimal(&i8, &inputType, &dec64, &decType); ASSERT_TRUE(code == 0); - code = decimalToStr(&dec64, TSDB_DATA_TYPE_DECIMAL64, prec, scale, buf, 64); - ASSERT_EQ(code, 0); - cout << "convert uint8: " << (int32_t)i8 << " to decimal64 " << (uint32_t)prec << ":" << (uint32_t)scale << "-> " - << buf << endl; - ASSERT_STREQ(buf, "22.00"); + cout << "convert uint8: " << (int32_t)i8 << " to "; + checkDecimal(&dec64, TSDB_DATA_TYPE_DECIMAL64, prec, scale, "22.00"); Decimal128 dec128 = {0}; decType.type = TSDB_DATA_TYPE_DECIMAL; @@ -188,31 +190,20 @@ TEST(decimal, conversion) { decType.bytes = 16; code = convertToDecimal(&i8, &inputType, &dec128, &decType); ASSERT_TRUE(code == 0); - code = decimalToStr(&dec128, TSDB_DATA_TYPE_DECIMAL, decType.precision, decType.scale, buf, 64); - ASSERT_EQ(code, 0); cout << "convert uint8: " << (int32_t)i8 << " to "; - printDecimal(buf, TSDB_DATA_TYPE_DECIMAL, decType.precision, decType.scale); - const char* expect = "22.0000000000"; - ASSERT_STREQ(buf, expect); + checkDecimal(&dec128, TSDB_DATA_TYPE_DECIMAL, decType.precision, decType.scale, "22.0000000000"); char inputBuf[64] = "123.000000000000000000000000000000001"; code = decimal128FromStr(inputBuf, strlen(inputBuf), 38, 35, &dec128); ASSERT_EQ(code, 0); - code = decimalToStr(&dec128, TSDB_DATA_TYPE_DECIMAL, 38, 35, buf, 64); - ASSERT_EQ(code, 0); - printDecimal(buf, TSDB_DATA_TYPE_DECIMAL, 38, 35); - expect = "123.00000000000000000000000000000000100"; - ASSERT_STREQ(expect, buf); + checkDecimal(&dec128, TSDB_DATA_TYPE_DECIMAL, 38, 35, "123.00000000000000000000000000000000100"); inputType.type = TSDB_DATA_TYPE_DECIMAL64; inputType.precision = prec; inputType.scale = scale; code = convertToDecimal(&dec64, &inputType, &dec128, &decType); ASSERT_EQ(code, 0); - decimalToStr(&dec128, TSDB_DATA_TYPE_DECIMAL, 38, 10, buf, 64); - expect = "22.0000000000"; - ASSERT_STREQ(expect, buf); - printDecimal(buf, TSDB_DATA_TYPE_DECIMAL, 38, 10); + checkDecimal(&dec128, TSDB_DATA_TYPE_DECIMAL, 38, 10, "22.0000000000"); } static constexpr uint64_t k1E16 = 10000000000000000LL; @@ -317,10 +308,23 @@ TEST(decimal, op) { 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); + checkDecimal(&res, TSDB_DATA_TYPE_DECIMAL64, tc.precision, tc.scale, "580.11"); + + a = {1234567890}; + b = {9876543210}; + ta = getDecimalType(18, 5); + tb = getDecimalType(15, 3); + code = decimalGetRetType(&ta, &tb, op, &tc); ASSERT_EQ(code, 0); - ASSERT_STREQ(buf, "580.11"); + tExpect.precision = 19; + tExpect.scale = 5; + tExpect.type = TSDB_DATA_TYPE_DECIMAL; + tExpect.bytes = sizeof(Decimal128); + ASSERT_EQ(tExpect, tc); + Decimal128 res128 = {0}; + code = decimalOp(op, &ta, &tb, &tc, &a, &b, &res128); + ASSERT_EQ(code, 0); + checkDecimal(&res128, 0, tExpect.precision, tExpect.scale, "9888888.88890"); } int main(int argc, char** argv) {