From eb7b35881accffa4a545621edf0c83c2e1dd0ad8 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 Date: Mon, 30 Dec 2024 13:45:01 +0800 Subject: [PATCH] support decimal128 divide --- include/libs/decimal/decimal.h | 4 +- source/libs/decimal/src/decimal.c | 52 ++++++++++++++++++------ source/libs/decimal/test/decimalTest.cpp | 35 ++++++++++++++++ 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/include/libs/decimal/decimal.h b/include/libs/decimal/decimal.h index 8ea3c8984c..5a8561bdff 100644 --- a/include/libs/decimal/decimal.h +++ b/include/libs/decimal/decimal.h @@ -47,7 +47,7 @@ int32_t decimal128FromStr(const char* str, int32_t len, uint8_t* precision, uint int32_t decimal64ToDataVal(const Decimal64* dec, SValue* pVal); int32_t decimal128ToDataVal(Decimal128* dec, SValue* pVal); -int32_t decimalToStr(DecimalWord* pDec, int8_t type, int8_t precision, int8_t scale, char* pBuf, int32_t bufLen); +int32_t decimalToStr(const DecimalWord* pDec, int8_t type, int8_t precision, int8_t scale, char* pBuf, int32_t bufLen); typedef struct SDecimalOps { void (*negate)(DecimalWord* pWord); @@ -55,7 +55,7 @@ typedef struct SDecimalOps { void (*add)(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); void (*subtract)(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); void (*multiply)(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); - void (*divide)(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); + void (*divide)(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum, DecimalWord* pRemainder); void (*mod)(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); bool (*lt)(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); bool (*gt)(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); diff --git a/source/libs/decimal/src/decimal.c b/source/libs/decimal/src/decimal.c index 21272985f1..956eea1de0 100644 --- a/source/libs/decimal/src/decimal.c +++ b/source/libs/decimal/src/decimal.c @@ -194,7 +194,7 @@ static int32_t decimalGetWhole(const DecimalWord* pDec, DecimalInternalType type if (type == DECIMAL_64) { pWhole[0] = *pDec; DecimalWord scaleMul = SCALE_MULTIPLIER_64[scale]; - pOps->divide(pWhole, &scaleMul, 1); + pOps->divide(pWhole, &scaleMul, 1, NULL); if (TSDB_CODE_SUCCESS != 0) { // TODO wjm } @@ -224,7 +224,7 @@ static void decimal64Abs(DecimalWord* pInt); static void decimal64Add(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static void decimal64Subtract(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static void decimal64Multiply(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); -static void decimal64divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); +static void decimal64divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum, DecimalWord* pRemainder); static void decimal64Mod(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static bool decimal64Lt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static bool decimal64Gt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); @@ -236,7 +236,7 @@ static void decimal128Abs(DecimalWord* pWord); static void decimal128Add(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static void decimal128Subtract(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static void decimal128Multiply(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); -static void decimal128divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); +static void decimal128divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum, DecimalWord* pRemainder); static void decimal128Mod(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static bool decimal128Lt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); static bool decimal128Gt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); @@ -270,7 +270,10 @@ void decimal64Abs(DecimalWord* pInt) { *pInt = TABS(*pInt); } void decimal64Add(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { *pLeft += *pRight; } void decimal64Subtract(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { *pLeft -= *pRight; } void decimal64Multiply(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { *pLeft *= *pRight; } -void decimal64divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { *pLeft /= *pRight; } +void decimal64divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum, DecimalWord* pRemainder) { + *pLeft /= *pRight; + if (pRemainder) *pRemainder = *pLeft % *pRight; +} void decimal64Mod(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { *pLeft %= *pRight; } bool decimal64Lt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { return *pLeft < *pRight; } bool decimal64Gt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { return *pLeft > *pRight; } @@ -327,7 +330,7 @@ static void decimal128Abs(DecimalWord* pWord) { } static void decimal128Add(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { - Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = (Decimal128*)pRight; Decimal128 right = {0}; DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); @@ -338,7 +341,7 @@ static void decimal128Add(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t } static void decimal128Subtract(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { - Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = (Decimal128*)pRight; Decimal128 right = {0}; DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); @@ -349,7 +352,7 @@ static void decimal128Subtract(DecimalWord* pLeft, const DecimalWord* pRight, ui } static void decimal128Multiply(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { - Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = (Decimal128*)pRight; Decimal128 right = {0}; DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); @@ -367,7 +370,8 @@ static void decimal128Multiply(DecimalWord* pLeft, const DecimalWord* pRight, ui } static bool decimal128Lt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { - Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + // TODO wjm pRightDec use const + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = (Decimal128*)pRight; Decimal128 right = {0}; DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); @@ -376,12 +380,34 @@ static bool decimal128Lt(const DecimalWord* pLeft, const DecimalWord* pRight, ui DECIMAL128_LOW_WORDS(pLeftDec) < DECIMAL128_LOW_WORDS(pRightDec)); } -static void decimal128divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) {} +static void decimal128divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum, DecimalWord* pRemainder) { + Decimal128* pLeftDec = (Decimal128*)pLeft, *pRightDec = (Decimal128*)pRight, *pRemainderDec = (Decimal128*)pRemainder; + Decimal128 right = {0}; + DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); -static void decimal128Mod(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) {} + bool negate = DECIMAL128_SIGN(pLeftDec) != DECIMAL128_SIGN(pRightDec); + UInt128 a = {0}, b = {0}, c = {0}, d = {0}; + Decimal128 x = *pLeftDec, y = *pRightDec; + decimal128Abs(x.words); + decimal128Abs(y.words); + makeUInt128(&a, DECIMAL128_HIGH_WORDS(&x), DECIMAL128_LOW_WORDS(&x)); + makeUInt128(&d, DECIMAL128_HIGH_WORDS(&x), DECIMAL128_LOW_WORDS(&x)); + makeUInt128(&b, DECIMAL128_HIGH_WORDS(&y), DECIMAL128_LOW_WORDS(&y)); + // TODO wjm refine the interface, so that here do not need to copy a + uInt128Divide(&a, &b); + uInt128Mod(&d, &b); + makeDecimal128(pLeftDec, uInt128Hi(&a), uInt128Lo(&a)); + makeDecimal128(pRemainderDec, uInt128Hi(&d), uInt128Lo(&d)); + if (negate) decimal128Negate(pLeftDec->words); + if (DECIMAL128_SIGN(pLeftDec) == -1) decimal128Negate(pRemainderDec->words); +} + +static void decimal128Mod(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { + +} static bool decimal128Gt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { - Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = (Decimal128*)pRight; Decimal128 right = {0}; DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); @@ -389,7 +415,7 @@ static bool decimal128Gt(const DecimalWord* pLeft, const DecimalWord* pRight, ui } static bool decimal128Eq(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { - Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = (Decimal128*)pRight; Decimal128 right = {0}; DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); @@ -450,7 +476,7 @@ static int32_t decimal128ToStr(const DecimalWord* pInt, uint8_t scale, char* pBu return len; } -int32_t decimalToStr(DecimalWord* pDec, int8_t dataType, int8_t precision, int8_t scale, char* pBuf, int32_t bufLen) { +int32_t decimalToStr(const DecimalWord* pDec, int8_t dataType, int8_t precision, int8_t scale, char* pBuf, int32_t bufLen) { pBuf[0] = '\0'; DecimalInternalType iType = DECIMAL_GET_INTERNAL_TYPE(dataType); switch (iType) { diff --git a/source/libs/decimal/test/decimalTest.cpp b/source/libs/decimal/test/decimalTest.cpp index c29eeb0220..f36bd7e2fe 100644 --- a/source/libs/decimal/test/decimalTest.cpp +++ b/source/libs/decimal/test/decimalTest.cpp @@ -38,6 +38,12 @@ void extractWideInteger(__int128 a) { printArray<38 / DIGIT_NUM>(segments); } +void printDecimal128(const Decimal128* pDec, int8_t precision, int8_t scale) { + char buf[64] = {0}; + decimalToStr(pDec->words, TSDB_DATA_TYPE_DECIMAL, precision, scale, buf, 64); + cout << buf; +} + __int128 generate_big_int128(uint32_t digitNum) { __int128 a = 0; for (int i = 0; i < digitNum + 1; ++i) { @@ -68,6 +74,35 @@ TEST(decimal128, to_string) { ASSERT_STREQ(buf, "1234567890123456789012345678.901234567"); } +TEST(decimal128, divide) { + __int128 i = generate_big_int128(15); + int64_t hi = i >> 64; + uint64_t lo = i; + Decimal128 d; + makeDecimal128(&d, hi, lo); + + Decimal128 d2 = {0}; + makeDecimal128(&d2, 0, 12345678); + + auto ops = getDecimalOps(TSDB_DATA_TYPE_DECIMAL); + Decimal128 remainder = {0}; + int8_t precision1 = 38, scale1 = 5, precision2 = 10, scale2 = 2; + 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); + __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); + cout << " = "; + ops->divide(d.words, d2.words, 2, remainder.words); + printDecimal128(&d, out_precision, out_scale); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();