define wide integer, support decimal128
This commit is contained in:
parent
eccbae4e52
commit
cdba5bd9c0
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ */
|
|
@ -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(©, 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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue