From cdba5bd9c0eb58437ba70419a8335154d249348e Mon Sep 17 00:00:00 2001 From: wangjiaming0909 Date: Fri, 27 Dec 2024 18:20:42 +0800 Subject: [PATCH] define wide integer, support decimal128 --- include/common/tdataformat.h | 2 +- include/libs/decimal/decimal.h | 51 ++- include/util/tdef.h | 3 +- source/client/src/clientImpl.c | 3 +- source/common/src/tdataformat.c | 6 +- source/libs/decimal/CMakeLists.txt | 1 + source/libs/decimal/inc/wideInteger.h | 64 ++++ source/libs/decimal/src/decimal.c | 386 ++++++++++++++++------- source/libs/decimal/src/wideInteger.c | 25 ++ source/libs/decimal/test/CMakeLists.txt | 3 +- source/libs/decimal/test/decimalTest.cpp | 72 ++++- tests/system-test/2-query/decimal.py | 8 +- 12 files changed, 471 insertions(+), 153 deletions(-) create mode 100644 source/libs/decimal/inc/wideInteger.h create mode 100644 source/libs/decimal/src/wideInteger.c diff --git a/include/common/tdataformat.h b/include/common/tdataformat.h index cd55cb8a75..4cc4e881ce 100644 --- a/include/common/tdataformat.h +++ b/include/common/tdataformat.h @@ -264,7 +264,7 @@ struct SValue { #define VALUE_GET_TRIVIAL_DATUM(pVal) ((pVal)->val) #define VALUE_SET_TRIVIAL_DATUM(pVal, v) (pVal)->val = v -void valueSetDatum(SValue *pVal, int8_t type, const void *pDatum, uint32_t len); +void valueSetDatum(SValue *pVal, int8_t type, void *pDatum, uint32_t len); void valueCloneDatum(SValue *pDst, const SValue *pSrc, int8_t type); void valueClearDatum(SValue *pVal, int8_t type); diff --git a/include/libs/decimal/decimal.h b/include/libs/decimal/decimal.h index 970c4ea66a..8ea3c8984c 100644 --- a/include/libs/decimal/decimal.h +++ b/include/libs/decimal/decimal.h @@ -20,10 +20,10 @@ extern "C" { #endif -#include "ttypes.h" #include "tdef.h" +#include "ttypes.h" typedef struct SDataType SDataType; -typedef struct SValue SValue; +typedef struct SValue SValue; typedef struct Decimal64 { DecimalWord words[1]; @@ -33,6 +33,9 @@ typedef struct Decimal128 { DecimalWord words[2]; } Decimal128; +void makeDecimal64(Decimal64* pDec64, DecimalWord w); +void makeDecimal128(Decimal128* pDec128, int64_t hi, uint64_t low); + #define DECIMAL_WORD_NUM(TYPE) sizeof(TYPE) / sizeof(DecimalWord) int32_t decimalCalcTypeMod(const SDataType* pType); @@ -42,37 +45,25 @@ int32_t decimal64FromStr(const char* str, int32_t len, uint8_t* precision, uint8 int32_t decimal128FromStr(const char* str, int32_t len, uint8_t* precision, uint8_t* scale, Decimal128* result); int32_t decimal64ToDataVal(const Decimal64* dec, SValue* pVal); -int32_t decimal128ToDataVal(const Decimal128* dec, SValue* pVal); +int32_t decimal128ToDataVal(Decimal128* dec, SValue* pVal); -int32_t decimalToStr(DecimalWord* pDec, int8_t precision, int8_t scale, char* pBuf, int32_t bufLen); +int32_t decimalToStr(DecimalWord* pDec, int8_t type, int8_t precision, int8_t scale, char* pBuf, int32_t bufLen); -typedef struct DecimalVar DecimalVar; +typedef struct SDecimalOps { + void (*negate)(DecimalWord* pWord); + void (*abs)(DecimalWord* pWord); + 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 (*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); + bool (*eq)(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); + int32_t (*toStr)(const DecimalWord* pInt, uint8_t scale, char* pBuf, int32_t bufLen); +} SDecimalOps; -typedef struct SDecimalVarOps { - uint8_t wordNum; - int32_t (*add)(DecimalVar* pLeft, const DecimalVar* pRight); - int32_t (*multiply)(DecimalVar* pLeft, const DecimalVar* pRight); -} SDecimalVarOps; - -typedef struct SWideInteger { - int32_t wordNum; - DecimalWord *words; -} SWideInteger; - -// TODO wjm rename it -typedef struct SWideIntegerOps { - uint8_t wordNum; - int32_t (*abs)(DecimalWord* pInt); - int32_t (*add)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - int32_t (*subtract)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - int32_t (*multiply)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - int32_t (*divide)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - int32_t (*mod)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - bool (*lt)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - bool (*gt)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - bool (*eq)(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); - int32_t (*toStr)(DecimalWord* pInt, char* pBuf, int32_t bufLen); -} SWideIntegerOps; +SDecimalOps* getDecimalOps(int8_t dataType); #ifdef __cplusplus } diff --git a/include/util/tdef.h b/include/util/tdef.h index 1145285a24..b0f8ebd68a 100644 --- a/include/util/tdef.h +++ b/include/util/tdef.h @@ -693,7 +693,8 @@ typedef enum { #define TSDB_DECIMAL_MIN_SCALE 0 #define TSDB_DECIMAL_MAX_SCALE TSDB_DECIMAL_MAX_PRECISION -typedef int64_t DecimalWord; +typedef uint64_t DecimalWord; +#define WORD_NUM(TYPE) (sizeof(TYPE) / sizeof(DecimalWord)) #ifdef __cplusplus } diff --git a/source/client/src/clientImpl.c b/source/client/src/clientImpl.c index 43d2b5bc5f..d3390fcaa0 100644 --- a/source/client/src/clientImpl.c +++ b/source/client/src/clientImpl.c @@ -2155,7 +2155,8 @@ static int32_t convertDecimalType(SReqResultInfo* pResultInfo) { pResultInfo->convertBuf[i] = p; for (int32_t j = 0; j < pResultInfo->numOfRows; ++j) { - int32_t code = decimalToStr((DecimalWord*)(pResultInfo->pCol[i].pData + j * tDataTypes[type].bytes), pField->precision, pField->scale, p, bufLen); + int32_t code = decimalToStr((DecimalWord*)(pResultInfo->pCol[i].pData + j * tDataTypes[type].bytes), type, + pField->precision, pField->scale, p, bufLen); p += bufLen; if (TSDB_CODE_SUCCESS != code) { return code; diff --git a/source/common/src/tdataformat.c b/source/common/src/tdataformat.c index 9638e4670a..370895871e 100644 --- a/source/common/src/tdataformat.c +++ b/source/common/src/tdataformat.c @@ -4672,10 +4672,10 @@ int32_t tDecompressDataToBuffer(void *input, SCompressInfo *info, SBuffer *outpu } // handle all types, including var data -void valueSetDatum(SValue *pVal, int8_t type, const void *pDatum, uint32_t len) { +void valueSetDatum(SValue *pVal, int8_t type, void *pDatum, uint32_t len) { assert(type == pVal->type); if (IS_VAR_DATA_TYPE(type) || type == TSDB_DATA_TYPE_DECIMAL) { - memcpy(pVal->pData, pDatum, len); + pVal->pData = pDatum; pVal->nData = len; } else { switch (len) { @@ -4714,4 +4714,4 @@ void valueClearDatum(SValue *pVal, int8_t type) { } else { pVal->val = 0; } -} \ No newline at end of file +} diff --git a/source/libs/decimal/CMakeLists.txt b/source/libs/decimal/CMakeLists.txt index 6837ae8eb1..f82bedc1b1 100644 --- a/source/libs/decimal/CMakeLists.txt +++ b/source/libs/decimal/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(decimal STATIC ${DECIMAL_SRC}) target_include_directories( decimal PUBLIC "${TD_SOURCE_DIR}/include/libs/decimal" + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) target_link_libraries( decimal diff --git a/source/libs/decimal/inc/wideInteger.h b/source/libs/decimal/inc/wideInteger.h new file mode 100644 index 0000000000..eea2933d20 --- /dev/null +++ b/source/libs/decimal/inc/wideInteger.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _TD_WIDE_INTEGER_H_ +#define _TD_WIDE_INTEGER_H_ + +#include +#include "tdef.h" +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _UInt128 { + uint64_t low; + uint64_t high; +} _UInt128; + +// TODO wjm use cmake to check if the compiler supports __int128_t +#if defined(__GNUC__) || defined(__clang__) +// #if 0 +typedef __uint128_t UInt128; +#else +typedef _UInt128 UInt128; +#define Int128 UInt128 +#endif + +#define SAFE_SIGNED_OP(a, b, SIGNED_TYPE, UNSIGNED_TYPE, OP) (SIGNED_TYPE)((UNSIGNED_TYPE)(a)OP(UNSIGNED_TYPE)(b)) +#define SAFE_INT64_ADD(a, b) SAFE_SIGNED_OP(a, b, int64_t, uint64_t, +) +#define SAFE_INT64_SUBTRACT(a, b) SAFE_SIGNED_OP(a, b, int64_t, uint64_t, -) + +void makeUInt128(UInt128* pInt, DecimalWord hi, DecimalWord lo); +uint64_t uInt128Hi(const UInt128* pInt); +uint64_t uInt128Lo(const UInt128* pInt); + +void uInt128Abs(UInt128* pInt); +void uInt128Add(UInt128* pLeft, const UInt128* pRight); +void uInt128Subtract(UInt128* pLeft, const UInt128* pRight); +void uInt128Multiply(UInt128* pLeft, const UInt128* pRight); +void uInt128Divide(UInt128* pLeft, const UInt128* pRight); +void uInt128Mod(UInt128* pLeft, const UInt128* pRight); +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; + +#ifdef __cplusplus +} +#endif + +#endif /* _TD_WIDE_INTEGER_H_ */ diff --git a/source/libs/decimal/src/decimal.c b/source/libs/decimal/src/decimal.c index 2d8b5b4f01..21272985f1 100644 --- a/source/libs/decimal/src/decimal.c +++ b/source/libs/decimal/src/decimal.c @@ -16,46 +16,27 @@ #include "decimal.h" #include "querynodes.h" +#include "wideInteger.h" typedef enum DecimalInternalType { DECIMAL_64 = 0, DECIMAL_128 = 1, } DecimalInternalType; -#define DECIMAL_GET_INTERNAL_TYPE(precision) ((precision) > TSDB_DECIMAL64_MAX_SCALE ? DECIMAL_128 : DECIMAL_64) -#define DECIMAL_GET_WORD_NUM(decimalInternalType) ((decimalInternalType) == DECIMAL_64 ? DECIMAL_WORD_NUM(Decimal64) : DECIMAL_WORD_NUM(Decimal128)) +#define DECIMAL_GET_INTERNAL_TYPE(dataType) ((dataType) == TSDB_DATA_TYPE_DECIMAL ? DECIMAL_128 : DECIMAL_64) +#define DECIMAL_GET_WORD_NUM(decimalInternalType) \ + ((decimalInternalType) == DECIMAL_64 ? DECIMAL_WORD_NUM(Decimal64) : DECIMAL_WORD_NUM(Decimal128)) #define DecimalMax Decimal128 +static SDecimalOps* getDecimalOpsImp(DecimalInternalType t); -static int32_t int64Abs(DecimalWord* pInt); -static int32_t int64Add(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static int32_t int64Subtract(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static int32_t int64Multiply(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static int32_t int64divide(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static int32_t int64Mod(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static bool int64Lt(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static bool int64Gt(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static bool int64Eq(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum); -static int32_t int64ToStr(DecimalWord* pInt, char* pBuf, int32_t bufLen); - -typedef __int128_t Int128; - -SWideIntegerOps wideIntegerOps [2] = { - {DECIMAL_WORD_NUM(Decimal64), int64Abs, int64Add, int64Subtract, int64Multiply, int64divide, int64Mod, int64Lt, int64Gt, int64Eq, int64ToStr}, - {DECIMAL_WORD_NUM(Decimal128), 0, 0, 0}}; - -SDecimalVarOps decimalVarOps[2] = { - {DECIMAL_WORD_NUM(Decimal64), 0, 0}, - {DECIMAL_WORD_NUM(Decimal128), 0, 0} -}; - -struct DecimalVar { +typedef struct DecimalVar { DecimalInternalType type; uint8_t precision; uint8_t scale; int32_t exponent; int8_t sign; DecimalWord* words; -}; +} DecimalVar; static uint8_t maxPrecision(DecimalInternalType type) { switch (type) { @@ -108,9 +89,7 @@ static int32_t decimalVarFromStr(const char* str, int32_t len, DecimalVar* resul result->sign = 1; if (len == 0) return TSDB_CODE_INVALID_DATA_FMT; - SWideIntegerOps ops = - wideIntegerOps[result->type]; // TODO wjm currently, the meaning from type to index is not clear - SDecimalVarOps decOps = decimalVarOps[result->type]; + SDecimalOps* pOps = getDecimalOpsImp(result->type); // sign switch (str[pos]) { @@ -158,8 +137,8 @@ static int32_t decimalVarFromStr(const char* str, int32_t len, DecimalVar* resul result->scale++; } DecimalWord ten = 10, digit = str[pos] - '0'; - ops.multiply(result->words, &ten, 1); - ops.add(result->words, &digit, 1); + pOps->multiply(result->words, &ten, 1); + pOps->add(result->words, &digit, 1); places = 0; break; } @@ -173,7 +152,7 @@ static int32_t decimalVarFromStr(const char* str, int32_t len, DecimalVar* resul } if (result->sign < 0) { DecimalWord sign = (DecimalWord)result->sign; - ops.multiply(result->words, &sign, 1); + pOps->multiply(result->words, &sign, 1); } return code; } @@ -183,68 +162,13 @@ int32_t decimal64ToDataVal(const Decimal64* dec, SValue* pVal) { return TSDB_CODE_SUCCESS; } -int32_t decimal128ToDataVal(const Decimal128* dec, SValue* pVal) { +int32_t decimal128ToDataVal(Decimal128* dec, SValue* pVal) { pVal->pData = taosMemCalloc(2, sizeof(DecimalWord)); if (!pVal->pData) return terrno; valueSetDatum(pVal, TSDB_DATA_TYPE_DECIMAL, dec->words, DECIMAL_WORD_NUM(Decimal128) * sizeof(DecimalWord)); return TSDB_CODE_SUCCESS; } -static int32_t int64Abs(DecimalWord* pInt) { - *pInt = TABS(*pInt); - return 0; -} - -static int32_t int64Add(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - int32_t code = 0; - Int128 sum = *pLeft; - sum += *pRight; - *pLeft = (DecimalWord)sum; - return code; -} - -static int32_t int64Subtract(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - int32_t code = 0; - Int128 res = *pLeft; - res -= *pRight; - *pLeft = (DecimalWord)res; - return code; -} - -static int32_t int64Multiply(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - Int128 res = *pLeft; - res *= *pRight; - *pLeft = (DecimalWord)res; - return 0; -} - -static int32_t int64divide(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - Int128 res = *pLeft; - res /= *pRight; - *pLeft = (DecimalWord)res; - return 0; -} - -static int32_t int64Mod(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - Int128 res = *pLeft; - res %= *pRight; - *pLeft = (DecimalWord)res; - return 0; -} - -static bool int64Lt(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - return *pLeft < *pRight; -} -static bool int64Gt(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - return *pLeft > *pRight; -} -static bool int64Eq(DecimalWord* pLeft, DecimalWord* pRight, uint8_t rightWordNum) { - return *pLeft == *pRight; -} -static int32_t int64ToStr(DecimalWord* pInt, char *pBuf, int32_t bufLen) { - return snprintf(pBuf, bufLen, "%"PRId64, *pInt); -} - static int64_t SCALE_MULTIPLIER_64[19] = {1LL, 10LL, 100LL, @@ -266,53 +190,293 @@ static int64_t SCALE_MULTIPLIER_64[19] = {1LL, 1000000000000000000LL}; static int32_t decimalGetWhole(const DecimalWord* pDec, DecimalInternalType type, int8_t scale, DecimalWord* pWhole) { - SWideIntegerOps ops = wideIntegerOps[type]; + SDecimalOps* pOps = getDecimalOpsImp(type); if (type == DECIMAL_64) { pWhole[0] = *pDec; DecimalWord scaleMul = SCALE_MULTIPLIER_64[scale]; - int32_t code = ops.divide(pWhole, &scaleMul, 1); + pOps->divide(pWhole, &scaleMul, 1); if (TSDB_CODE_SUCCESS != 0) { // TODO wjm } - ops.abs(pWhole); + pOps->abs(pWhole); } else { memcpy(pWhole, pDec, DECIMAL_GET_WORD_NUM(type) * sizeof(DecimalWord)); // TODO wjm - //ops.divide(pWhole->words, ) + // pOps.divide(pWhole->words, ) } return 0; } static int32_t decimalGetFrac(const DecimalWord* pDec, DecimalInternalType type, int8_t scale, DecimalWord* pFrac) { - SWideIntegerOps ops = wideIntegerOps[type]; + SDecimalOps* pOps = getDecimalOpsImp(type); if (type == DECIMAL_64) { pFrac[0] = *pDec; DecimalWord scaleMul = SCALE_MULTIPLIER_64[scale]; - int32_t code = ops.mod(pFrac, &scaleMul, 1); - ops.abs(pFrac); + pOps->mod(pFrac, &scaleMul, 1); + pOps->abs(pFrac); } else { - } return 0; } -int32_t decimalToStr(DecimalWord* pDec, int8_t precision, int8_t scale, char* pBuf, int32_t bufLen) { - DecimalInternalType iType = DECIMAL_GET_INTERNAL_TYPE(precision); - int32_t wordNum = DECIMAL_GET_WORD_NUM(iType); - SWideIntegerOps ops = wideIntegerOps[iType]; - DecimalMax whole = {0}, frac = {0}; - DecimalWord zero = 0; - int32_t pos = 0; +static void decimal64Negate(DecimalWord* pInt); +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 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); +static bool decimal64Eq(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); +static int32_t decimal64ToStr(const DecimalWord* pInt, uint8_t scale, char* pBuf, int32_t bufLen); - if (ops.lt(pDec, &zero, 1)) { - pos = sprintf(pBuf, "-"); +static void decimal128Negate(DecimalWord* pInt); +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 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); +static bool decimal128Eq(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum); +static int32_t decimal128ToStr(const DecimalWord* pInt, uint8_t scale, char* pBuf, int32_t bufLen); + +SDecimalOps decimal64Ops = {decimal64Negate, decimal64Abs, decimal64Add, decimal64Subtract, + decimal64Multiply, decimal64divide, decimal64Mod, decimal64Lt, + decimal64Gt, decimal64Eq, decimal64ToStr}; +SDecimalOps decimal128Ops = {decimal128Negate, decimal128Abs, decimal128Add, decimal128Subtract, + decimal128Multiply, decimal128divide, decimal128Mod, decimal128Lt, + decimal128Gt, decimal128Eq, decimal128ToStr}; + +static SDecimalOps* getDecimalOpsImp(DecimalInternalType t) { + switch (t) { + case DECIMAL_128: + return &decimal128Ops; + case DECIMAL_64: + return &decimal64Ops; + default: + return NULL; } - int32_t code = decimalGetWhole(pDec, iType, scale, whole.words); - if (!ops.eq(whole.words, &zero, 1)) { - pos += ops.toStr(whole.words, pBuf + pos, bufLen - pos); +} +SDecimalOps* getDecimalOps(int8_t dataType) { return getDecimalOpsImp(DECIMAL_GET_INTERNAL_TYPE(dataType)); } + +void makeDecimal64(Decimal64* pDec64, DecimalWord w) { pDec64->words[0] = w; } + +// TODO wjm handle overflow problem of DecimalWord +void decimal64Negate(DecimalWord* pInt) { *pInt = -(int64_t)(*pInt); } +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 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; } +bool decimal64Eq(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { + return *pLeft == *pRight; +} +int32_t decimal64ToStr(const DecimalWord* pInt, uint8_t scale, char* pBuf, int32_t bufLen) { + return snprintf(pBuf, bufLen, "%" PRId64, *pInt); +} + +// TODO wjm handle endian problem +#define DECIMAL128_LOW_WORDS(pDec) (uint64_t)((pDec)->words[0]) +#define DECIMAL128_HIGH_WORDS(pDec) (int64_t)((pDec)->words[1]) +// return 1 if positive or zero, else return -1 +#define DECIMAL128_SIGN(pDec) (1 | (DECIMAL128_HIGH_WORDS(pDec) >> 63)) + +void makeDecimal128(Decimal128* pDec128, int64_t hi, uint64_t low) { + // TODO wjm handle endian problem + pDec128->words[1] = hi; + pDec128->words[0] = low; +} + +static void makeDecimal128FromDecimal64(Decimal128* pTarget, Decimal64 decimal64) { + DecimalWord zero = 0, sign = 0; + if (decimal64Lt(decimal64.words, &zero, 1)) { + decimal64Negate(decimal64.words); + sign = -1; + } + makeDecimal128(pTarget, sign, (uint64_t)decimal64.words[0]); +} + +static void decimal128Negate(DecimalWord* pWord) { + Decimal128* pDec = (Decimal128*)pWord; + uint64_t lo = ~DECIMAL128_LOW_WORDS(pDec) + 1; + int64_t hi = ~DECIMAL128_HIGH_WORDS(pDec); + if (lo == 0) hi = SAFE_INT64_ADD(hi, 1); + makeDecimal128(pDec, hi, lo); +} + +static void decimal128Abs(DecimalWord* pWord) { + DecimalWord zero = 0; + if (decimal128Lt(pWord, &zero, 1)) { + decimal128Negate(pWord); + } +} + +// TODO wjm put it out of decimal128 functions +#define DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pTarget, rightDec, pWord) \ + if (rightWordNum != WORD_NUM(Decimal128)) { \ + Decimal64 d64 = {0}; \ + makeDecimal64(&d64, *pWord); \ + makeDecimal128FromDecimal64(&rightDec, d64); \ + pTarget = &rightDec; \ + } + +static void decimal128Add(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 right = {0}; + DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); + + int64_t hi = SAFE_INT64_ADD(DECIMAL128_HIGH_WORDS(pLeftDec), DECIMAL128_HIGH_WORDS(pRightDec)); + uint64_t lo = DECIMAL128_LOW_WORDS(pLeftDec) + DECIMAL128_LOW_WORDS(pRightDec); + hi = SAFE_INT64_ADD(hi, lo < DECIMAL128_LOW_WORDS(pLeftDec)); + makeDecimal128(pLeftDec, hi, lo); +} + +static void decimal128Subtract(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 right = {0}; + DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); + + int64_t hi = SAFE_INT64_SUBTRACT(DECIMAL128_HIGH_WORDS(pLeftDec), DECIMAL128_HIGH_WORDS(pRightDec)); + uint64_t lo = DECIMAL128_LOW_WORDS(pLeftDec) - DECIMAL128_LOW_WORDS(pRightDec); + hi = SAFE_INT64_SUBTRACT(hi, lo > DECIMAL128_LOW_WORDS(pLeftDec)); + makeDecimal128(pLeftDec, hi, lo); +} + +static void decimal128Multiply(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 right = {0}; + DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); + + bool negate = DECIMAL128_SIGN(pLeftDec) != DECIMAL128_SIGN(pRightDec); + Decimal128 x = *pLeftDec, y = *pRightDec; + decimal128Abs(x.words); + decimal128Abs(y.words); + + UInt128 res = {0}, tmp = {0}; + makeUInt128(&res, DECIMAL128_HIGH_WORDS(&x), DECIMAL128_LOW_WORDS(&x)); + makeUInt128(&tmp, DECIMAL128_HIGH_WORDS(&y), DECIMAL128_LOW_WORDS(&y)); + uInt128Multiply(&res, &tmp); + makeDecimal128(pLeftDec, uInt128Hi(&res), uInt128Lo(&res)); + if (negate) decimal128Negate(pLeftDec->words); +} + +static bool decimal128Lt(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 right = {0}; + DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); + + return DECIMAL128_HIGH_WORDS(pLeftDec) < DECIMAL128_HIGH_WORDS(pRightDec) || + (DECIMAL128_HIGH_WORDS(pLeftDec) == DECIMAL128_HIGH_WORDS(pRightDec) && + DECIMAL128_LOW_WORDS(pLeftDec) < DECIMAL128_LOW_WORDS(pRightDec)); +} + +static void decimal128divide(DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) {} + +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 right = {0}; + DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); + + return decimal128Lt(pRightDec->words, pLeftDec->words, WORD_NUM(Decimal128)); +} + +static bool decimal128Eq(const DecimalWord* pLeft, const DecimalWord* pRight, uint8_t rightWordNum) { + Decimal128 *pLeftDec = (Decimal128*)pLeft, *pRightDec = NULL; + Decimal128 right = {0}; + DECIMAL128_CHECK_RIGHT_WORD_NUM(rightWordNum, pRightDec, right, pRight); + + return pLeftDec->words == pRightDec->words; +} + +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}; + *digitNum = 0; + makeUInt128(&a, DECIMAL128_HIGH_WORDS(pDec), DECIMAL128_LOW_WORDS(pDec)); + while (!uInt128Eq(&a, &uInt128Zero)) { + uint64_t hi = a >> 64; + uint64_t lo = a; + + uint64_t hiQuotient = hi / k1e18; + uint64_t hiRemainder = hi % k1e18; + makeUInt128(&b, hiRemainder, lo); + uInt128Divide(&b, &uInt128_1e18); + uint64_t loQuotient = uInt128Lo(&b); + makeUInt128(&b, hiRemainder, lo); + uInt128Mod(&b, &uInt128_1e18); + uint64_t loRemainder = uInt128Lo(&b); + makeUInt128(&a, hiQuotient, loQuotient); + digits[(*digitNum)++] = loRemainder; + } +} + +// TODO wjm checkBuflen +static int32_t decimal128ToStr(const DecimalWord* pInt, uint8_t scale, char* pBuf, int32_t bufLen) { + const Decimal128* pDec = (const Decimal128*)pInt; + bool negative = DECIMAL128_SIGN(pDec) < 0; + uint64_t segments[3] = {0}; + int32_t digitNum = 0; + char buf[64] = {0}; + if (negative) { + Decimal128 copy = {0}; + makeDecimal128(©, DECIMAL128_HIGH_WORDS(pDec), DECIMAL128_LOW_WORDS(pDec)); + decimal128Abs(copy.words); + extractDecimal128Digits(©, segments, &digitNum); + } else { + extractDecimal128Digits(pDec, segments, &digitNum); + } + int32_t len = 0; + for (int32_t i = digitNum - 1; i >= 0; --i) { + len += snprintf(buf + len, 64 - len, i == digitNum - 1 ? "%" PRIu64 : "%018" PRIu64, segments[i]); + } + int32_t wholeLen = len - scale; + if (wholeLen > 0) { + TAOS_STRNCAT(pBuf, buf, wholeLen); + } + if (scale > 0) { + TAOS_STRNCAT(pBuf, ".", 2); + TAOS_STRNCAT(pBuf, buf + TMAX(0, wholeLen), scale); + } + return len; +} + +int32_t decimalToStr(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) { + case DECIMAL_64: { + int32_t wordNum = DECIMAL_GET_WORD_NUM(iType); + SDecimalOps* pOps = getDecimalOpsImp(iType); + DecimalMax whole = {0}, frac = {0}; + DecimalWord zero = 0; + int32_t pos = 0; + + if (pOps->lt(pDec, &zero, 1)) { + pos = sprintf(pBuf, "-"); + } + int32_t code = decimalGetWhole(pDec, iType, scale, whole.words); + if (!pOps->eq(whole.words, &zero, 1)) { + pos += pOps->toStr(whole.words, scale, pBuf + pos, bufLen - pos); + } + pos += snprintf(pBuf + pos, bufLen - pos, "."); + code = decimalGetFrac(pDec, iType, scale, frac.words); + pOps->toStr(frac.words, scale, pBuf + pos, bufLen - pos); + return 0; + } + case DECIMAL_128: + return decimal128ToStr(pDec, scale, pBuf, bufLen); + default: + break; } - pos += snprintf(pBuf + pos, bufLen - pos, "."); - code = decimalGetFrac(pDec, iType, scale, frac.words); - ops.toStr(frac.words, pBuf + pos, bufLen - pos); return 0; } diff --git a/source/libs/decimal/src/wideInteger.c b/source/libs/decimal/src/wideInteger.c new file mode 100644 index 0000000000..03823e5e28 --- /dev/null +++ b/source/libs/decimal/src/wideInteger.c @@ -0,0 +1,25 @@ +#include "wideInteger.h" + +#if defined(__GNUC__) || defined(__clang__) +// #if 0 +void makeUInt128(UInt128* pInt, DecimalWord hi, DecimalWord lo) { *pInt = ((UInt128)hi) << 64 | lo; } +uint64_t uInt128Hi(const UInt128* pInt) { return *pInt >> 64; } +uint64_t uInt128Lo(const UInt128* pInt) { return *pInt & 0xFFFFFFFFFFFFFFFF; } + +void uInt128Abs(UInt128* pInt); +void uInt128Add(UInt128* pLeft, const UInt128* pRight) { *pLeft += *pRight; } +void uInt128Subtract(UInt128* pLeft, const UInt128* pRight); +void uInt128Multiply(UInt128* pLeft, const UInt128* pRight) { *pLeft = *pLeft * *pRight; } +void uInt128Divide(UInt128* pLeft, const UInt128* pRight) { *pLeft /= *pRight; } +void uInt128Mod(UInt128* pLeft, const UInt128* pRight) { *pLeft %= *pRight; } +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; +#else + +void uInt128Multiply(UInt128* pLeft, const UInt128* pRight) {} + +#endif diff --git a/source/libs/decimal/test/CMakeLists.txt b/source/libs/decimal/test/CMakeLists.txt index 4a2b1f2b6d..23ee480ccc 100644 --- a/source/libs/decimal/test/CMakeLists.txt +++ b/source/libs/decimal/test/CMakeLists.txt @@ -9,9 +9,10 @@ ADD_EXECUTABLE(decimalTest ${SOURCE_LIST}) TARGET_INCLUDE_DIRECTORIES( decimalTest PUBLIC "${TD_SOURCE_DIR}/include/libs/decimal/" + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../inc" ) TARGET_LINK_LIBRARIES( decimalTest - PUBLIC os util common gtest qcom + PUBLIC os util common gtest qcom decimal ) diff --git a/source/libs/decimal/test/decimalTest.cpp b/source/libs/decimal/test/decimalTest.cpp index 64becff4b6..c29eeb0220 100644 --- a/source/libs/decimal/test/decimalTest.cpp +++ b/source/libs/decimal/test/decimalTest.cpp @@ -1,6 +1,74 @@ #include +#include +#include "decimal.h" +#include "wideInteger.h" +using namespace std; + +template +void printArray(const std::array &arr) { + auto it = arr.rbegin(); + for (; it != arr.rend(); ++it) { + cout << *it; + } + cout << endl; +} + +template +void extractWideInteger(__int128 a) { + uint64_t k = 10; + std::array segments{}; + int seg_num = 0; + for (int i = 0; i < DIGIT_NUM; ++i) { + k *= 10; + } + + while (a != 0) { + uint64_t hi = a >> 64; + uint64_t lo = a; + cout << "hi: " << hi << " lo: " << lo << endl; + uint64_t hi_quotient = hi / k; + uint64_t hi_remainder = hi % k; + // cout << "hi % 1e9: " << hi_remainder << endl; + __int128 tmp = ((__int128)hi_remainder << 64) | (__int128)lo; + uint64_t lo_remainder = tmp % k; + uint64_t lo_quotient = tmp / k; + a = (__int128)hi_quotient << 64 | (__int128)lo_quotient; + segments[seg_num++] = lo_remainder; + } + printArray<38 / DIGIT_NUM>(segments); +} + +__int128 generate_big_int128(uint32_t digitNum) { + __int128 a = 0; + for (int i = 0; i < digitNum + 1; ++i) { + a *= 10; + a += (i % 10); + } + return a; +} + +TEST(decimal, a) { + __int128 a = generate_big_int128(37); + extractWideInteger<9>(a); + ASSERT_TRUE(1); +} + +TEST(decimal128, to_string) { + __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}; + decimalToStr(d.words, TSDB_DATA_TYPE_DECIMAL, 38, 10, buf, 64); + ASSERT_STREQ(buf, "123456789012345678901234567.8901234567"); + + buf[0] = '\0'; + decimalToStr(d.words, TSDB_DATA_TYPE_DECIMAL, 38, 9, buf, 64); + ASSERT_STREQ(buf, "1234567890123456789012345678.901234567"); +} int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/tests/system-test/2-query/decimal.py b/tests/system-test/2-query/decimal.py index b6f5764772..ab72ed3c9b 100644 --- a/tests/system-test/2-query/decimal.py +++ b/tests/system-test/2-query/decimal.py @@ -44,6 +44,8 @@ class DecimalType: digits += '.' for _ in range(dscale): digits += str(secrets.randbelow(10)) + if digits == '': + digits = '0' return digits @@ -409,9 +411,9 @@ class TDTestCase: tdLog.printNoPrefix("-------- test create decimal column") self.columns = [ DataType(TypeEnum.DECIMAL, type_mod=DataType.get_decimal_type_mod(DecimalType(10, 2))), - #DataType(TypeEnum.DECIMAL, type_mod=DataType.get_decimal_type_mod(DecimalType(20, 2))), - #DataType(TypeEnum.DECIMAL, type_mod=DataType.get_decimal_type_mod(DecimalType(30, 2))), - #DataType(TypeEnum.DECIMAL, type_mod=DataType.get_decimal_type_mod(DecimalType(38, 2))), + DataType(TypeEnum.DECIMAL, type_mod=DataType.get_decimal_type_mod(DecimalType(20, 2))), + DataType(TypeEnum.DECIMAL, type_mod=DataType.get_decimal_type_mod(DecimalType(30, 2))), + DataType(TypeEnum.DECIMAL, type_mod=DataType.get_decimal_type_mod(DecimalType(38, 2))), DataType(TypeEnum.TINYINT), DataType(TypeEnum.INT), DataType(TypeEnum.BIGINT),