define wide integer, support decimal128

This commit is contained in:
wangjiaming0909 2024-12-27 18:20:42 +08:00
parent eccbae4e52
commit cdba5bd9c0
12 changed files with 471 additions and 153 deletions

View File

@ -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);

View File

@ -20,8 +20,8 @@
extern "C" {
#endif
#include "ttypes.h"
#include "tdef.h"
#include "ttypes.h"
typedef struct SDataType SDataType;
typedef struct SValue SValue;
@ -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
}

View File

@ -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
}

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _TD_WIDE_INTEGER_H_
#define _TD_WIDE_INTEGER_H_
#include <stdint.h>
#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_ */

View File

@ -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);
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);
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;
}
}
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(&copy, DECIMAL128_HIGH_WORDS(pDec), DECIMAL128_LOW_WORDS(pDec));
decimal128Abs(copy.words);
extractDecimal128Digits(&copy, 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);
SWideIntegerOps ops = wideIntegerOps[iType];
SDecimalOps* pOps = getDecimalOpsImp(iType);
DecimalMax whole = {0}, frac = {0};
DecimalWord zero = 0;
int32_t pos = 0;
if (ops.lt(pDec, &zero, 1)) {
if (pOps->lt(pDec, &zero, 1)) {
pos = sprintf(pBuf, "-");
}
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);
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);
ops.toStr(frac.words, pBuf + pos, bufLen - pos);
pOps->toStr(frac.words, scale, pBuf + pos, bufLen - pos);
return 0;
}
case DECIMAL_128:
return decimal128ToStr(pDec, scale, pBuf, bufLen);
default:
break;
}
return 0;
}

View File

@ -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

View File

@ -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
)

View File

@ -1,4 +1,72 @@
#include <gtest/gtest.h>
#include <iostream>
#include "decimal.h"
#include "wideInteger.h"
using namespace std;
template <int N>
void printArray(const std::array<uint64_t, N> &arr) {
auto it = arr.rbegin();
for (; it != arr.rend(); ++it) {
cout << *it;
}
cout << endl;
}
template <int DIGIT_NUM>
void extractWideInteger(__int128 a) {
uint64_t k = 10;
std::array<uint64_t, 38 / DIGIT_NUM> 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);

View File

@ -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),