From 1ef210980fd629031147af4ec0547773be515907 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 Date: Thu, 9 Jan 2025 15:29:17 +0800 Subject: [PATCH] decimal simple conversion --- include/libs/decimal/decimal.h | 14 +- source/libs/decimal/inc/wideInteger.h | 5 +- source/libs/decimal/src/decimal.c | 306 ++++++++++++++++++----- source/libs/decimal/src/wideInteger.c | 5 +- source/libs/decimal/test/decimalTest.cpp | 127 ++++++++-- 5 files changed, 369 insertions(+), 88 deletions(-) diff --git a/include/libs/decimal/decimal.h b/include/libs/decimal/decimal.h index 101fbd1674..20a1ce3939 100644 --- a/include/libs/decimal/decimal.h +++ b/include/libs/decimal/decimal.h @@ -27,15 +27,17 @@ typedef struct SValue SValue; typedef void DecimalType; typedef struct Decimal64 { - DecimalWord words[1]; // do not touch it directly, use DECIMAL64_GET_VALUE MACRO + DecimalWord words[1]; // do not touch it directly, use DECIMAL64_GET_VALUE MACRO } Decimal64; -#define DECIMAL64_GET_VALUE(pDec) (int64_t)((pDec)->words[0]) +#define DECIMAL64_GET_VALUE(pDec) (int64_t)((pDec)->words[0]) #define DECIMAL64_SET_VALUE(pDec, val) (*(int64_t*)((pDec)->words)) = (int64_t)(val) -#define DECIMAL64_CLONE(pDst, pFrom) ((Decimal64*)(pDst))->words[0] = ((Decimal64*)(pFrom))->words[0] +#define DECIMAL64_CLONE(pDst, pFrom) ((Decimal64*)(pDst))->words[0] = ((Decimal64*)(pFrom))->words[0] +#define DECIMAL64_MAX 999999999999999999LL +#define DECIMAL64_MIN -999999999999999999LL typedef struct Decimal128 { - DecimalWord words[2]; // do not touch it directly, use DECIMAL128_HIGH_WORD/DECIMAL128_LOW_WORD + DecimalWord words[2]; // do not touch it directly, use DECIMAL128_HIGH_WORD/DECIMAL128_LOW_WORD } Decimal128; #define Decimal Decimal128 @@ -63,7 +65,7 @@ int32_t decimalToStr(const DecimalType* pDec, int8_t type, int8_t precision, int int32_t decimalGetRetType(const SDataType* pLeftT, const SDataType* pRightT, EOperatorType opType, SDataType* pOutType); int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pRightT, const SDataType* pOutT, const void* pLeftData, const void* pRightData, void* pOutputData); -int32_t convertToDecimal(const void* pData, const SDataType* pInputType, void* pOut, const SDataType* poutType); +int32_t convertToDecimal(const void* pData, const SDataType* pInputType, void* pOut, const SDataType* pOutType); // TODO wjm change rightWordNum to DecimalType?? typedef struct SDecimalOps { @@ -82,6 +84,8 @@ typedef struct SDecimalOps { SDecimalOps* getDecimalOps(int8_t dataType); +__int128 decimal128ToInt128(const Decimal128* pDec); + #ifdef __cplusplus } #endif diff --git a/source/libs/decimal/inc/wideInteger.h b/source/libs/decimal/inc/wideInteger.h index eea2933d20..89f4377b3d 100644 --- a/source/libs/decimal/inc/wideInteger.h +++ b/source/libs/decimal/inc/wideInteger.h @@ -54,8 +54,9 @@ bool uInt128Lt(const UInt128* pLeft, const UInt128* pRight); bool uInt128Gt(const UInt128* pLeft, const UInt128* pRight); bool uInt128Eq(const UInt128* pLeft, const UInt128* pRight); -extern UInt128 uInt128_1e18; -extern UInt128 uInt128Zero; +extern const UInt128 uInt128_1e18; +extern const UInt128 uInt128Zero; +extern const uint64_t k1e18; #ifdef __cplusplus } diff --git a/source/libs/decimal/src/decimal.c b/source/libs/decimal/src/decimal.c index a5e786b1b8..2e5af032a5 100644 --- a/source/libs/decimal/src/decimal.c +++ b/source/libs/decimal/src/decimal.c @@ -274,6 +274,8 @@ static bool decimal64Lt(const DecimalType* pLeft, const DecimalType* pRight, static bool decimal64Gt(const DecimalType* pLeft, const DecimalType* pRight, uint8_t rightWordNum); static bool decimal64Eq(const DecimalType* pLeft, const DecimalType* pRight, uint8_t rightWordNum); 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); static void decimal128Negate(DecimalType* pInt); static void decimal128Abs(DecimalType* pWord); @@ -287,7 +289,9 @@ static bool decimal128Lt(const DecimalType* pLeft, const DecimalType* pRight, static bool decimal128Gt(const DecimalType* pLeft, const DecimalType* pRight, uint8_t rightWordNum); static bool decimal128Eq(const DecimalType* pLeft, const DecimalType* pRight, uint8_t rightWordNum); static int32_t decimal128ToStr(const DecimalType* pInt, uint8_t scale, char* pBuf, int32_t bufLen); -static void decimal64ScaleTo(Decimal64* pDec, uint8_t oldScale, uint8_t newScale); +void decimal128ScaleTo(Decimal128* pDec, uint8_t oldScale, uint8_t newScale); +void decimal128ScaleDown(Decimal128* pDec, uint8_t scaleDown); +void decimal128ScaleUp(Decimal128* pDec, uint8_t scaleUp); SDecimalOps decimal64Ops = {decimal64Negate, decimal64Abs, decimal64Add, decimal64Subtract, decimal64Multiply, decimal64divide, decimal64Mod, decimal64Lt, @@ -376,10 +380,26 @@ bool decimal64Eq(const DecimalType* pLeft, const DecimalType* pRight, uint8_t ri return DECIMAL64_GET_VALUE((Decimal64*)pLeft) == DECIMAL64_GET_VALUE(((Decimal64*)pRight)); } int32_t decimal64ToStr(const DecimalType* pInt, uint8_t scale, char* pBuf, int32_t bufLen) { - char format[16] = "\%0"; - snprintf(format + 2, 14, "%" PRIu8 PRIu64, scale); - return snprintf(pBuf, bufLen, DECIMAL64_GET_VALUE((Decimal64*)pInt) != 0 ? format : "%" PRIu64, - DECIMAL64_GET_VALUE((Decimal64*)pInt)); + Decimal whole = {0}, frac = {0}; + DecimalWord zero = 0; // TODO wjm remove zero, use SIGN + int32_t pos = 0; + + if (DECIMAL64_SIGN((Decimal64*)pInt) == -1) { + pos = sprintf(pBuf, "-"); + } + int32_t code = decimalGetWhole(pInt, DECIMAL_64, scale, &whole); + pos += snprintf(pBuf + pos, bufLen - pos, "%" PRId64, DECIMAL64_GET_VALUE(&whole)); + if (scale > 0) { + decimalGetFrac(pInt, DECIMAL_64, scale, &frac); + if (DECIMAL64_GET_VALUE(&frac) != 0 || DECIMAL64_GET_VALUE(&whole) != 0) { + TAOS_STRCAT(pBuf + pos, "."); + pos += 1; + char format[16] = "\%0"; + snprintf(format + 2, 14, "%" PRIu8 PRIu64, scale); + snprintf(pBuf + pos, bufLen - pos, format, DECIMAL64_GET_VALUE(&frac)); + } + } + return 0; } // TODO wjm handle endian problem @@ -450,7 +470,7 @@ void makeDecimal128(Decimal128* pDec128, int64_t hi, uint64_t low) { } static void makeDecimal128FromDecimal64(Decimal128* pTarget, Decimal64 decimal64) { - bool negative = false; + bool negative = false; if (DECIMAL64_SIGN(&decimal64) == -1) { decimal64Negate(&decimal64); negative = true; @@ -577,13 +597,12 @@ static bool decimal128Eq(const DecimalType* pLeft, const DecimalType* pRight, ui static void extractDecimal128Digits(const Decimal128* pDec, uint64_t* digits, int32_t* digitNum) { #define DIGIT_NUM_ONCE 18 - const uint64_t k1e18 = 100000000000000000; - UInt128 a = {0}; - UInt128 b = {0}; + UInt128 a = {0}; + UInt128 b = {0}; *digitNum = 0; makeUInt128(&a, DECIMAL128_HIGH_WORD(pDec), DECIMAL128_LOW_WORD(pDec)); while (!uInt128Eq(&a, &uInt128Zero)) { - uint64_t hi = a >> 64; + uint64_t hi = a >> 64; // TODO wjm ??? uint64_t lo = a; uint64_t hiQuotient = hi / k1e18; @@ -602,7 +621,7 @@ static void extractDecimal128Digits(const Decimal128* pDec, uint64_t* digits, in // TODO wjm checkBuflen static int32_t decimal128ToStr(const DecimalType* pInt, uint8_t scale, char* pBuf, int32_t bufLen) { const Decimal128* pDec = (const Decimal128*)pInt; - bool negative = DECIMAL128_SIGN(pDec) < 0; + bool negative = DECIMAL128_SIGN(pDec) == -1; uint64_t segments[3] = {0}; int32_t digitNum = 0; char buf[64] = {0}; @@ -617,6 +636,10 @@ static int32_t decimal128ToStr(const DecimalType* pInt, uint8_t scale, char* pBu } else { extractDecimal128Digits(pDec, segments, &digitNum); } + if (digitNum == 0) { + TAOS_STRNCAT(pBuf, "0", 2); + 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]); @@ -637,25 +660,8 @@ int32_t decimalToStr(const DecimalType* pDec, int8_t dataType, int8_t precision, pBuf[0] = '\0'; DecimalInternalType iType = DECIMAL_GET_INTERNAL_TYPE(dataType); switch (iType) { - case DECIMAL_64: { - int32_t wordNum = DECIMAL_GET_WORD_NUM(iType); - SDecimalOps* pOps = getDecimalOpsImp(iType); - Decimal whole = {0}, frac = {0}; - DecimalWord zero = 0;// TODO wjm remove zero, use SIGN - int32_t pos = 0; - - if (pOps->lt(pDec, &zero, 1)) { - pos = sprintf(pBuf, "-"); - } - int32_t code = decimalGetWhole(pDec, iType, scale, &whole); - if (!pOps->eq(&whole, &zero, 1)) { - pos += pOps->toStr(&whole, scale, pBuf + pos, bufLen - pos); - } - code = decimalGetFrac(pDec, iType, scale, &frac); - if (DECIMAL64_GET_VALUE(&frac) != 0) pos += snprintf(pBuf + pos, bufLen - pos, "."); - pOps->toStr(&frac, scale, pBuf + pos, bufLen - pos); - return 0; - } + case DECIMAL_64: + return decimal64ToStr(pDec, scale, pBuf, bufLen); case DECIMAL_128: return decimal128ToStr(pDec, scale, pBuf, bufLen); default: @@ -701,7 +707,7 @@ int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pR int32_t code = 0; \ Decimal64 dv = {ABS(v)}; \ if (DECIMAL64_IS_OVERFLOW(dv, max)) { \ - code = TSDB_CODE_INTERNAL_ERROR; \ + code = TSDB_CODE_DECIMAL_OVERFLOW; \ } else { \ makeDecimal64(pDec, (int64_t)(v)); \ } \ @@ -711,13 +717,96 @@ int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pR #define MAKE_DECIMAL64_SIGNED(pDec, v, max) CHECK_OVERFLOW_AND_MAKE_DECIMAL64(pDec, v, max, ABS_INT64) #define MAKE_DECIMAL64_UNSIGNED(pDec, v, max) CHECK_OVERFLOW_AND_MAKE_DECIMAL64(pDec, v, max, ABS_UINT64); +static int32_t decimal64FromInt64(DecimalType* pDec, uint8_t prec, uint8_t scale, int64_t val) { + 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; + } + DECIMAL64_SET_VALUE((Decimal64*)pDec, val); + decimal64ScaleUp(pDec, scale); + return 0; +} + +static int32_t decimal64FromUint64(DecimalType* pDec, uint8_t prec, uint8_t scale, uint64_t val) { + Decimal64 max = {0}; + DECIMAL64_GET_MAX(prec - scale, &max); + if ((uint64_t)DECIMAL64_GET_VALUE(&max) < val) return TSDB_CODE_DECIMAL_OVERFLOW; + DECIMAL64_SET_VALUE((Decimal64*)pDec, val); + decimal64ScaleUp(pDec, scale); + return 0; +} + +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, + uint8_t valPrec, uint8_t valScale) { + return 0; +} + +static int32_t decimal64FromDecimal64(DecimalType* pDec, uint8_t pec, uint8_t scale, const DecimalType* pVal, + uint8_t valPrec, uint8_t valScale) { + return 0; +} + +static int32_t decimal128FromInt64(DecimalType* pDec, uint8_t prec, uint8_t scale, int64_t val) { + if (prec - scale <= 18) { + 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; + } + uint64_t valAbs = ABS_INT64(val); + makeDecimal128(pDec, 0, valAbs); + if (val < 0) decimal128Negate(pDec); + decimal128ScaleUp(pDec, scale); + return 0; +} + +static int32_t decimal128FromUint64(DecimalType* pDec, uint8_t prec, uint8_t scale, uint64_t val) { + if (prec - scale <= 19) { + Decimal128 max = {0}, decVal = {0}; + DECIMAL128_GET_MAX(prec - scale, &max); + makeDecimal128(&decVal, 0, val); + if (decimal128Gt(&decVal, &max, DECIMAL_WORD_NUM(Decimal128))) { + return TSDB_CODE_DECIMAL_OVERFLOW; + } + } + makeDecimal128(pDec, 0, val); + decimal128ScaleUp(pDec, scale); + return 0; +} + +static int32_t decimal128FromDouble(DecimalType* pDec, uint8_t prec, uint8_t scale, double val) { return 0; } + +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; + } + Decimal128 max = {0}; + DECIMAL128_GET_MAX(prec - scale, &max); + 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); + return 0; +} +static int32_t decimal128FromDecimal128(DecimalType* pDec, uint8_t prec, uint8_t scale, const DecimalType* pVal, + uint8_t valPrec, uint8_t valScale) { + return 0; +} #define CHECK_OVERFLOW_AND_MAKE_DECIMAL128(pDec, v, max, ABS) \ ({ \ int32_t code = 0; \ Decimal128 dv = {0}; \ makeDecimal128(&dv, 0, ABS(v)); \ if (DECIMAL128_IS_OVERFLOW(dv, max)) { \ - code = TSDB_CODE_INTERNAL_ERROR; \ + code = TSDB_CODE_DECIMAL_OVERFLOW; \ } else { \ makeDecimal128(pDec, v < 0 ? -1 : 0, ABS(v)); \ } \ @@ -791,71 +880,168 @@ int32_t decimalOp(EOperatorType op, const SDataType* pLeftT, const SDataType* pR } \ } while (0) +#define CONVERT_TO_DECIMAL2(pData, pInputType, pOut, pOutType, decimal) \ + ({ \ + int32_t code = 0; \ + int64_t val = 0; \ + uint64_t uval = 0; \ + double dval = 0; \ + switch (pInputType->type) { \ + case TSDB_DATA_TYPE_NULL: \ + break; \ + case TSDB_DATA_TYPE_BOOL: \ + uval = *(const bool*)pData; \ + code = decimal##FromUint64(pOut, pOutType->precision, pOutType->scale, uval); \ + break; \ + case TSDB_DATA_TYPE_TINYINT: \ + val = *(const int8_t*)pData; \ + code = decimal##FromInt64(pOut, pOutType->precision, pOutType->scale, val); \ + break; \ + case TSDB_DATA_TYPE_SMALLINT: \ + val = *(const int16_t*)pData; \ + code = decimal##FromInt64(pOut, pOutType->precision, pOutType->scale, val); \ + break; \ + case TSDB_DATA_TYPE_INT: \ + val = *(const int32_t*)pData; \ + code = decimal##FromInt64(pOut, pOutType->precision, pOutType->scale, val); \ + break; \ + case TSDB_DATA_TYPE_TIMESTAMP: \ + case TSDB_DATA_TYPE_BIGINT: \ + val = *(const int64_t*)pData; \ + code = decimal##FromInt64(pOut, pOutType->precision, pOutType->scale, val); \ + break; \ + case TSDB_DATA_TYPE_UTINYINT: \ + uval = *(const uint8_t*)pData; \ + code = decimal##FromUint64(pOut, pOutType->precision, pOutType->scale, uval); \ + break; \ + case TSDB_DATA_TYPE_USMALLINT: \ + uval = *(const uint16_t*)pData; \ + code = decimal##FromUint64(pOut, pOutType->precision, pOutType->scale, uval); \ + break; \ + case TSDB_DATA_TYPE_UINT: \ + uval = *(const uint32_t*)pData; \ + code = decimal##FromUint64(pOut, pOutType->precision, pOutType->scale, uval); \ + break; \ + case TSDB_DATA_TYPE_UBIGINT: \ + uval = *(const uint64_t*)pData; \ + code = decimal##FromUint64(pOut, pOutType->precision, pOutType->scale, uval); \ + break; \ + case TSDB_DATA_TYPE_FLOAT: { \ + dval = *(const float*)pData; \ + code = decimal##FromDouble(pOut, pOutType->precision, pOutType->scale, dval); \ + } break; \ + case TSDB_DATA_TYPE_DOUBLE: { \ + dval = *(const double*)pData; \ + code = decimal##FromDouble(pOut, pOutType->precision, pOutType->scale, dval); \ + } break; \ + case TSDB_DATA_TYPE_VARCHAR: \ + case TSDB_DATA_TYPE_VARBINARY: \ + case TSDB_DATA_TYPE_NCHAR: { \ + /*decimal##FromStr()*/ \ + } break; \ + case TSDB_DATA_TYPE_DECIMAL64: { \ + code = decimal##FromDecimal64(pOut, pOutType->precision, pOutType->scale, pData, pInputType->precision, \ + pInputType->scale); \ + } break; \ + case TSDB_DATA_TYPE_DECIMAL: { \ + code = decimal##FromDecimal128(pOut, pOutType->precision, pOutType->scale, pData, pInputType->precision, \ + pInputType->scale); \ + } break; \ + default: \ + code = TSDB_CODE_OPS_NOT_SUPPORT; \ + break; \ + } \ + code; \ + }) + int32_t convertToDecimal(const void* pData, const SDataType* pInputType, void* pOut, const SDataType* pOutType) { if (pInputType->type == pOutType->type) return 0; int32_t code = 0; switch (pOutType->type) { case TSDB_DATA_TYPE_DECIMAL64: { - Decimal64 max = {0}; - DECIMAL64_GET_MAX(pOutType->precision - pOutType->scale, &max); - CONVERT_TO_DECIMAL(pInputType->type, pData, pOut, MAKE_DECIMAL64, max, code); + code = CONVERT_TO_DECIMAL2(pData, pInputType, pOut, pOutType, decimal64); } break; case TSDB_DATA_TYPE_DECIMAL: { - Decimal128 max = {0}; - DECIMAL128_GET_MAX(pOutType->precision - pOutType->scale, &max); - CONVERT_TO_DECIMAL(pInputType->type, pData, pOut, MAKE_DECIMAL128, max, code); + code = CONVERT_TO_DECIMAL2(pData, pInputType, pOut, pOutType, decimal128); } break; default: code = TSDB_CODE_INTERNAL_ERROR; break; } - if (TSDB_CODE_SUCCESS == code) { - // scale to output scale - } return code; } +void decimal64ScaleDown(Decimal64* pDec, uint8_t scaleDown) { + Decimal64 divisor = SCALE_MULTIPLIER_64[scaleDown]; + decimal64divide(pDec, &divisor, WORD_NUM(Decimal64), NULL); +} + +void decimal64ScaleUp(Decimal64* pDec, uint8_t scaleUp) { + Decimal64 multiplier = SCALE_MULTIPLIER_64[scaleUp]; + decimal64Multiply(pDec, &multiplier, WORD_NUM(Decimal64)); +} + void decimal64ScaleTo(Decimal64* pDec, uint8_t oldScale, uint8_t newScale) { - if (newScale < oldScale) { - Decimal64 divisor = SCALE_MULTIPLIER_64[oldScale - newScale]; - decimal64divide(pDec, &divisor, 1, NULL); - } + if (newScale > oldScale) + decimal64ScaleUp(pDec, newScale - oldScale); + else if (newScale < oldScale) + decimal64ScaleDown(pDec, oldScale - newScale); } -int32_t decimal64FromStr(const char* str, int32_t len, uint8_t expectPrecision, uint8_t expectScale, - Decimal64* result) { +int32_t decimal64FromStr(const char* str, int32_t len, uint8_t expectPrecision, uint8_t expectScale, Decimal64* pRes) { int32_t code = 0; - DecimalVar var = {.type = DECIMAL_64, .pDec = result->words}; + DecimalVar var = {.type = DECIMAL_64, .pDec = pRes->words}; + DECIMAL64_SET_VALUE(pRes, 0); code = decimalVarFromStr(str, len, &var); if (TSDB_CODE_SUCCESS != code) return code; Decimal64 max = {0}; - DECIMAL64_GET_MAX(expectPrecision - expectScale, &max); - if (var.scale > expectScale) decimal64ScaleTo(result, var.scale, expectScale); - if (decimal64Gt(result, &max, 1)) { + DECIMAL64_GET_MAX(expectPrecision, &max); + decimal64ScaleTo(pRes, var.scale, expectScale); + if (decimal64Gt(pRes, &max, 1)) { return TSDB_CODE_DECIMAL_OVERFLOW; } return code; } +void decimal128ScaleDown(Decimal128* pDec, uint8_t scaleDown) { + Decimal128 divisor = SCALE_MULTIPLIER_128[scaleDown]; + decimal128Divide(pDec, &divisor, 2, NULL); +} + +void decimal128ScaleUp(Decimal128* pDec, uint8_t scaleUp) { + Decimal128 multiplier = SCALE_MULTIPLIER_128[scaleUp]; + decimal128Multiply(pDec, &multiplier, WORD_NUM(Decimal128)); +} + void decimal128ScaleTo(Decimal128* pDec, uint8_t oldScale, uint8_t newScale) { - if (newScale < oldScale) { - Decimal128 divisor = SCALE_MULTIPLIER_128[oldScale - newScale]; - decimal128Divide(pDec, &divisor, 2, NULL); - } + if (newScale > oldScale) + decimal128ScaleUp(pDec, newScale - oldScale); + else if (newScale < oldScale) + decimal128ScaleDown(pDec, oldScale - newScale); } int32_t decimal128FromStr(const char* str, int32_t len, uint8_t expectPrecision, uint8_t expectScale, - Decimal128* result) { + Decimal128* pRes) { int32_t code = 0; - DecimalVar var = {.type = DECIMAL_128, .pDec = result->words}; + DecimalVar var = {.type = DECIMAL_128, .pDec = pRes->words}; + DECIMAL128_SET_HIGH_WORD(pRes, 0); + DECIMAL128_SET_LOW_WORD(pRes, 0); code = decimalVarFromStr(str, len, &var); if (TSDB_CODE_SUCCESS != code) return code; Decimal128 max = {0}; - DECIMAL128_GET_MAX(expectPrecision - expectScale, &max); - if (var.scale > expectScale) decimal128ScaleTo(result, var.scale, expectScale); - if (decimal128Gt(result, &max, 2)) { + DECIMAL128_GET_MAX(expectPrecision, &max); + decimal128ScaleTo(pRes, var.scale, expectScale); + if (decimal128Gt(pRes, &max, 2)) { return TSDB_CODE_DECIMAL_OVERFLOW; } return code; } + +__int128 decimal128ToInt128(const Decimal128* pDec) { + __int128 ret = 0; + ret = DECIMAL128_HIGH_WORD(pDec); + ret <<= 64; + ret |= DECIMAL128_LOW_WORD(pDec); + return ret; +} diff --git a/source/libs/decimal/src/wideInteger.c b/source/libs/decimal/src/wideInteger.c index c514cab7d7..f3f2f7eff8 100644 --- a/source/libs/decimal/src/wideInteger.c +++ b/source/libs/decimal/src/wideInteger.c @@ -16,8 +16,9 @@ bool uInt128Lt(const UInt128* pLeft, const UInt128* pRight); bool uInt128Gt(const UInt128* pLeft, const UInt128* pRight); bool uInt128Eq(const UInt128* pLeft, const UInt128* pRight) { return *pLeft == *pRight; } -UInt128 uInt128Zero = 0; -UInt128 uInt128_1e18 = 1000000000000000000; +const UInt128 uInt128Zero = 0; +const uint64_t k1e18 = 1000000000000000000LL; +const UInt128 uInt128_1e18 = k1e18; #else void uInt128Multiply(UInt128* pLeft, const UInt128* pRight) {} diff --git a/source/libs/decimal/test/decimalTest.cpp b/source/libs/decimal/test/decimalTest.cpp index 4167d5329b..f3e3ef67d0 100644 --- a/source/libs/decimal/test/decimalTest.cpp +++ b/source/libs/decimal/test/decimalTest.cpp @@ -1,12 +1,14 @@ #include #include + #include "decimal.h" +#include "libs/nodes/querynodes.h" #include "tcommon.h" #include "wideInteger.h" using namespace std; template -void printArray(const std::array &arr) { +void printArray(const std::array& arr) { auto it = arr.rbegin(); for (; it != arr.rend(); ++it) { cout << *it; @@ -61,9 +63,9 @@ TEST(decimal, a) { } TEST(decimal128, to_string) { - __int128 i = generate_big_int128(37); - int64_t hi = i >> 64; - uint64_t lo = i; + __int128 i = generate_big_int128(37); + int64_t hi = i >> 64; + uint64_t lo = i; Decimal128 d; makeDecimal128(&d, hi, lo); char buf[64] = {0}; @@ -76,21 +78,21 @@ TEST(decimal128, to_string) { } TEST(decimal128, divide) { - __int128 i = generate_big_int128(15); - int64_t hi = i >> 64; - uint64_t lo = i; + __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); + 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; + 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; @@ -105,6 +107,7 @@ TEST(decimal128, divide) { } TEST(decimal, cpi_taos_fetch_rows) { + GTEST_SKIP(); const char* host = "127.0.0.1"; const char* user = "root"; const char* passwd = "taosdata"; @@ -117,16 +120,16 @@ TEST(decimal, cpi_taos_fetch_rows) { FAIL(); } - auto *res = taos_query(pTaos, sql); + auto* res = taos_query(pTaos, sql); int32_t code = taos_errno(res); if (code != 0) { cout << "taos_query with sql: " << sql << " failed: " << taos_errstr(res); FAIL(); } - char buf[1024] = {0}; - auto *fields = taos_fetch_fields(res); - auto fieldNum = taos_field_count(res); + char buf[1024] = {0}; + auto* fields = taos_fetch_fields(res); + auto fieldNum = taos_field_count(res); while (auto row = taos_fetch_row(res)) { taos_print_row(buf, row, fields, fieldNum); cout << buf << endl; @@ -140,7 +143,7 @@ TEST(decimal, cpi_taos_fetch_rows) { FAIL(); } - void* pData = NULL; + void* pData = NULL; int32_t numOfRows = 0; code = taos_fetch_raw_block(res, &numOfRows, &pData); if (code != 0) { @@ -151,12 +154,98 @@ TEST(decimal, cpi_taos_fetch_rows) { SSDataBlock* pBlock; taos_free_result(res); - taos_close(pTaos); taos_cleanup(); } -int main(int argc, char **argv) { +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 + char buf[64] = {0}; + int8_t i8 = 22; + SDataType inputType = {.type = TSDB_DATA_TYPE_TINYINT, .bytes = 1}; + uint8_t prec = 10, scale = 2; + SDataType decType = {.type = TSDB_DATA_TYPE_DECIMAL64, .precision = prec, .scale = scale, .bytes = 8}; + 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"); + + Decimal128 dec128 = {0}; + decType.type = TSDB_DATA_TYPE_DECIMAL; + decType.precision = 38; + decType.scale = 10; + 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); + + 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); + + 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); +} + +static constexpr uint64_t k1E16 = 10000000000000000LL; + +TEST(decimal, decimalFromStr) { + char inputBuf[64] = "123.000000000000000000000000000000001"; + Decimal128 dec128 = {0}; + int32_t code = decimal128FromStr(inputBuf, strlen(inputBuf), 38, 35, &dec128); + ASSERT_EQ(code, 0); + __int128 res = decimal128ToInt128(&dec128); + __int128 resExpect = 123; + resExpect *= k1E16; + resExpect *= k1E16; + resExpect *= 10; + resExpect += 1; + resExpect *= 100; + ASSERT_EQ(res, resExpect); +} + +TEST(decimal, toStr) { + Decimal64 dec = {0}; + char buf[64] = {0}; + int32_t code = decimalToStr(&dec, TSDB_DATA_TYPE_DECIMAL64, 10, 2, buf, 64); + ASSERT_EQ(code, 0); + ASSERT_STREQ(buf, "0"); + + Decimal128 dec128 = {0}; + code = decimalToStr(&dec128, TSDB_DATA_TYPE_DECIMAL, 38, 10, buf, 64); + ASSERT_EQ(code, 0); + ASSERT_STREQ(buf, "0"); +} + +int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }