8449 lines
301 KiB
C++
8449 lines
301 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#define USING_LOG_PREFIX LIB
|
|
#include "lib/number/ob_number_v2.h"
|
|
#include "lib/ob_define.h"
|
|
#include "lib/utility/utility.h"
|
|
#include "lib/utility/serialization.h"
|
|
#include "lib/worker.h"
|
|
#include <assert.h>
|
|
#include <type_traits>
|
|
#include "lib/charset/ob_dtoa.h"
|
|
#include <algorithm>
|
|
#include "lib/oblog/ob_log_module.h"
|
|
#include "lib/utility/ob_fast_convert.h"
|
|
#include "lib/utility/ob_hang_fatal_error.h"
|
|
|
|
namespace oceanbase
|
|
{
|
|
namespace common
|
|
{
|
|
using namespace lib;
|
|
namespace number
|
|
{
|
|
const char *ObNumber::DIGIT_FORMAT = "%09u";
|
|
const char *ObNumber::BACK_DIGIT_FORMAT[DIGIT_LEN] =
|
|
{
|
|
"%09u",
|
|
"%08u",
|
|
"%07u",
|
|
"%06u",
|
|
"%05u",
|
|
"%04u",
|
|
"%03u",
|
|
"%02u",
|
|
"%01u",
|
|
};
|
|
|
|
const int ObNumber::MIN_ROUND_DIGIT_COUNT[] = {
|
|
ObNumber::FLOATING_SCALE / ObNumber::DIGIT_LEN,
|
|
common::OB_MAX_NUMBER_PRECISION_INNER / ObNumber::DIGIT_LEN
|
|
};
|
|
|
|
const char ObNumber::FLOATING_ZEROS[ObNumber::FLOATING_SCALE + 1] =
|
|
"000000000000000000000000000000000000000000000000000000000000000000000000";
|
|
|
|
const ObNumber &ObNumber::get_pi()
|
|
{
|
|
// len, flag, exp, sign
|
|
static Desc desc(10, 0, EXP_ZERO, POSITIVE);
|
|
static uint32_t digits[] = {3,141592653,589793238,462643383,279502884,197169399,375105820,974944592,307816406,286208998};
|
|
static ObNumber pi(desc.desc_, reinterpret_cast<uint32_t*>(digits));
|
|
return pi;
|
|
}
|
|
|
|
const ObString NUMBER_ERRMSG("ERROR NUM");//9 byte
|
|
|
|
// ObNumber 序列化反序列化实现,完全参考了 ObObj 中的代码
|
|
DEFINE_SERIALIZE(ObNumber)
|
|
{
|
|
return serialization::encode_number_type(buf, buf_len, pos, d_, digits_);
|
|
}
|
|
|
|
DEFINE_DESERIALIZE(ObNumber)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_SUCC(serialization::decode_number_type(buf, data_len, pos, d_, digits_))) {
|
|
d_.reserved_ = 0; // reserved_ is always 0.
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
DEFINE_GET_SERIALIZE_SIZE(ObNumber)
|
|
{
|
|
return serialization::encode_length_number_type(d_);
|
|
}
|
|
|
|
const ObNumber &ObNumber::get_positive_one()
|
|
{
|
|
struct Init
|
|
{
|
|
explicit Init(ObNumber &v)
|
|
{
|
|
static StackAllocator sa;
|
|
v.from("1", sa);
|
|
};
|
|
};
|
|
static ObNumber one;
|
|
static Init init(one);
|
|
return one;
|
|
}
|
|
|
|
const ObNumber &ObNumber::get_positive_zero_dot_five()
|
|
{
|
|
struct Init
|
|
{
|
|
explicit Init(ObNumber &v)
|
|
{
|
|
static StackAllocator sa;
|
|
v.from("0.5", sa);
|
|
};
|
|
};
|
|
static ObNumber zero_dot_five;
|
|
static Init init(zero_dot_five);
|
|
return zero_dot_five;
|
|
}
|
|
|
|
const ObNumber &ObNumber::get_zero()
|
|
{
|
|
struct Init
|
|
{
|
|
explicit Init(ObNumber &v)
|
|
{
|
|
v.set_zero();
|
|
};
|
|
};
|
|
static ObNumber one;
|
|
static Init init(one);
|
|
return one;
|
|
}
|
|
|
|
uint32_t *ObNumber::alloc_(IAllocator &allocator, const int64_t num, const lib::ObMemAttr *attr)
|
|
{
|
|
if (0 >= num) {
|
|
digits_ = NULL;
|
|
} else {
|
|
digits_ = NULL != attr ? allocator.alloc(num * sizeof(uint32_t), *attr) : allocator.alloc(num * sizeof(uint32_t));
|
|
}
|
|
d_.reserved_ = 0; // keep reserved_ always 0.
|
|
return digits_;
|
|
}
|
|
|
|
bool ObNumber::is_equal(const number::ObNumber::Desc &this_desc,
|
|
const uint32_t *this_digits, const number::ObNumber::Desc &other_desc,
|
|
const uint32_t *other_digits)
|
|
{
|
|
bool bret = false;
|
|
if (this_desc.se_ == other_desc.se_ && this_desc.len_ == other_desc.len_) {
|
|
bret = uint32equal(this_digits, other_digits, this_desc.len_) ;
|
|
}
|
|
return bret;
|
|
}
|
|
|
|
int ObNumber::compare(const number::ObNumber::Desc &this_desc,
|
|
const uint32_t *this_digits, const number::ObNumber::Desc &other_desc,
|
|
const uint32_t *other_digits)
|
|
{
|
|
int ret = 0;
|
|
if (this_desc.se_ == other_desc.se_) {
|
|
if (this_desc.len_ == other_desc.len_) {
|
|
ret = uint32cmp(this_digits, other_digits, this_desc.len_);
|
|
} else if (this_desc.len_ < other_desc.len_) {
|
|
if (0 == (ret = uint32cmp(this_digits, other_digits, this_desc.len_))) {
|
|
ret = -1;
|
|
}
|
|
} else {
|
|
if (0 == (ret = uint32cmp(this_digits, other_digits, other_desc.len_))) {
|
|
ret = 1;
|
|
}
|
|
}
|
|
if (NEGATIVE == this_desc.sign_) {
|
|
ret = -ret;
|
|
}
|
|
} else if (this_desc.se_ < other_desc.se_) {
|
|
ret = -1;
|
|
} else {
|
|
ret = 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
对于整型数值,可以做如下的优化:
|
|
计算每一个进制位上的对应的数值(整型最多有两位),并根据整型参数填充Number的DESC(符号位、exp等)。
|
|
比如对于1000000001,进制位10^9,不断除以10^9,可以计算出两个进制位上的数值为(1,1),即digit数组为
|
|
[1, 1],并且可以知道指数值为1,符号位的值为1,digit的有效长度为2
|
|
*/
|
|
template <class IntegerT>
|
|
int ObNumber::from_integer_(const IntegerT value, IAllocator &allocator) {
|
|
int ret = OB_SUCCESS;
|
|
static const uint64_t MAX_DIGITS[3] = {1L,
|
|
1000000000L,
|
|
1000000000000000000L};
|
|
if (0 == value) {
|
|
set_zero();
|
|
} else {
|
|
Desc desc;
|
|
uint64_t abs_val = 0;
|
|
|
|
if (std::is_signed<IntegerT>::value) { // 如果是符号整数,调用abs
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wabsolute-value"
|
|
#endif
|
|
abs_val = (uint64_t)std::labs(value);
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
} else { // 否则直接赋值
|
|
abs_val = value;
|
|
}
|
|
uint64_t exp = 0;
|
|
uint32_t digits[OB_MAX_INTEGER_DIGIT] = {0};
|
|
uint8_t idx = 0;
|
|
uint8_t zero_cnt = 0;
|
|
|
|
for (int i = 2; i >= 0; i--) {
|
|
if (abs_val >= MAX_DIGITS[i]) {
|
|
idx += zero_cnt;
|
|
digits[idx++] = uint32_t(abs_val / MAX_DIGITS[i]);
|
|
exp = std::max(exp, (uint64_t)i);
|
|
abs_val %= MAX_DIGITS[i];
|
|
zero_cnt = 0;
|
|
} else if (exp > 0) {
|
|
zero_cnt++;
|
|
}
|
|
}
|
|
|
|
desc.exp_ = ((uint8_t)exp + EXP_ZERO) & 0x7f;
|
|
if (value >= 0) {
|
|
desc.sign_ = POSITIVE;
|
|
} else {
|
|
desc.sign_ = NEGATIVE;
|
|
desc.exp_ = 0x7f & (~desc.exp_);
|
|
++desc.exp_;
|
|
}
|
|
desc.len_ = idx;
|
|
desc.reserved_ = 0;
|
|
|
|
uint32_t* digit_mem = NULL;
|
|
if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * idx))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("fail to alloc obnumber digit memory", K(ret), K(digit_mem), K(idx));
|
|
} else {
|
|
MEMCPY(digit_mem, digits, ITEM_SIZE(digits_)*idx);
|
|
assign(desc.desc_, (uint32_t*)digit_mem);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_(const int64_t value, IAllocator &allocator)
|
|
{
|
|
return from_integer_(value, allocator);
|
|
}
|
|
|
|
int ObNumber::from_(const uint64_t value, IAllocator &allocator)
|
|
{
|
|
return from_integer_(value, allocator);
|
|
}
|
|
|
|
int ObNumber::from_(const char *str, IAllocator &allocator, int16_t *precision, int16_t *scale, const bool do_rounding)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int warning = OB_SUCCESS;
|
|
int64_t length = (NULL == str) ? 0 : strlen(str);
|
|
ret = from_(str, length, allocator, warning, NULL, precision, scale, NULL, do_rounding);
|
|
if (OB_SUCCESS == ret && OB_SUCCESS != warning) {
|
|
ret = warning;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* from_sci -- from scientific notation
|
|
*
|
|
* str -- IN, scientific notation string representation
|
|
* length -- IN, the length of the input string
|
|
* allocator -- IN, used to alloc ObNumber
|
|
* precision -- OUT, precision of the converted result
|
|
* scale -- OUT, scale of the converted result
|
|
* This function converts a scientific notation string into the internal number format
|
|
* It's more efficient to call from_ if the string is a normal numeric string */
|
|
int ObNumber::from_sci_(const char *str, const int64_t length, IAllocator &allocator, int &warning,
|
|
int16_t *precision, int16_t *scale, const bool do_rounding)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
char full_str[MAX_PRINTABLE_SIZE] = {0};
|
|
char digit_str[MAX_PRINTABLE_SIZE] = {0};
|
|
bool is_neg = false;
|
|
int32_t nth = 0;
|
|
int32_t i_nth = 0;
|
|
int32_t i = 0;
|
|
int32_t e_value = 0;
|
|
int32_t valid_len = 0;
|
|
int32_t dec_n_zero = 0;
|
|
bool e_neg = false;
|
|
bool as_zero = false;
|
|
bool has_digit = false;
|
|
char tmpstr[MAX_PRINTABLE_SIZE] = {0};
|
|
char cur = '\0';
|
|
if (OB_UNLIKELY(NULL == str || length <= 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid param", K(length), KP(str), K(ret));
|
|
} else {
|
|
/* parse str like 1.2e3
|
|
* part 1:allow str like 000123=> 123(valid length is 3); if valid length >126, ignore the rest
|
|
* part 2:if '.' exists, part 2 must not be empty: str like 1.e3 is illegal
|
|
* part 3:part 3's value + length of part1 <= 126
|
|
* */
|
|
for (i = 0; i < length && isspace(str[i]); ++i);
|
|
if ('-' == str[i]) {
|
|
is_neg = true;
|
|
full_str[nth++] = '-';
|
|
i++;
|
|
} else if ('+' == str[i]) {
|
|
i++;
|
|
}
|
|
/* 000123 -> 123 */
|
|
while ('0' == str[i] && i + 1 < length) {
|
|
i++;
|
|
has_digit = true;
|
|
}
|
|
cur = str[i];
|
|
while(i + 1 < length && cur <= '9' && cur >= '0') {
|
|
if (i_nth < MAX_PRECISION) {
|
|
digit_str[i_nth++] = cur;
|
|
cur = str[++i];
|
|
has_digit = true;
|
|
} else {
|
|
/* ignore the rest */
|
|
i++;
|
|
}
|
|
valid_len++;
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && cur == '.' && valid_len < MAX_PRECISION && i + 1 < length) {
|
|
cur = str[++i];
|
|
if (0 == valid_len) {
|
|
/* 0.000123 -> dec_n_zero = 3 */
|
|
while ('0' == cur && i + 1 < length) {
|
|
valid_len--;
|
|
dec_n_zero++;
|
|
cur = str[++i];
|
|
has_digit = true;
|
|
}
|
|
}
|
|
/* 1.23 0.0123 0.123 -> digit_str:'123' */
|
|
while(i + 1 < length && cur <= '9' && cur >= '0') {
|
|
if (i_nth < MAX_PRECISION) {
|
|
digit_str[i_nth++] = cur;
|
|
} else {
|
|
/* ignore the rest */
|
|
}
|
|
cur = str[++i];
|
|
if (0 >= valid_len) {
|
|
valid_len--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && (has_digit || 0 < i_nth)
|
|
&& ('e' == cur || 'E' == cur)
|
|
&& is_valid_sci_tail_(str, length, i)) {
|
|
LOG_DEBUG("ObNumber from sci", K(ret), K(i), K(cur), K(is_neg), K(nth), KCSTRING(digit_str), K(i_nth),
|
|
K(valid_len), K(dec_n_zero));
|
|
if (0 == i || i >= length - 1) {
|
|
if (i_nth > 0) {
|
|
memcpy(full_str + nth, digit_str, i_nth);
|
|
nth += i_nth;
|
|
}
|
|
warning = OB_INVALID_NUMERIC;
|
|
} else if (0 == valid_len || 0 == i_nth) {
|
|
// `i_nth = 0` means all digits are zero.
|
|
/* ignore e's value; only do the check*/
|
|
cur = str[++i];
|
|
if ('-' == cur || '+' == cur) {
|
|
if (i < length - 1) {
|
|
cur = str[++i];
|
|
}
|
|
}
|
|
if (cur <= '9' && cur >= '0') {
|
|
while (cur <= '9' && cur >= '0' && (++i < length)) {
|
|
cur = str[i];
|
|
}
|
|
} else {
|
|
/* 0e */
|
|
ret = OB_INVALID_NUMERIC;
|
|
LOG_WARN("Number from sci invalid exponent", K(ret), K(cur), K(i));
|
|
}
|
|
} else {
|
|
cur = str[++i];
|
|
switch (cur) {
|
|
case '-':
|
|
e_neg = true;
|
|
/* go through */
|
|
case '+':
|
|
if (i < length - 1) {
|
|
cur = str[++i];
|
|
}
|
|
break;
|
|
}
|
|
/* Oracle max valid length of string is 255(exponent of the value is [-253, 255])
|
|
* exponent of number's legal range is [-130, 125]
|
|
* so e_value's valid range can't larger than 3 digits */
|
|
int e_cnt = 0;
|
|
while (i < length && cur <= '9' && cur >= '0') {
|
|
if (e_cnt < 4) {
|
|
e_value = e_neg ? (e_value * 10 - (cur - '0')) : (e_value * 10 + cur - '0');
|
|
}
|
|
e_cnt++;
|
|
if (++i >= length) {
|
|
break;
|
|
}
|
|
cur = str[i];
|
|
}
|
|
|
|
LOG_DEBUG("ObNumber from sci E", K(warning), K(e_neg), K(e_cnt), K(e_value), K(valid_len), K(i));
|
|
if (0 == e_cnt) {
|
|
warning = OB_INVALID_NUMERIC;
|
|
e_value = 0;
|
|
}
|
|
if ((valid_len >= 0 && (e_value + valid_len <= MIN_SCI_SIZE))
|
|
||(valid_len < 0 && (e_value - dec_n_zero <= MIN_SCI_SIZE))) {
|
|
as_zero = true;
|
|
} else if (e_value + valid_len > MAX_SCI_SIZE) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
} else if (valid_len <= 0) {
|
|
/* 0.01234e-5 */
|
|
if (e_value < 0) {
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "0.%0*d%s", 0 - e_value + dec_n_zero, 0, digit_str);
|
|
LOG_DEBUG("ObNumber sci", KCSTRING(tmpstr), K(nth), KCSTRING(full_str), K(e_value), K(dec_n_zero), KCSTRING(digit_str));
|
|
} else {
|
|
if (dec_n_zero - e_value > 0) {
|
|
/* 0.00012e2 -> 0.012 */
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "0.%0*d%s", dec_n_zero - e_value, 0, digit_str);
|
|
LOG_DEBUG("ObNumber sci", KCSTRING(tmpstr), K(e_value), K(dec_n_zero));
|
|
} else if (e_value < dec_n_zero + i_nth) {
|
|
/* 0.001234e4 -> 12.34
|
|
* e_value - dec_n_zero = 4 - 2 = 2
|
|
* fmt str: %2.s%s, digit_str, digit_str + 2*/
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "%.*s.%s", e_value - dec_n_zero, digit_str, digit_str + e_value - dec_n_zero);
|
|
LOG_DEBUG("ObNumber sci", KCSTRING(tmpstr), KCSTRING(full_str), K(nth), K(e_value), K(dec_n_zero), KCSTRING(digit_str));
|
|
} else {
|
|
/* 0.001234e8 -> 123400
|
|
* e_value - dec_n_zero - i_nth = 8 - 2 - 4 = 2 */
|
|
if (e_value - dec_n_zero - i_nth > 0) {
|
|
snprintf(tmpstr, MAX_PRINTABLE_SIZE, "%0*d", e_value - dec_n_zero - i_nth, 0);
|
|
}
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "%s%s", digit_str, tmpstr);
|
|
LOG_DEBUG("ObNumber sci", KCSTRING(tmpstr), KCSTRING(digit_str), KCSTRING(full_str), K(nth), K(e_value), K(i_nth), K(dec_n_zero));
|
|
}
|
|
}
|
|
} else {
|
|
if (e_value >= 0) {
|
|
/* 12.34e5 -> 1234000
|
|
* e_value - (i_nth - valid_len) = 5 - (4 - 2) = 0*/
|
|
if (e_value - (i_nth - valid_len) > 0) {
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "%s%0*d",
|
|
digit_str, e_value - (i_nth - valid_len), 0);
|
|
LOG_DEBUG("ObNumber sci", KCSTRING(tmpstr), KCSTRING(full_str), K(nth), K(e_value), K(i_nth), K(valid_len));
|
|
} else if (e_value - (i_nth - valid_len) == 0) {
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "%s", digit_str);
|
|
LOG_DEBUG("ObNumber sci", KCSTRING(full_str), K(nth), K(e_value), K(i_nth), K(valid_len));
|
|
} else {
|
|
/* 12.345e2 -> 1234.5
|
|
* valid_len + e_value = 2 + 2 = 4
|
|
* fmt_str: %4.s, digit_str, digit_str + 4*/
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "%.*s.%s",
|
|
valid_len + e_value, digit_str, digit_str + valid_len + e_value);
|
|
LOG_DEBUG("ObNumber sci", K(valid_len + e_value), KCSTRING(full_str), K(nth), K(e_value), KCSTRING(digit_str), K(valid_len));
|
|
}
|
|
} else {
|
|
if (valid_len + e_value > 0)
|
|
{
|
|
/* 12.34e-1 -> 1.234
|
|
* valid_len + e_value = 2 - 1 = 1
|
|
* fmt_str: %1.s, digit_str, digit_str + 1*/
|
|
//sprintf(tmpstr, "%%%d.s.%%s", valid_len + e_value);
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "%.*s.%s",
|
|
valid_len + e_value, digit_str, digit_str + valid_len + e_value);
|
|
LOG_DEBUG("ObNumber sci", K(valid_len + e_value), KCSTRING(full_str), K(nth), K(e_value), KCSTRING(digit_str), K(valid_len));
|
|
} else {
|
|
/* 12.34e-4 -> 0.001234
|
|
* 0 - (valid_len + e_value) = 0 - (2 - 4) = 2 */
|
|
if (valid_len + e_value < 0) {
|
|
snprintf(tmpstr, MAX_PRINTABLE_SIZE, "%0*d", 0 - (valid_len + e_value), 0);
|
|
}
|
|
nth += snprintf(full_str + nth, MAX_PRINTABLE_SIZE - nth, "0.%s%s", tmpstr, digit_str);
|
|
LOG_DEBUG("ObNumber sci", KCSTRING(tmpstr), KCSTRING(full_str), K(nth), K(e_value), KCSTRING(digit_str), K(valid_len));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) || !is_oracle_mode()) {
|
|
LOG_DEBUG("Number from sci last ", K(cur), K(i), "str", ObString(length, str),
|
|
K(length), K(valid_len), K(ret), K(warning));
|
|
while (cur == ' ' && i < length - 1) {
|
|
cur = str[++i];
|
|
}
|
|
if (cur != ' ' && i <= length - 1) {
|
|
warning = OB_INVALID_NUMERIC;
|
|
LOG_WARN("invalid numeric string", K(ret), K(i), K(length), K(cur), "str", ObString(length, str));
|
|
}
|
|
if ((OB_SUCCESS != warning) && is_oracle_mode() && OB_SUCC(ret)) {
|
|
ret = warning;
|
|
} else if (OB_FAIL(ret) && !is_oracle_mode()) {
|
|
as_zero = true;
|
|
// warning = OB_ERR_DOUBLE_TRUNCATED;
|
|
warning = ret;
|
|
ret = OB_SUCCESS;
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
LOG_DEBUG("ObNumber sci final", K(ret), K(warning), K(full_str), K(nth), K(as_zero), K(e_neg), K(e_value), K(valid_len), K(i), K(i_nth));
|
|
if (as_zero || 0 == valid_len || 0 == i_nth) {
|
|
full_str[0] = '0';
|
|
nth = 1;
|
|
set_zero();
|
|
} else {
|
|
int tmp_warning = OB_SUCCESS;
|
|
ret = from_(full_str, nth, allocator, tmp_warning, NULL, precision, scale, NULL, do_rounding);
|
|
if (OB_SUCC(ret) && OB_SUCCESS != warning) {
|
|
warning = tmp_warning;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ret = from_(str, length, allocator, warning, NULL, precision, scale, NULL, do_rounding);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_v1_(const char *str, const int64_t length, IAllocator &allocator, int &warning,
|
|
ObNumberFmtModel *fmt, int16_t *precision, int16_t *scale, const lib::ObMemAttr *attr)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
uint32_t digits[MAX_CALC_LEN];
|
|
ObNumberBuilder nb;
|
|
nb.number_.set_digits(digits);
|
|
nb.number_.d_.reserved_ = 0;
|
|
if (OB_UNLIKELY(NULL == str || length <= 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid param", K(length), KP(str), K(ret));
|
|
} else if (OB_FAIL(nb.build(str, length, warning, fmt, precision, scale))) {
|
|
LIB_LOG(WARN, "number build from fail", K(ret), K(length), "str", ObString(length, str));
|
|
} else if (OB_FAIL(is_oracle_mode()
|
|
? nb.number_.round_scale_oracle_(MAX_SCALE, true, precision, scale)
|
|
: nb.number_.round_scale_(FLOATING_SCALE, true))) {
|
|
LIB_LOG(WARN, "round scale fail", K(ret), K(length), "str", ObString(length, str));
|
|
} else if (OB_FAIL(exp_check_(nb.number_.get_desc()))) {
|
|
LIB_LOG(WARN, "exponent precision check fail", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (0 == nb.number_.d_.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(nb.number_.d_.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
LIB_LOG(WARN, "out of range, ret = %d , length = %d",
|
|
K(ret), K(nb.number_.d_.len_));
|
|
} else if (OB_ISNULL(digits_ = alloc_(allocator, nb.number_.d_.len_, attr))) {
|
|
_OB_LOG(WARN, "alloc digits fail, length=%hhu", nb.number_.d_.len_);
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else {
|
|
MEMCPY(digits_, nb.number_.get_digits(), nb.number_.d_.len_ * ITEM_SIZE(digits_));
|
|
d_ = nb.number_.d_;
|
|
d_.reserved_ = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_v2_(const char *str, const int64_t length, IAllocator &allocator, int &warning,
|
|
ObNumberFmtModel *fmt, int16_t *precision, int16_t *scale, const lib::ObMemAttr *attr, const bool do_rounding)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
uint32_t digits[MAX_CALC_LEN] = {};
|
|
ObNumberBuilder nb;
|
|
nb.number_.set_digits(digits);
|
|
nb.number_.d_.reserved_ = 0;
|
|
if (OB_ISNULL(str) || OB_UNLIKELY(length <= 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid param", K(length), KP(str), K(ret));
|
|
} else if (OB_FAIL(nb.build_v2(str, length, warning, fmt, precision, scale))) {
|
|
LIB_LOG(WARN, "number build from fail", K(ret), K(length), "str", ObString(length, str));
|
|
} else if (do_rounding && OB_FAIL(nb.number_.round_scale_v3_(is_oracle_mode() ? MAX_SCALE: FLOATING_SCALE, true, false))) {
|
|
_LIB_LOG(WARN, "round scale fail, ret=%d str=[%.*s]", ret, static_cast<int32_t>(length), str);
|
|
} else if (OB_FAIL(exp_check_(nb.number_.get_desc(), lib::is_oracle_mode()))) {
|
|
LIB_LOG(WARN, "exponent precision check fail", K(nb.number_), K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (0 == nb.number_.d_.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(nb.number_.d_.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
LIB_LOG(WARN, "out of range", K(ret), K(nb.number_.d_.len_));
|
|
} else if (OB_ISNULL(digits_ = alloc_(allocator, nb.number_.d_.len_, attr))) {
|
|
_OB_LOG(WARN, "alloc digits fail, length=%hhu", nb.number_.d_.len_);
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else {
|
|
MEMCPY(digits_, nb.number_.get_digits(), nb.number_.d_.len_ * ITEM_SIZE(digits_));
|
|
d_ = nb.number_.d_;
|
|
d_.reserved_ = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* [start, floating_point-1] : int range
|
|
* [floating_point+1, end-1] : decimal range
|
|
*/
|
|
int ObNumber::find_point_range_(const char *str, const int64_t length,
|
|
int64_t &start_idx, int64_t &floating_point, int64_t &end_idx,
|
|
bool &negative, int32_t &warning, int16_t *precision, int16_t *scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t tmp_precision = 0;
|
|
int64_t tmp_scale = 0;
|
|
int64_t tmp_start_idx = 0;
|
|
if (OB_UNLIKELY(NULL == str || length <= 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid param", K(length), KP(str), K(ret));
|
|
} else {
|
|
const char *str_ptr = str;
|
|
const char *str_end = str + length;
|
|
|
|
for (; str_ptr != str_end && isspace(*str_ptr); ++str_ptr); /* ignore leading space */
|
|
for (; str_ptr != str_end && isspace(*(str_end - 1)); --str_end); /* ignore tailer space */
|
|
if (OB_UNLIKELY(str_ptr == str_end)) {
|
|
//empty string is enabled
|
|
warning = OB_INVALID_NUMERIC;
|
|
} else {
|
|
bool contains_sign = false;
|
|
/* check positive or negative */
|
|
if (*str_ptr == '+') {
|
|
++str_ptr;
|
|
contains_sign = true;
|
|
} else if (*str_ptr == '-') {
|
|
++str_ptr;
|
|
negative = true;
|
|
contains_sign = true;
|
|
}
|
|
int64_t str_length = str_end - str_ptr;
|
|
// In oracle mode, the number is invalid when str is '+', '-' or '.' without any digits.
|
|
// e.g: ' +', '-.' '+. '
|
|
if (lib::is_oracle_mode() &&
|
|
((0 == str_length && contains_sign) || (1 == str_length && *str_ptr == '.'))) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "invalid number", K(ret), KCSTRING(str));
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
for (; str_ptr != str_end && *str_ptr == '0'; ++str_ptr); /* ignore leading zero */
|
|
if (str_ptr == str_end) {
|
|
// Handleing all zeros cases. E.G: '0' or '000'.
|
|
// Make end_idx and start_idx point to same position so that consequent
|
|
// construct_digits_() would generate 0 num.
|
|
// Make start_idx point to last 0.
|
|
start_idx = str_end - 1 - str;
|
|
tmp_start_idx = start_idx;
|
|
floating_point = start_idx;
|
|
end_idx = start_idx;
|
|
} else {
|
|
|
|
start_idx = str_ptr - str;
|
|
tmp_start_idx = ((*str_ptr == '.') ? (start_idx - 1) : start_idx);
|
|
|
|
for (; str_ptr != str_end && ('0' <= *str_ptr && *str_ptr <= '9'); ++str_ptr);
|
|
|
|
if (str_ptr != str_end && *str_ptr == '.') { /* find floating point */
|
|
floating_point = str_ptr - str;
|
|
++str_ptr;
|
|
for (; str_ptr != str_end && ('0' <= *str_ptr && *str_ptr <= '9'); ++str_ptr);
|
|
end_idx = str_ptr - str;
|
|
} else {
|
|
floating_point = str_ptr - str;
|
|
end_idx = floating_point;
|
|
}
|
|
|
|
if (str_ptr != str_end) {
|
|
warning = OB_INVALID_NUMERIC;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (NULL != precision && NULL != scale) {
|
|
*scale = static_cast<int16_t>((end_idx - floating_point >= 1) ? (end_idx - floating_point - 1) : 0);
|
|
if (*scale > ObNumber::FLOATING_SCALE) {
|
|
*scale = ObNumber::FLOATING_SCALE;
|
|
}
|
|
*precision = static_cast<int16_t>(floating_point - tmp_start_idx + *scale);
|
|
tmp_precision = *precision;
|
|
tmp_scale = *scale;
|
|
}
|
|
}
|
|
const ObString tmp_string(length, str);
|
|
LIB_LOG(DEBUG, "succ to find_point_range_", K(tmp_string), K(start_idx),
|
|
K(tmp_start_idx), K(floating_point), K(end_idx), K(negative),
|
|
K(ret), K(warning), K(tmp_precision), K(tmp_scale));
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::construct_digits_(const char *str, const int64_t start_idx,
|
|
const int64_t floating_point, const int64_t end_idx, uint32_t *digits, int64_t &exp, int64_t &len)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t POWS[] = {1, 10, 100, 1000, 10000, 100000, 1000000, \
|
|
10000000, 100000000, 1000000000, 10000000000};
|
|
|
|
int64_t di_id = 0;
|
|
int step = 0;
|
|
bool has_integer_part = false;
|
|
uint32_t *curr_digit = digits + di_id;
|
|
if (start_idx < floating_point) {
|
|
const char *str_ptr = str + start_idx;
|
|
const char *str_end = str + floating_point;
|
|
int integer_length = (floating_point - start_idx - 1) / ObNumber::MAX_STORE_LEN + 1; // = floor[(end - start_idx + 1) / MAX_STORE_LEN]
|
|
step = (floating_point - start_idx - 1) % ObNumber::MAX_STORE_LEN + 1;
|
|
for (; str_ptr < str_end && di_id < len; str_ptr += step, ++di_id, step = ObNumber::MAX_STORE_LEN) {
|
|
*curr_digit = ObFastAtoi<uint32_t>::atoi_positive_unchecked(str_ptr, str_ptr + step);
|
|
// LIB_LOG(DEBUG, "push to construct_digits_", "di_id", di_id, "digit", *curr_digit, "curr_idx", str_ptr - str, K(step));
|
|
++curr_digit;
|
|
}
|
|
exp = integer_length - 1;
|
|
has_integer_part = true;
|
|
}
|
|
if (di_id < len && floating_point + 1 < end_idx) {
|
|
const char *str_ptr = str + floating_point + 1;
|
|
const char *str_end = str + end_idx;
|
|
if (!has_integer_part) {
|
|
for (; str_ptr != str_end && *str_ptr == '0'; ++str_ptr); // ignore leading zero in decimal part
|
|
int decimal_zero = ((str_ptr - str) - floating_point - 1) / ObNumber::MAX_STORE_LEN + 1;
|
|
exp = -decimal_zero;
|
|
step = ObNumber::MAX_STORE_LEN - ((str_ptr - str) - floating_point - 1) % ObNumber::MAX_STORE_LEN;
|
|
} else {
|
|
step = ObNumber::MAX_STORE_LEN;
|
|
}
|
|
for (; str_ptr < str_end && di_id < len; str_ptr += step, ++di_id, step = ObNumber::MAX_STORE_LEN) {
|
|
if (str_ptr + step < str_end) {
|
|
*curr_digit = ObFastAtoi<uint32_t>::atoi_positive_unchecked(str_ptr, str_ptr + step);
|
|
} else {
|
|
*curr_digit = ObFastAtoi<uint32_t>::atoi_positive_unchecked(str_ptr, str_end);
|
|
*curr_digit *= POWS[step - (str_end - str_ptr)];
|
|
}
|
|
// LIB_LOG(DEBUG, "push to construct_digits_", "di_id", di_id, "digit", *curr_digit, "curr_idx", str_ptr - str, K(step));
|
|
++curr_digit;
|
|
}
|
|
|
|
}
|
|
|
|
curr_digit = digits + di_id - 1;
|
|
while (curr_digit >= digits && 0 == *curr_digit) {
|
|
--curr_digit;
|
|
}
|
|
len = (curr_digit >= digits ? (curr_digit - digits + 1) : 0);
|
|
LIB_LOG(DEBUG, "succ to construct_digits_", K(exp), K(len));
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_v3_(const char *str, const int64_t length, IAllocator &allocator, int &warning,
|
|
ObNumberFmtModel *fmt, int16_t *precision, int16_t *scale, const lib::ObMemAttr *attr, const bool do_rounding)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(fmt);
|
|
uint32_t digits[OB_CALC_BUFFER_SIZE] ={}; // 只保存最多 72 位有效数字,占位 9 个uint32,多余的数字四舍五入
|
|
bool negative = false;
|
|
int64_t start_idx = -1;
|
|
int64_t floating_point = -1;
|
|
int64_t end_idx = -1;
|
|
int64_t exp = 0;
|
|
int64_t len = OB_CALC_BUFFER_SIZE;
|
|
|
|
Desc d;
|
|
/* Step 1: find floating point and integer & decimal range */
|
|
if (OB_FAIL(find_point_range_(str, length, start_idx, floating_point, end_idx, negative, warning,
|
|
precision, scale))) {
|
|
LOG_WARN("find floating point and range error\n");
|
|
/* Step 2: construct digits with fixed length, ignore overflowed, return exp and len */
|
|
} else if (OB_FAIL(construct_digits_(str, start_idx, floating_point, end_idx, digits, exp, len))) {
|
|
LOG_WARN("fail to construct digits\n");
|
|
/* Step 3: calc desc : promise exp and len are in range */
|
|
} else {
|
|
d.desc_ = 0;
|
|
d.sign_ = negative ? ObNumber::NEGATIVE : ObNumber::POSITIVE;
|
|
d.len_ = (uint8_t)len;
|
|
d.reserved_ = 0; // keep always 0.
|
|
d.exp_ = 0x7f & (uint8_t)(EXP_ZERO + exp);
|
|
if (d.sign_ == NEGATIVE) {
|
|
d.exp_ = (0x7f & ~d.exp_) + 1;
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
d_.desc_ = d.desc_;
|
|
if (0 == d.len_) {
|
|
set_zero();
|
|
/* Step 5: construct ObNumber */
|
|
} else if (OB_ISNULL(digits_ = alloc_(allocator, d.len_, attr))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
_OB_LOG(ERROR, "alloc digits fail, length=%hhu", d.len_);
|
|
} else {
|
|
MEMCPY(digits_, digits, d.len_ * sizeof(uint32_t));
|
|
|
|
/* Step 6: normalize to 72 digits and check */
|
|
if (do_rounding && OB_FAIL(round_scale_v3_(is_oracle_mode() ?
|
|
(-MIN_SCI_SIZE): FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret));
|
|
} else if (OB_FAIL(exp_check_(d_, lib::is_oracle_mode()))) {
|
|
LOG_WARN("exponent precision check fail", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
set_zero();
|
|
}
|
|
return ret;
|
|
}
|
|
int ObNumber::from_(const uint32_t desc, const ObCalcVector &vector, IAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
|
|
ObCalcVector normalized_vector = vector;
|
|
if (OB_FAIL(normalized_vector.normalize())) {
|
|
LOG_WARN("normalized_vector.normalize() fails", K(ret));
|
|
}
|
|
Desc d;
|
|
d.desc_ = desc;
|
|
d.len_ = (uint8_t)std::min(+MAX_STORE_LEN, normalized_vector.size());
|
|
if (OB_FAIL(ret)) {
|
|
LOG_WARN("Previous normalize() fails", K(ret));
|
|
} else if (0 == d.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(d.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
_OB_LOG(WARN, "out of range, ret = %d , length = %d",
|
|
ret, (int)d.len_);
|
|
} else if (OB_ISNULL(digits_ = alloc_(allocator, d.len_))) {
|
|
LOG_WARN("alloc digits fail", K(d.len_));
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else {
|
|
MEMCPY(digits_, normalized_vector.get_digits(), d.len_ * ITEM_SIZE(digits_));
|
|
d_.sign_ = d.sign_;
|
|
d_.exp_ = d.exp_;
|
|
if (OB_FAIL(normalize_(digits_, d.len_))) {
|
|
_OB_LOG(WARN, "normalize [%s] fail, ret=%d", to_cstring(*this), ret);
|
|
} else if (OB_FAIL(round_scale_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true))) {
|
|
LOG_WARN("round scale fail", K(ret), K(*this));
|
|
} else if (OB_FAIL(exp_check_(d_, lib::is_oracle_mode()))) {
|
|
LOG_WARN("exponent precision check fail", K(ret), K(*this));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else {
|
|
// do nothing
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_v2_(const uint32_t desc, const ObCalcVector &vector, IAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObCalcVector normalized_vector = vector;
|
|
if (OB_FAIL(normalized_vector.normalize())) {
|
|
LOG_WARN("normalized_vector.normalize() fails", K(ret));
|
|
}
|
|
Desc d;
|
|
d.desc_ = desc;
|
|
d.len_ = (uint8_t)std::min(+MAX_STORE_LEN, normalized_vector.size());
|
|
if (OB_FAIL(ret)) {
|
|
LOG_WARN("Previous normalize() fails", K(ret));
|
|
} else if (0 == d.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(d.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
_OB_LOG(WARN, "out of range, ret = %d , length = %d", ret, (int)d.len_);
|
|
} else if (OB_ISNULL(digits_ = alloc_(allocator, d.len_))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("alloc digits fail", K(d.len_));
|
|
} else {
|
|
MEMCPY(digits_, normalized_vector.get_digits(), d.len_ * ITEM_SIZE(digits_));
|
|
d_.sign_ = d.sign_;
|
|
d_.exp_ = d.exp_;
|
|
if (OB_FAIL(normalize_(digits_, d.len_))) {
|
|
_OB_LOG(WARN, "normalize [%s] fail, ret=%d", to_cstring(*this), ret);
|
|
} else if (OB_FAIL(round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(*this));
|
|
} else if (OB_FAIL(exp_check_(d_, lib::is_oracle_mode()))) {
|
|
LOG_WARN("exponent precision check fail", K(ret), K(*this));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else {
|
|
// do nothing
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_(
|
|
const int16_t precision,
|
|
const int16_t scale,
|
|
const char *str,
|
|
const int64_t length,
|
|
IAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int warning = OB_SUCCESS;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
|
|
if (OB_UNLIKELY(MIN_PRECISION > precision
|
|
|| MAX_PRECISION < precision
|
|
|| MIN_SCALE > scale
|
|
|| MAX_SCALE < scale
|
|
|| NULL == str
|
|
|| 0 >= length)) {
|
|
LOG_WARN("invalid param",
|
|
K(precision), K(scale), KP(str), K(length));
|
|
ret = OB_INVALID_ARGUMENT;
|
|
} else {
|
|
uint32_t digits[MAX_CALC_LEN];
|
|
ObNumberBuilder nb;
|
|
nb.number_.set_digits(digits);
|
|
nb.number_.d_.reserved_ = 0;
|
|
|
|
if (OB_FAIL(nb.build(str, length, warning))) {
|
|
_OB_LOG(WARN, "number build from fail, ret=%d str=[%.*s]", ret, (int)length, str);
|
|
} else if (OB_FAIL(nb.number_.check_and_round(precision, scale))) {
|
|
_OB_LOG(WARN, "check and round fail, ret=%d str=[%.*s]", ret, (int)length, str);
|
|
} else if (0 == nb.number_.d_.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(nb.number_.d_.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
_OB_LOG(WARN, "out of range, ret = %d , length = %d",
|
|
ret, (int)nb.number_.d_.len_);
|
|
} else if (OB_ISNULL(digits_ = alloc_(allocator, nb.number_.d_.len_))) {
|
|
LOG_WARN("alloc digits fail", K(nb.number_.d_.len_));
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else {
|
|
MEMCPY(digits_, nb.number_.get_digits(), nb.number_.d_.len_ * ITEM_SIZE(digits_));
|
|
d_ = nb.number_.d_;
|
|
d_.reserved_ = 0;
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
// pass
|
|
} else if (OB_SUCCESS != warning) {
|
|
ret = warning;
|
|
} else { /* Do nothing */ }
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_(const ObNumber &other, IAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
|
|
if (&other == this) {
|
|
// assign to self
|
|
} else if (0 == other.d_.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(other.d_.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
_OB_LOG(WARN, "out of range, ret = %d , length = %d",
|
|
ret, (int)other.d_.len_);
|
|
} else if (OB_ISNULL(digits_ = alloc_(allocator, other.d_.len_))) {
|
|
_OB_LOG(DEBUG, "alloc digits fail, length=%hhu", other.d_.len_);
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else {
|
|
MEMCPY(digits_, other.digits_, other.d_.len_ * ITEM_SIZE(digits_));
|
|
d_ = other.d_;
|
|
d_.reserved_ = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::deep_copy(const ObNumber &other, IAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
uint32_t *digits = NULL;
|
|
if (&other == this) {
|
|
// assign to self
|
|
} else if (0 == other.d_.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(other.d_.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
_OB_LOG(WARN, "out of range, ret = %d , length = %d",
|
|
ret, (int)other.d_.len_);
|
|
} else if (OB_ISNULL(digits = alloc_(allocator, other.d_.len_))) {
|
|
_OB_LOG(DEBUG, "alloc digits fail, length=%hhu", other.d_.len_);
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else {
|
|
MEMCPY(digits, other.digits_, other.d_.len_ * ITEM_SIZE(digits));
|
|
d_ = other.d_;
|
|
d_.reserved_ = 0;
|
|
digits_ = digits;
|
|
OB_LOG(DEBUG, "deep_copy", K(other));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::deep_copy_v3(const ObNumber &other, ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
uint32_t *digits = NULL;
|
|
if (&other == this) {
|
|
// assign to self
|
|
} else if (0 == other.d_.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(other.d_.len_ > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
_OB_LOG(WARN, "out of range, ret = %d , length = %d",
|
|
ret, (int)other.d_.len_);
|
|
} else if (OB_ISNULL(digits = (uint32_t *)allocator.alloc(sizeof(uint32_t) * other.d_.len_))) {
|
|
_OB_LOG(DEBUG, "alloc digits fail, length=%hhu", other.d_.len_);
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else {
|
|
MEMCPY(digits, other.digits_, other.d_.len_ * ITEM_SIZE(digits));
|
|
d_ = other.d_;
|
|
d_.reserved_ = 0; // keep always 0.
|
|
digits_ = digits;
|
|
// OB_LOG(DEBUG, "deep_copy_v3", K(other));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::deep_copy_to_allocator_(ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber tmp;
|
|
if (OB_FAIL(tmp.from(*this, allocator))) {
|
|
LOG_WARN("failed: depp copy *this to tmp with given allocator",
|
|
KPC(this), K(tmp), K(ret));
|
|
} else {
|
|
*this = tmp;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::from_(const ObNumber &other, uint32_t *digits)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (&other == this) {
|
|
// assign to self
|
|
} else if (0 == other.d_.len_) {
|
|
set_zero();
|
|
} else if (OB_UNLIKELY(NULL == digits || MAX_STORE_LEN < other.d_.len_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else {
|
|
digits_ = digits;
|
|
MEMCPY(digits_, other.digits_, other.d_.len_ * ITEM_SIZE(digits_));
|
|
d_ = other.d_;
|
|
d_.reserved_ = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::floor(const int64_t scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int int_len = 0;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
if (OB_UNLIKELY(0 != scale)) {
|
|
LOG_WARN("invalid param", K(scale));
|
|
ret = OB_INVALID_ARGUMENT;
|
|
} else if (is_zero()) {
|
|
ret = OB_SUCCESS;
|
|
} else if (OB_UNLIKELY(NULL == digits_
|
|
|| 0 >= d_.len_)) {
|
|
ret = OB_NOT_INIT;
|
|
} else {
|
|
if (POSITIVE == d_.sign_) {
|
|
int_len = d_.se_ - POSITIVE_EXP_BOUNDARY;
|
|
if (int_len >= 0) {
|
|
int_len += 1;
|
|
} else {
|
|
int_len = -1;
|
|
}
|
|
} else if (NEGATIVE == d_.sign_) {
|
|
int_len = NEGATIVE_EXP_BOUNDARY - d_.se_;
|
|
if (int_len >= 0) {
|
|
int_len += 1;
|
|
} else {
|
|
int_len = -1;
|
|
}
|
|
} else { /* Do nothing */ }
|
|
if (int_len > 0 && int_len < d_.len_) {
|
|
if (POSITIVE == d_.sign_) {
|
|
for (int64_t i = int_len; i < d_.len_; ++i) {
|
|
digits_[i] = 0;
|
|
}
|
|
} else if (NEGATIVE == d_.sign_) {
|
|
for (int64_t i = int_len; i < d_.len_; ++i) {
|
|
digits_[i] = 0;
|
|
}
|
|
for (int64_t i = int_len - 1; i >= 0; --i) {
|
|
if ((BASE - 1) != digits_[i]) {
|
|
digits_[i] += 1;
|
|
break;
|
|
} else {
|
|
digits_[i] = 0;
|
|
}
|
|
}
|
|
if (0 == digits_[0] && NEGATIVE == d_.sign_) {
|
|
for (int64_t i = (int_len - 1); i >= 0; --i) {
|
|
digits_[i + 1] = digits_[i];
|
|
}
|
|
digits_[0] = 1;
|
|
--d_.exp_;
|
|
}
|
|
} else { /* Do nothing */ }
|
|
if (int_len < MAX_STORE_LEN) {
|
|
d_.len_ = static_cast<unsigned char>(int_len);
|
|
}
|
|
} else if (-1 == int_len && OB_SUCCESS == ret) {
|
|
for (int64_t i = 0; i < d_.len_; ++i) {
|
|
digits_[i] = 0;
|
|
}
|
|
if (POSITIVE == d_.sign_) {
|
|
set_zero();
|
|
} else if (NEGATIVE == d_.sign_) {
|
|
digits_[0] = 1;
|
|
d_.se_ = 0x40;
|
|
d_.len_ = 1;
|
|
}
|
|
} else { /* Do nothing */ }
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::ceil(const int64_t scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int int_len = 0;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
if (OB_UNLIKELY(0 != scale)) {
|
|
LOG_WARN("invalid param", K(scale));
|
|
ret = OB_INVALID_ARGUMENT;
|
|
} else if (is_zero()) {
|
|
ret = OB_SUCCESS;
|
|
} else if (OB_UNLIKELY(NULL == digits_
|
|
|| 0 >= d_.len_)) {
|
|
ret = OB_NOT_INIT;
|
|
} else {
|
|
if (POSITIVE == d_.sign_) {
|
|
int_len = d_.se_ - POSITIVE_EXP_BOUNDARY;
|
|
if (int_len >= 0) {
|
|
int_len += 1;
|
|
} else {
|
|
int_len = -1;
|
|
}
|
|
} else if (NEGATIVE == d_.sign_) {
|
|
int_len = NEGATIVE_EXP_BOUNDARY - d_.se_;
|
|
if (int_len >= 0) {
|
|
int_len += 1;
|
|
} else {
|
|
int_len = -1;
|
|
}
|
|
} else { /* Do nothing */ }
|
|
if (int_len > 0 && int_len < d_.len_) {
|
|
for (int64_t i = int_len; i < d_.len_; ++i) {
|
|
digits_[i] = 0;
|
|
}
|
|
d_.len_ = static_cast<uint8_t>(int_len);
|
|
if (POSITIVE == d_.sign_) {
|
|
for (int64_t i = int_len - 1; i >= 0; --i) {
|
|
if ((BASE - 1) != digits_[i]) {
|
|
digits_[i] += 1;
|
|
break;
|
|
} else {
|
|
digits_[i] = 0;
|
|
}
|
|
}
|
|
if (0 == digits_[0]) {
|
|
// since int_len is less than len_, so the loop below is safe,
|
|
// we won't get a buffer overflow problem.
|
|
for (int64_t i = (int_len - 1); i >= 0; --i) {
|
|
digits_[i + 1] = digits_[i];
|
|
}
|
|
digits_[0] = 1;
|
|
++d_.len_;
|
|
++d_.exp_;
|
|
}
|
|
}
|
|
} else if (int_len < 0) {
|
|
for (int64_t i = 0; i < d_.len_; ++i) {
|
|
digits_[i] = 0;
|
|
}
|
|
if (POSITIVE == d_.sign_) {
|
|
digits_[0] = 1;
|
|
d_.se_ = 0xc0;
|
|
d_.len_ = 1;
|
|
} else if (NEGATIVE == d_.sign_) {
|
|
set_zero();
|
|
}
|
|
} else { /* Do nothing */ }
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::trunc(const int64_t scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
|
|
if (OB_UNLIKELY(MIN_SCALE > scale
|
|
|| MAX_SCALE < scale)) {
|
|
LOG_WARN("invalid param", K(scale));
|
|
ret = OB_INVALID_ARGUMENT;
|
|
} else if (is_zero()) {
|
|
ret = OB_SUCCESS;
|
|
} else if (OB_UNLIKELY(NULL == digits_
|
|
|| 0 >= d_.len_)) {
|
|
ret = OB_NOT_INIT;
|
|
} else if (OB_FAIL(trunc_scale_(scale, false))) {
|
|
LOG_WARN("trunc scale failed", K(*this), K(ret));
|
|
} else {
|
|
LOG_DEBUG("trunc scale succ", K(*this), K(scale));
|
|
// do nothing
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::round_v1(const int64_t scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
if (is_zero()) {
|
|
ret = OB_SUCCESS;
|
|
} else if (OB_UNLIKELY(NULL == digits_
|
|
|| 0 >= d_.len_)) {
|
|
ret = OB_NOT_INIT;
|
|
} else if (is_oracle_mode()) {
|
|
if (OB_FAIL(round_scale_oracle_(scale, false))) {
|
|
//_OB_LOG(WARN, "Buffer overflow, %s", to_cstring(*this));
|
|
}
|
|
} else {
|
|
if (OB_UNLIKELY(MIN_SCALE > scale
|
|
|| MAX_SCALE < scale)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid param", K(scale), K(ret));
|
|
} else if (OB_FAIL(round_scale_(scale, false))) {
|
|
//_OB_LOG(WARN, "Buffer overflow, %s", to_cstring(*this));
|
|
} else {
|
|
// do nothing
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::check_and_round(const int64_t precision, const int64_t scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObTRecover recover_guard(*this, OB_SUCCESS, ret);
|
|
|
|
if (is_zero()) {
|
|
ret = OB_SUCCESS;
|
|
} else if (OB_UNLIKELY(NULL == digits_
|
|
|| 0 >= d_.len_)) {
|
|
ret = OB_NOT_INIT;
|
|
} else if (INT64_MAX != precision
|
|
&& OB_FAIL(round_scale_v3_(scale, false, false))) {
|
|
//_OB_LOG(WARN, "Buffer overflow, %s", to_cstring(*this));
|
|
} else if (INT64_MAX != precision
|
|
&& INT64_MAX != scale
|
|
&& OB_FAIL(check_precision_(precision, scale))) {
|
|
// _OB_LOG(WARN, "Precision overflow, %s", to_cstring(*this));
|
|
} else {
|
|
// do nothing
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int64_t ObNumber::to_string(char *buffer, const int64_t length) const
|
|
{
|
|
int64_t pos = 0;
|
|
if (d_.len_ > 0 && OB_ISNULL(digits_)) {
|
|
databuff_printf(buffer, length, pos, "WARN the pointer is null");
|
|
} else {
|
|
databuff_printf(buffer, length, pos, "\"sign=%hhu exp=%hhu se=0x%hhx len=%hhu digits=[", d_.sign_,
|
|
d_.exp_, d_.se_, d_.len_);
|
|
for (uint8_t i = 0; i < d_.len_; ++i) {
|
|
databuff_printf(buffer, length, pos, "%u,", digits_[i]);
|
|
}
|
|
databuff_printf(buffer, length, pos, "]\"");
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
bool ObNumber::is_valid_uint64(uint64_t &uint64) const
|
|
{
|
|
bool bret = false;
|
|
uint64_t tmp_int_parts = 0;
|
|
uint64_t tmp_decimal_parts = 0;
|
|
if (OB_UNLIKELY(OB_SUCCESS != check_range(&bret, NULL, tmp_int_parts, tmp_decimal_parts))) {
|
|
LOG_WARN_RET(OB_ERROR, "can't to check the param range", K(bret));
|
|
} else {
|
|
uint64 = tmp_int_parts;
|
|
bret = bret && (0 == tmp_decimal_parts);//Should not use if-test.
|
|
}
|
|
|
|
return bret;
|
|
}
|
|
|
|
bool ObNumber::is_valid_int64(int64_t &int64) const
|
|
{
|
|
bool bret = false;
|
|
uint64_t tmp_int_parts = 0;
|
|
uint64_t tmp_decimal_parts = 0;
|
|
if (OB_UNLIKELY(OB_SUCCESS != check_range(NULL, &bret, tmp_int_parts, tmp_decimal_parts))) {
|
|
LOG_WARN_RET(OB_ERROR, "can't to check the param range", K(bret));
|
|
} else {
|
|
int64 = is_negative() ? (-1 * tmp_int_parts) : tmp_int_parts;
|
|
bret = bret && (0 == tmp_decimal_parts);
|
|
}
|
|
|
|
return bret;
|
|
}
|
|
|
|
// 小数点儿后全部截断
|
|
int ObNumber::extract_valid_int64_with_trunc(int64_t &value) const
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (!is_valid_int64(value)) {
|
|
number::ObNumber tmp_number;
|
|
char buf_alloc[ObNumber::MAX_BYTE_LEN];
|
|
ObDataBuffer allocator(buf_alloc, ObNumber::MAX_BYTE_LEN);
|
|
//need deep copy before round
|
|
if (OB_FAIL(tmp_number.from(*this, allocator))) {
|
|
LOG_WARN("fail to deep_copy", K(ret), K(tmp_number));
|
|
} else if (OB_FAIL(tmp_number.trunc(0))) {
|
|
LOG_WARN("fail to trunc", K(ret), K(tmp_number));
|
|
} else if (!tmp_number.is_valid_int64(value)) {
|
|
ret = OB_DATA_OUT_OF_RANGE;
|
|
LOG_WARN("invalid const type for array index", K(tmp_number), K(ret));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::extract_valid_uint64_with_trunc(uint64_t &value) const
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (!is_valid_uint64(value)) {
|
|
number::ObNumber tmp_number;
|
|
char buf_alloc[ObNumber::MAX_BYTE_LEN];
|
|
ObDataBuffer allocator(buf_alloc, ObNumber::MAX_BYTE_LEN);
|
|
//need deep copy before round
|
|
if (OB_FAIL(tmp_number.from(*this, allocator))) {
|
|
LOG_WARN("fail to deep_copy", K(ret), K(tmp_number));
|
|
} else if (OB_FAIL(tmp_number.trunc(0))) {
|
|
LOG_WARN("fail to trunc", K(ret), K(tmp_number));
|
|
} else if (!tmp_number.is_valid_uint64(value)) {
|
|
ret = OB_DATA_OUT_OF_RANGE;
|
|
LOG_WARN("invalid const type for array index", K(tmp_number), K(ret));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::extract_valid_int64_with_round(int64_t &value) const
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (!is_valid_int64(value)) {
|
|
number::ObNumber tmp_number;
|
|
char buf_alloc[ObNumber::MAX_BYTE_LEN];
|
|
ObDataBuffer allocator(buf_alloc, ObNumber::MAX_BYTE_LEN);
|
|
//need deep copy before round
|
|
if (OB_FAIL(tmp_number.from(*this, allocator))) {
|
|
LOG_WARN("fail to deep_copy", K(ret), K(tmp_number));
|
|
} else if (OB_FAIL(tmp_number.round(0))) {
|
|
LOG_WARN("fail to trunc", K(ret), K(tmp_number));
|
|
} else if (!tmp_number.is_valid_int64(value)) {
|
|
ret = OB_DATA_OUT_OF_RANGE;
|
|
LOG_WARN("invalid const type for array index", K(tmp_number), K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::extract_valid_uint64_with_round(uint64_t &value) const
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (!is_valid_uint64(value)) {
|
|
number::ObNumber tmp_number;
|
|
char buf_alloc[ObNumber::MAX_BYTE_LEN];
|
|
ObDataBuffer allocator(buf_alloc, ObNumber::MAX_BYTE_LEN);
|
|
//need deep copy before round
|
|
if (OB_FAIL(tmp_number.from(*this, allocator))) {
|
|
LOG_WARN("fail to deep_copy", K(ret), K(tmp_number));
|
|
} else if (OB_FAIL(tmp_number.round(0))) {
|
|
LOG_WARN("fail to trunc", K(ret), K(tmp_number));
|
|
} else if (!tmp_number.is_valid_uint64(value)) {
|
|
ret = OB_DATA_OUT_OF_RANGE;
|
|
LOG_WARN("invalid const type for array index", K(tmp_number), K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::width_bucket(const ObNumber &start, const ObNumber &end, const ObNumber &bucket, ObNumber &value, ObIAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber bucket_num, res;
|
|
char buf_bucket[ObNumber::MAX_BYTE_LEN];
|
|
char buf_res[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc1[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc2[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc3[ObNumber::MAX_BYTE_LEN];
|
|
ObDataBuffer allocator_bucket(buf_bucket, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator_res(buf_res, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator1(buf_alloc1, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator2(buf_alloc2, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator3(buf_alloc3, ObNumber::MAX_BYTE_LEN);
|
|
if (OB_FAIL(bucket_num.from(bucket, allocator_bucket))) {
|
|
LOG_WARN("copy bucket number failed", K(bucket_num), K(ret));
|
|
} else if (OB_FAIL(bucket_num.floor(0))) {
|
|
LOG_WARN("bucket floor failed", K(bucket_num), K(ret));
|
|
} else if (OB_UNLIKELY(bucket_num.is_zero() || bucket_num.is_negative())) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("bucket < 1", K(bucket_num), K(ret));
|
|
} else {
|
|
ObNumber range,offset;
|
|
if (start == end) {
|
|
if (*this < start) {
|
|
if (OB_FAIL(res.from(static_cast<int64_t>(0), allocator_res))) {
|
|
LOG_WARN("create number 0 failed", K(res), K(ret));
|
|
}
|
|
} else {
|
|
ObNumber temp;
|
|
if(OB_FAIL(temp.from(static_cast<int64_t>(1), allocator1))) {
|
|
LOG_WARN("create number 1 failed", K(res), K(temp), K(ret));
|
|
} else if (OB_FAIL(bucket_num.add(temp, res, allocator_res))) {
|
|
LOG_WARN("bucket add 1 failed", K(bucket_num), K(res), K(ret));
|
|
}
|
|
}
|
|
} else {
|
|
if (start > end) {
|
|
if (OB_FAIL(start.sub(end, range, allocator1))) {
|
|
LOG_WARN("start sub end failed", K(start), K(end), K(range), K(ret));
|
|
} else if(OB_FAIL(start.sub(*this, offset, allocator2))) {
|
|
LOG_WARN("start sub target failed", K(start), K(offset), K(ret));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(end.sub(start, range, allocator1))) {
|
|
LOG_WARN("end sub start failed", K(start), K(end), K(range), K(ret));
|
|
} else if(OB_FAIL(sub(start, offset, allocator2))) {
|
|
LOG_WARN("end sub target failed", K(start), K(offset), K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (offset.is_negative()) {
|
|
if (OB_FAIL(res.from(static_cast<int64_t>(0), allocator_res))) {
|
|
LOG_WARN("assign 0 to res failed", K(res), K(ret));
|
|
}
|
|
} else if (offset >= range) {
|
|
ObNumber temp;
|
|
if (OB_FAIL(temp.from(static_cast<int64_t>(1), allocator3))) {
|
|
LOG_WARN("create number 1 failed", K(temp), K(ret));
|
|
} else if (OB_FAIL(bucket_num.add(temp, res, allocator_res))) {
|
|
LOG_WARN("bucket add 1 failed", K(bucket_num), K(temp), K(res), K(ret));
|
|
}
|
|
} else {
|
|
ObNumber q;
|
|
if (OB_FAIL(range.div(bucket_num, q, allocator3))) {
|
|
LOG_WARN("width_bucket:range div bucket failed", K(range), K(bucket_num), K(q), K(ret));
|
|
} else if (OB_FAIL(offset.div(q, res, allocator_res))) {
|
|
LOG_WARN("width_bucket:offset div q failed", K(offset), K(q), K(res), K(ret));
|
|
} else if (OB_FAIL(res.floor(0))) {
|
|
LOG_WARN("width_bucket:value floor failed", K(res), K(ret));
|
|
} else {
|
|
ObNumber temp;
|
|
allocator1.free();
|
|
allocator2.free();
|
|
std::swap(allocator2, allocator_res);
|
|
if (OB_FAIL(temp.from(static_cast<int64_t>(1), allocator1))) {
|
|
LOG_WARN("create number 1 failed", K(temp), K(ret));
|
|
} else if (OB_FAIL(res.add(temp, res, allocator_res))) {
|
|
LOG_WARN("value add 1 failed", K(res), K(temp), K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(value.from(res, allocator))) {
|
|
LOG_WARN("copy res failed", K(value), K(res), K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ObNumber::is_valid_int() const
|
|
{
|
|
bool bret = false;
|
|
uint64_t tmp_int_parts = 0;
|
|
uint64_t tmp_decimal_parts = 0;
|
|
if (OB_UNLIKELY(OB_SUCCESS != check_range(NULL, &bret, tmp_int_parts, tmp_decimal_parts))) {
|
|
LOG_WARN_RET(OB_ERROR, "can't to check the param range", K(bret));
|
|
} else {
|
|
bret = bret && (0 == tmp_decimal_parts);
|
|
}
|
|
|
|
return bret;
|
|
}
|
|
|
|
bool ObNumber::is_int_parts_valid_int64(int64_t &int_parts, int64_t &decimal_parts) const
|
|
{
|
|
bool bret = false;
|
|
uint64_t tmp_int_parts = 0;
|
|
uint64_t tmp_decimal_parts = 0;
|
|
if (OB_UNLIKELY(OB_SUCCESS != check_range(NULL, &bret, tmp_int_parts, tmp_decimal_parts))) {
|
|
LOG_WARN_RET(OB_ERROR, "can't to check the param range", K(bret));
|
|
} else {
|
|
decimal_parts = tmp_decimal_parts;
|
|
int_parts = is_negative() ? (-1 * tmp_int_parts) : tmp_int_parts;
|
|
}
|
|
LOG_DEBUG("is int parts valid int64", K(*this), K(int_parts), K(decimal_parts));
|
|
return bret;
|
|
}
|
|
|
|
int ObNumber::check_range(bool *is_valid_uint64, bool *is_valid_int64,
|
|
uint64_t &int_parts, uint64_t &decimal_parts) const
|
|
{
|
|
/*
|
|
* 为了注释能短点,这回就用中文写了。不是我英文水平不行啊。^_^
|
|
* 对于一个负数,它绝对不可能是一个合法的uint64
|
|
* -value < -x 等价于 value > x 因此,取绝对值来判断
|
|
* 对于一个正数,uint64的范围比int64范围大。
|
|
* 这几种情况,上限都不同,放到了THRESHOLD数组里。
|
|
* 正数、负数;uint64、int64;四种组合情况
|
|
* 其中负数与uint64这种情况直接枪毙,因此THRESHOLD只有三个upperbound。
|
|
*
|
|
*
|
|
*/
|
|
|
|
int ret = OB_SUCCESS;
|
|
if (OB_UNLIKELY((NULL == is_valid_uint64) && (NULL == is_valid_int64))) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the param is invalid", K(ret));
|
|
} else {
|
|
bool *is_valid = NULL;
|
|
static const int FLAG_INT64 = 0;
|
|
static const int FLAG_UINT64 = 1;
|
|
static const int SIGN_NEG = 0;
|
|
static const int SIGN_POS = 1;
|
|
int flag = FLAG_INT64;
|
|
if (NULL != is_valid_uint64) {
|
|
is_valid = is_valid_uint64;
|
|
flag = FLAG_UINT64;
|
|
} else {
|
|
is_valid = is_valid_int64;
|
|
}
|
|
*is_valid = true;
|
|
if (OB_UNLIKELY(is_zero())) {
|
|
//do nothing
|
|
} else {
|
|
int sign = is_negative() ? SIGN_NEG : SIGN_POS;
|
|
if ((SIGN_NEG == sign) && (FLAG_UINT64 == flag)) {
|
|
*is_valid = false; //there are no neg guys in uint64 !
|
|
}
|
|
uint64_t value = 0;
|
|
static uint64_t ABS_INT64_MIN = 9223372036854775808ULL;
|
|
//int64 uint64
|
|
static uint64_t THRESHOLD[2][2] = {{ABS_INT64_MIN, 0}, //neg
|
|
{INT64_MAX, UINT64_MAX}}; //pos
|
|
ObDigitIterator di;
|
|
di.assign(d_.desc_, digits_);
|
|
uint32_t digit = 0;
|
|
bool from_integer = false;
|
|
bool last_decimal = false;
|
|
while (OB_SUCC(di.get_next_digit(digit, from_integer, last_decimal))) {
|
|
if (from_integer) {
|
|
//In case of overflow we can not write "value * BASE + digit <= THRESHOLD[index]"
|
|
if (*is_valid && value <= (THRESHOLD[sign][flag] - digit) / BASE) {
|
|
value = value * BASE + digit;
|
|
} else {
|
|
*is_valid = false; //no break
|
|
}
|
|
} else {
|
|
decimal_parts = digit;
|
|
break;
|
|
}
|
|
}
|
|
ret = (OB_ITER_END == ret) ? OB_SUCCESS : ret;
|
|
int_parts = value; //no matter valid or not, we set int_parts always here.
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::check_precision_(const int64_t precision, const int64_t scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t limit = precision - scale;
|
|
ObDigitIterator di;
|
|
di.assign(d_.desc_, digits_);
|
|
int64_t integer_counter = 0;
|
|
int64_t decimal_zero_counter = 0;
|
|
uint32_t digit = 0;
|
|
bool head_flag = true;
|
|
bool from_integer = false;
|
|
bool last_decimal = false;
|
|
while (OB_SUCCESS == (ret = di.get_next_digit(digit, from_integer, last_decimal))) {
|
|
if (from_integer) {
|
|
if (head_flag) {
|
|
integer_counter += get_digit_len(digit);
|
|
} else {
|
|
integer_counter += DIGIT_LEN;
|
|
}
|
|
if (OB_UNLIKELY(integer_counter > limit)) {
|
|
_OB_LOG(WARN, "Precision=%ld scale=%ld integer_number=%ld precision overflow %s",
|
|
precision, scale, integer_counter, to_cstring(*this));
|
|
ret = OB_INTEGER_PRECISION_OVERFLOW;
|
|
break;
|
|
}
|
|
} else {
|
|
if (0 != digit) {
|
|
decimal_zero_counter += (DIGIT_LEN - get_digit_len(digit));
|
|
} else {
|
|
decimal_zero_counter += DIGIT_LEN;
|
|
}
|
|
if (decimal_zero_counter >= -limit) {
|
|
break;
|
|
}
|
|
}
|
|
head_flag = false;
|
|
}
|
|
ret = (OB_ITER_END == ret) ? OB_SUCCESS : ret;
|
|
if (OB_UNLIKELY(OB_SUCCESS == ret
|
|
&& decimal_zero_counter < -limit)) {
|
|
LOG_WARN("precision overflow ",
|
|
K(precision), K(scale), K(decimal_zero_counter), K(*this));
|
|
ret = OB_DECIMAL_PRECISION_OVERFLOW;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::round_scale_(const int64_t scale, const bool using_floating_scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObDigitIterator di;
|
|
di.assign(d_.desc_, digits_);
|
|
|
|
uint32_t digit = 0;
|
|
bool from_integer = false;
|
|
bool last_decimal = false;
|
|
uint32_t integer_digits[MAX_CALC_LEN];
|
|
int64_t integer_length = 0;
|
|
int64_t integer_counter = 0;
|
|
uint32_t decimal_digits[MAX_CALC_LEN];
|
|
int64_t decimal_length = 0;
|
|
while (OB_SUCC(di.get_next_digit(digit, from_integer, last_decimal))) {
|
|
if (OB_UNLIKELY(MAX_CALC_LEN <= integer_length
|
|
|| MAX_CALC_LEN <= decimal_length)) {
|
|
LOG_WARN("buffer size overflow",
|
|
K(integer_length), K(decimal_length));
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
break;
|
|
}
|
|
if (from_integer) {
|
|
if (0 == integer_length) {
|
|
integer_counter += get_digit_len(digit);
|
|
} else {
|
|
integer_counter += DIGIT_LEN;
|
|
}
|
|
|
|
integer_digits[integer_length++] = digit;
|
|
} else if (0 <= scale) {
|
|
if (using_floating_scale
|
|
&& 0 == digit
|
|
&& 0 == integer_length
|
|
&& 0 == decimal_length) {
|
|
continue;
|
|
}
|
|
decimal_digits[decimal_length++] = digit;
|
|
if ((decimal_length * DIGIT_LEN) > scale) {
|
|
// LOG_DEBUG("Number need round decimal", K(decimal_length), K(scale), K(integer_counter));
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
ret = (OB_ITER_END == ret) ? OB_SUCCESS : ret;
|
|
int64_t floating_scale = scale;
|
|
if (OB_SUCCESS == ret
|
|
&& using_floating_scale) {
|
|
floating_scale -= integer_counter;
|
|
}
|
|
// LOG_DEBUG("Number before rebuild", K(is_oracle_mode()), K(using_floating_scale),
|
|
// K(scale), K(floating_scale), K(integer_counter), K(integer_length),
|
|
// K(decimal_length), KPC(this), K(lbt()));
|
|
|
|
if (OB_FAIL(ret)) {
|
|
// do nothing
|
|
} else if (0 < floating_scale) {
|
|
if (OB_FAIL(round_decimal_(floating_scale, decimal_digits, decimal_length))) {
|
|
LOG_ERROR("fail to get round_decimal");
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, decimal_digits, decimal_length);
|
|
}
|
|
} else if (0 > floating_scale) {
|
|
if (OB_FAIL(round_integer_(-floating_scale, integer_digits, integer_length))) {
|
|
LOG_ERROR("fail to get round_integer");
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, NULL, 0);
|
|
}
|
|
|
|
} else if (0 == floating_scale) {
|
|
if (0 < decimal_length) {
|
|
if ((5 * BASE / 10) <= decimal_digits[0]) {
|
|
decimal_digits[0] = BASE;
|
|
} else {
|
|
decimal_digits[0] = 0;
|
|
}
|
|
ret = rebuild_digits_(integer_digits, integer_length, decimal_digits, 1);
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, NULL, 0);
|
|
}
|
|
} else { /* Do nothing */ }
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::round_scale_v2_(const int64_t scale, const bool using_floating_scale,
|
|
const bool for_oracle_to_char,
|
|
int16_t *res_precision/*NULL*/, int16_t *res_scale/*NULL*/)
|
|
{
|
|
static const uint64_t ROUND_POWS_DESC[] = {
|
|
1000000000,
|
|
100000000,
|
|
10000000,
|
|
1000000,
|
|
100000,
|
|
10000,
|
|
1000,
|
|
100,
|
|
10,
|
|
};
|
|
|
|
static const uint64_t ROUND_POWS_ASC[] = {
|
|
1000000000,
|
|
10,
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000,
|
|
1000000,
|
|
10000000,
|
|
100000000
|
|
};
|
|
|
|
int ret = OB_SUCCESS;
|
|
if (is_zero()) {
|
|
//do nothing
|
|
} else {
|
|
const int64_t expr_value = get_decode_exp(d_);
|
|
//xxx_length means xx digit array length
|
|
const int64_t integer_length = (expr_value >= 0 ? (expr_value + 1) : 0);
|
|
//e.g. 1,000000000,000000000, digits=[1], expr_value=2, len_=1, integer_length=3, valid_integer_length=1
|
|
const int64_t valid_integer_length = (d_.len_ > integer_length ? integer_length : d_.len_);
|
|
//xxx_count means xx digit count
|
|
const int64_t integer_count = (valid_integer_length > 0 ? (get_digit_len_v2(digits_[0]) + (integer_length - 1) * DIGIT_LEN) : 0);
|
|
|
|
//len_ > integer_length means have decimal
|
|
const int64_t valid_decimal_length = (d_.len_ > integer_length ? (d_.len_ - integer_length) : 0);
|
|
//e.g. 0.000000000 000000001, digits=[1], expr_value=-2, len_=1, decimal_length=2, valid_decimal_length=1
|
|
const int64_t decimal_length = valid_decimal_length + ((0 == integer_length && !using_floating_scale) ? (0 - expr_value - 1) : 0);
|
|
|
|
int64_t floating_scale = scale;
|
|
if (is_oracle_mode()) {
|
|
const int64_t decimal_prefix_zero_count = ((0 == integer_length) ? (0 - expr_value - 1 + DIGIT_LEN - get_digit_len_v2(digits_[0])) : 0);
|
|
int64_t valid_precision = 0;
|
|
if (for_oracle_to_char) {
|
|
valid_precision = OB_MAX_NUMBER_PRECISION_INNER - 1 - (is_decimal() ? 1 : 0) - (is_negative() ? 1 : 0);
|
|
} else {
|
|
valid_precision = (OB_MAX_NUMBER_PRECISION_INNER - ((0 == integer_count ? decimal_prefix_zero_count : integer_count) % 2));
|
|
}
|
|
int64_t r_precision = 0;
|
|
int64_t r_scale = 0;
|
|
int64_t decimal_non_zero_count = 0;
|
|
int64_t tail_decimal_zero_count = 0;
|
|
if (valid_decimal_length > 0) {
|
|
remove_back_zero(digits_[d_.len_ - 1], tail_decimal_zero_count);
|
|
decimal_non_zero_count = (DIGIT_LEN * valid_decimal_length
|
|
- (0 == integer_length ? (DIGIT_LEN - get_digit_len_v2(digits_[0])) : 0)
|
|
- tail_decimal_zero_count);
|
|
}
|
|
const int64_t decimal_count = decimal_length * DIGIT_LEN - tail_decimal_zero_count;
|
|
|
|
if (using_floating_scale) {
|
|
if (integer_count > 0) {
|
|
floating_scale = valid_precision - integer_count;
|
|
} else if (decimal_non_zero_count > valid_precision) {
|
|
/* decimal_cnt - non-zero decimal digit + valid decimal number */
|
|
floating_scale = MIN(scale, (decimal_count - decimal_non_zero_count + valid_precision));
|
|
}
|
|
r_precision = PRECISION_UNKNOWN_YET;
|
|
r_scale = ORA_NUMBER_SCALE_UNKNOWN_YET;
|
|
} else {
|
|
if (integer_count > 0) {
|
|
if (scale > 0) {
|
|
/* 1..30th.123 scale = 30 floating_scale = 0 res_p = 30 res_s = 0
|
|
* 1..20th.123 scale = 10 floating_scale = 10 res_p = 20
|
|
* 1...42th.123 scale = 10 floating_scale = -2 res_s = 10 res_p = 40, res_s = -2
|
|
* 1.000..20th0123 scale = 22 floating_scale = 22 res_s = 22 res_p = 22*/
|
|
floating_scale = MIN(valid_precision - integer_count, scale);
|
|
r_precision = MIN(valid_precision, integer_count + decimal_count);
|
|
r_scale = floating_scale < 0 ? floating_scale : MIN(floating_scale, decimal_count);
|
|
} else {
|
|
/* 1234.345 scale = -2, res_p = 2 res_s = -2
|
|
* 1234.324 scale = -10 res_p = 0 res_s = -10
|
|
* 1...42th.123 scale = -1 res_p = 40, res_s = -2*/
|
|
r_precision = MIN(valid_precision, MAX(integer_count + scale, 0));
|
|
r_scale = MIN(integer_count - valid_precision, scale);
|
|
}
|
|
} else if (decimal_non_zero_count > valid_precision) {
|
|
/* total_length - non-zero decimal digit + valid decimal number */
|
|
floating_scale = MIN(scale, (decimal_count - decimal_non_zero_count + valid_precision));
|
|
/* 0.345 scale = -2, res_p = 2 res_s = -2
|
|
* 1234.324 scale = -10 res_p = 0 res_s = -10
|
|
* 1...42th.123 scale = -1 res_p = 40, res_s = -2*/
|
|
if (scale > 0) {
|
|
r_precision = MIN(valid_precision, decimal_non_zero_count - (scale - decimal_count));
|
|
} else {
|
|
r_precision = 0;
|
|
}
|
|
r_scale = floating_scale;
|
|
}
|
|
}
|
|
|
|
if (NULL != res_precision && NULL != res_scale) {
|
|
if (using_floating_scale
|
|
|| (r_precision <= valid_precision
|
|
&& r_precision >= 0
|
|
&& r_scale <= OB_MAX_NUMBER_SCALE
|
|
&& r_scale >= OB_MIN_NUMBER_SCALE)) {
|
|
*res_precision = static_cast<int16_t>(r_precision);
|
|
*res_scale = static_cast<int16_t>(r_scale);
|
|
} else {
|
|
LOG_WARN("got invalid precision or scale on oracle mode, use builded",
|
|
K(r_precision), K(r_scale));
|
|
}
|
|
}
|
|
LOG_DEBUG("Number before rebuild v2", K(is_oracle_mode()), K(using_floating_scale), K(scale),
|
|
K(floating_scale), K(expr_value), K(integer_length), K(valid_integer_length), K(integer_count),
|
|
K(decimal_length), K(valid_decimal_length), K(decimal_count), K(decimal_prefix_zero_count),
|
|
K(decimal_non_zero_count), K(r_precision), K(r_scale), KPC(this), KCSTRING(lbt()));
|
|
} else {
|
|
if (using_floating_scale && valid_integer_length > 0) {
|
|
floating_scale -= integer_count;
|
|
}
|
|
LOG_DEBUG("Number before rebuild v2", K(is_oracle_mode()), K(using_floating_scale), K(scale),
|
|
K(floating_scale), K(expr_value), K(integer_length), K(valid_integer_length),
|
|
K(decimal_length), K(valid_decimal_length), KPC(this), KCSTRING(lbt()));
|
|
}
|
|
|
|
bool need_normalize = false;
|
|
if (floating_scale > 0) {
|
|
//round decimal
|
|
const int64_t round_length = floating_scale / DIGIT_LEN;
|
|
if (round_length >= decimal_length) {
|
|
// do nothing
|
|
// LOG_DEBUG("with no round", K(decimal_length), K(round_length), K(valid_decimal_length));
|
|
} else if (decimal_length > valid_decimal_length
|
|
&& round_length < (decimal_length - valid_decimal_length)) {
|
|
//e.g. value = 0.000000000 000000000 000000001 000000002, digits=[1,2], expr_value=-3, len_=2,
|
|
// decimal_length=4, valid_decimal_length=2
|
|
//round(value, 8), round(value, 9), round(value, 17)
|
|
set_zero();
|
|
} else {
|
|
//e.g. value = 1.000000000 000000000 000000001 000000002, digits=[1,0,0,1,2], expr_value=0, len_=5,
|
|
// decimal_length=4, valid_decimal_length=4
|
|
const int64_t round = floating_scale - round_length * DIGIT_LEN;
|
|
d_.len_ -= (decimal_length - (round_length + 1));
|
|
uint32_t &digit_round = *(digits_ + d_.len_ - 1);
|
|
const uint64_t round_pows = ROUND_POWS_DESC[round];
|
|
const uint32_t residue = digit_round % round_pows;
|
|
need_normalize = true;
|
|
if (residue >= round_pows / 2) {
|
|
digit_round += (round_pows - residue);
|
|
} else {
|
|
digit_round -= residue;
|
|
}
|
|
}
|
|
} else if (floating_scale < 0) {
|
|
floating_scale = 0 - floating_scale;
|
|
const int64_t round_length = floating_scale / DIGIT_LEN;
|
|
const int64_t round = floating_scale - round_length * DIGIT_LEN;
|
|
int64_t round_integer_length = round_length + (0 == round ? 0 : 1);
|
|
if (integer_length < round_integer_length) {
|
|
set_zero();
|
|
} else if (integer_length > valid_integer_length
|
|
&& round_integer_length <= (integer_length - valid_integer_length)) {
|
|
//e.g. 123,000000456, 000000000,000000000,
|
|
// digits=[123, 456], expr_value=3, len_=2, integer_length=4, valid_integer_length=2
|
|
//round(value, -8),round(value, -18),
|
|
//do nothing
|
|
} else {
|
|
need_normalize = true;
|
|
d_.len_ = integer_length - round_integer_length + 1;
|
|
uint32_t &digit_round = *(digits_ + d_.len_ - 1);
|
|
const uint64_t round_pows = ROUND_POWS_ASC[round];
|
|
uint32_t residue = digit_round % round_pows;
|
|
if (residue >= round_pows / 2) {
|
|
digit_round += (round_pows - residue);
|
|
} else {
|
|
digit_round -= residue;
|
|
}
|
|
}
|
|
} else {
|
|
if (0 == decimal_length) {
|
|
//do nothing
|
|
} else if (valid_integer_length > 0) {
|
|
need_normalize = true;
|
|
d_.len_ = valid_integer_length;
|
|
uint32_t &digit_round = *(digits_ + valid_integer_length);
|
|
if (digit_round >= (BASE / 2)) {
|
|
digits_[d_.len_ - 1] += 1;
|
|
}
|
|
} else {
|
|
uint32_t &digit_round = *(digits_ + 0);
|
|
if (digit_round >= (BASE / 2) && get_decode_exp(d_) == -1) {
|
|
set_one();
|
|
} else {
|
|
set_zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_normalize) {
|
|
if (OB_FAIL(normalize_v2_(true, true))) {
|
|
LOG_ERROR("fail to normalize_v2_", KPC(this), K(ret));
|
|
}
|
|
}
|
|
}
|
|
LOG_DEBUG("finish round_scale_v2_", KPC(this));
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObNumber::round_scale_v3_(const int64_t scale, const bool using_floating_scale,
|
|
const bool for_oracle_to_char,
|
|
int16_t *res_precision/*NULL*/, int16_t *res_scale/*NULL*/)
|
|
{
|
|
static const uint64_t ROUND_POWS[] = {
|
|
1000000000,
|
|
100000000,
|
|
10000000,
|
|
1000000,
|
|
100000,
|
|
10000,
|
|
1000,
|
|
100,
|
|
10,
|
|
1000000000,
|
|
};
|
|
|
|
int ret = OB_SUCCESS;
|
|
if (is_zero()) {
|
|
//do nothing
|
|
} else {
|
|
LOG_DEBUG("before round_scale_v3_", KPC(this), K(scale), K(using_floating_scale), K(for_oracle_to_char));
|
|
const int64_t digit_0_len = get_digit_len_v2(digits_[0]);
|
|
const int64_t expr_value = get_decode_exp(d_);
|
|
//xxx_length means xx digit array length
|
|
const int64_t integer_length = (expr_value >= 0 ? (expr_value + 1) : 0);
|
|
//e.g. 1,000000000,000000000, digits=[1], expr_value=2, len_=1, integer_length=3, valid_integer_length=1
|
|
const int64_t valid_integer_length = (d_.len_ > integer_length ? integer_length : d_.len_);
|
|
//xxx_count means xx digit count
|
|
const int64_t integer_count = (valid_integer_length > 0 ? (digit_0_len + (integer_length - 1) * DIGIT_LEN) : 0);
|
|
|
|
//len_ > integer_length means have decimal
|
|
const int64_t valid_decimal_length = (d_.len_ > integer_length ? (d_.len_ - integer_length) : 0);
|
|
//e.g. 0.000000000 000000001, digits=[1], expr_value=-2, len_=1, decimal_length=2, valid_decimal_length=1
|
|
const int64_t decimal_length = valid_decimal_length + ((0 == integer_length && !using_floating_scale) ? (0 - expr_value - 1) : 0);
|
|
|
|
int64_t floating_scale = scale;
|
|
|
|
if (is_oracle_mode()) {
|
|
const int64_t decimal_prefix_zero_count = ((0 == integer_length) ? (0 - expr_value - 1 + DIGIT_LEN - digit_0_len) : 0);
|
|
int64_t valid_precision = 0;
|
|
if (for_oracle_to_char) {
|
|
// Todo: 当number小数部分包含前缀0时(e.g:0.00012345),oracle to_char 转换有效数字需包含前缀0,
|
|
// e.g:
|
|
// select cast(0.00012345678901234567890123456789012345678901111 as varchar(100)) from dual;
|
|
// Oracle result: .000123456789012345678901234567890123457
|
|
// oracle在to_char计算时,长度大于40的number会进行科学计数法转换,前缀0会去除。OB暂时没有兼容Oracle行为(科学计数法与精度联动),
|
|
// 因此在number to char转换出现前缀0时(decimal_prefix_zero_count > 0),保持与修改前一致
|
|
valid_precision = OB_MAX_NUMBER_PRECISION_INNER - is_negative()
|
|
- (has_decimal() ? (decimal_prefix_zero_count > 0 ? 2 : 1) : 0);
|
|
} else {
|
|
valid_precision = (OB_MAX_NUMBER_PRECISION_INNER - ((0 == integer_count ? decimal_prefix_zero_count : integer_count) % 2));
|
|
}
|
|
int64_t r_precision = 0;
|
|
int64_t r_scale = 0;
|
|
int64_t decimal_non_zero_count = 0;
|
|
int64_t tail_decimal_zero_count = 0;
|
|
if (valid_decimal_length > 0) {
|
|
remove_back_zero(digits_[d_.len_ - 1], tail_decimal_zero_count);
|
|
decimal_non_zero_count = (DIGIT_LEN * valid_decimal_length
|
|
- (0 == integer_length ? (DIGIT_LEN - digit_0_len) : 0)
|
|
- tail_decimal_zero_count);
|
|
}
|
|
const int64_t decimal_count = decimal_length * DIGIT_LEN - tail_decimal_zero_count;
|
|
|
|
if (using_floating_scale) {
|
|
if (integer_count > 0) {
|
|
floating_scale = valid_precision - integer_count;
|
|
} else if (decimal_non_zero_count > valid_precision) {
|
|
/* decimal_cnt - non-zero decimal digit + valid decimal number */
|
|
floating_scale = MIN(scale, (decimal_count - decimal_non_zero_count + valid_precision));
|
|
}
|
|
r_precision = PRECISION_UNKNOWN_YET;
|
|
r_scale = ORA_NUMBER_SCALE_UNKNOWN_YET;
|
|
} else {
|
|
if (integer_count > 0) {
|
|
if (scale > 0) {
|
|
/* 1..30th.123 scale = 30 floating_scale = 0 res_p = 30 res_s = 0
|
|
* 1..20th.123 scale = 10 floating_scale = 10 res_p = 20
|
|
* 1...42th.123 scale = 10 floating_scale = -2 res_s = 10 res_p = 40, res_s = -2
|
|
* 1.000..20th0123 scale = 22 floating_scale = 22 res_s = 22 res_p = 22*/
|
|
floating_scale = MIN(valid_precision - integer_count, scale);
|
|
r_precision = MIN(valid_precision, integer_count + decimal_count);
|
|
r_scale = floating_scale < 0 ? floating_scale : MIN(floating_scale, decimal_count);
|
|
} else {
|
|
/* 1234.345 scale = -2, res_p = 2 res_s = -2
|
|
* 1234.324 scale = -10 res_p = 0 res_s = -10
|
|
* 1...42th.123 scale = -1 res_p = 40, res_s = -2*/
|
|
r_precision = MIN(valid_precision, MAX(integer_count + scale, 0));
|
|
r_scale = MIN(integer_count - valid_precision, scale);
|
|
}
|
|
} else if (decimal_non_zero_count > valid_precision) {
|
|
/* total_length - non-zero decimal digit + valid decimal number */
|
|
floating_scale = MIN(scale, (decimal_count - decimal_non_zero_count + valid_precision));
|
|
/* 0.345 scale = -2, res_p = 2 res_s = -2
|
|
* 1234.324 scale = -10 res_p = 0 res_s = -10
|
|
* 1...42th.123 scale = -1 res_p = 40, res_s = -2*/
|
|
if (scale > 0) {
|
|
r_precision = MIN(valid_precision, decimal_non_zero_count - (scale - decimal_count));
|
|
} else {
|
|
r_precision = 0;
|
|
}
|
|
r_scale = floating_scale;
|
|
}
|
|
}
|
|
|
|
if (NULL != res_precision && NULL != res_scale) {
|
|
if (OB_LIKELY(using_floating_scale
|
|
|| (r_precision <= valid_precision
|
|
&& r_precision >= 0
|
|
&& r_scale <= OB_MAX_NUMBER_SCALE
|
|
&& r_scale >= OB_MIN_NUMBER_SCALE))) {
|
|
*res_precision = static_cast<int16_t>(r_precision);
|
|
*res_scale = static_cast<int16_t>(r_scale);
|
|
} else {
|
|
LOG_WARN("got invalid precision or scale on oracle mode, use builded",
|
|
K(r_precision), K(r_scale));
|
|
}
|
|
}
|
|
// LOG_DEBUG("Number before rebuild v2", K(is_oracle_mode()), K(using_floating_scale), K(scale),
|
|
// K(floating_scale), K(expr_value), K(integer_length), K(valid_integer_length), K(integer_count),
|
|
// K(decimal_length), K(valid_decimal_length), K(decimal_count), K(decimal_prefix_zero_count),
|
|
// K(decimal_non_zero_count), K(r_precision), K(r_scale), KPC(this), K(lbt()));
|
|
} else {
|
|
if (using_floating_scale && valid_integer_length > 0) {
|
|
floating_scale -= integer_count;
|
|
}
|
|
// LOG_DEBUG("Number before rebuild v3", K(is_oracle_mode()), K(using_floating_scale), K(scale),
|
|
// K(floating_scale), K(expr_value), K(integer_length), K(valid_integer_length),
|
|
// K(decimal_length), K(valid_decimal_length), KPC(this));
|
|
}
|
|
|
|
int32_t digit_id = OB_INVALID_INDEX;
|
|
int32_t pow_id = OB_INVALID_INDEX;
|
|
if (floating_scale > 0) {
|
|
//round decimal
|
|
const int64_t round_length = floating_scale / DIGIT_LEN;
|
|
if (round_length >= decimal_length) {
|
|
// do nothing
|
|
// LOG_DEBUG("with no round", K(decimal_length), K(round_length), K(valid_decimal_length));
|
|
} else if (decimal_length > valid_decimal_length
|
|
&& round_length < (decimal_length - valid_decimal_length)) {
|
|
//e.g. value = 0.000000000 000000000 000000001 000000002, digits=[1,2], expr_value=-3, len_=2,
|
|
// decimal_length=4, valid_decimal_length=2
|
|
//round(value, 8), round(value, 9), round(value, 17)
|
|
set_zero();
|
|
} else {
|
|
//e.g. value = 1.000000000 000000000 000000001 000000002, digits=[1,0,0,1,2], expr_value=0, len_=5,
|
|
// decimal_length=4, valid_decimal_length=4
|
|
const int64_t round = floating_scale - round_length * DIGIT_LEN;
|
|
d_.len_ -= (decimal_length - (round_length + 1));
|
|
digit_id = d_.len_ - 1;
|
|
pow_id = round;
|
|
}
|
|
} else if (floating_scale < 0) {
|
|
floating_scale = 0 - floating_scale;
|
|
const int64_t round_length = floating_scale / DIGIT_LEN;
|
|
const int64_t round = floating_scale - round_length * DIGIT_LEN;
|
|
int64_t round_integer_length = round_length + (0 == round ? 0 : 1);
|
|
if (integer_length < round_integer_length) {
|
|
set_zero();
|
|
} else if (integer_length > valid_integer_length
|
|
&& round_integer_length <= (integer_length - valid_integer_length)) {
|
|
//e.g. 123,000000456, 000000000,000000000,
|
|
// digits=[123, 456], expr_value=3, len_=2, integer_length=4, valid_integer_length=2
|
|
//round(value, -8),round(value, -18),
|
|
//do nothing
|
|
} else {
|
|
d_.len_ = integer_length - round_integer_length + 1;
|
|
digit_id = d_.len_ - 1;
|
|
pow_id = 9 - round;
|
|
}
|
|
} else {
|
|
if (0 == decimal_length) {
|
|
//do nothing
|
|
} else if (valid_integer_length > 0) {
|
|
d_.len_ = valid_integer_length;
|
|
digit_id = valid_integer_length;
|
|
pow_id = 0;
|
|
} else {
|
|
uint32_t &digit_round = *(digits_ + 0);
|
|
if (digit_round >= (BASE / 2) && get_decode_exp(d_) == -1) {
|
|
set_one();
|
|
} else {
|
|
set_zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (digit_id != OB_INVALID_INDEX && pow_id != OB_INVALID_INDEX) {
|
|
uint32_t &digit_value = *(digits_ + digit_id);
|
|
const uint64_t tmp_round_pows = ROUND_POWS[pow_id];
|
|
const uint32_t residue = digit_value % tmp_round_pows;
|
|
bool need_normalize = false;
|
|
if (residue >= tmp_round_pows / 2) {
|
|
digit_value += (ROUND_POWS[pow_id] - residue);
|
|
need_normalize = (digit_value == BASE);
|
|
} else {
|
|
digit_value -= residue;
|
|
if (digit_value == 0) {
|
|
uint32_t *tmp_digit = digits_ + d_.len_ - 1;
|
|
while (tmp_digit >= digits_ && 0 == *tmp_digit) {
|
|
--tmp_digit;
|
|
}
|
|
if (tmp_digit < digits_) {
|
|
set_zero();
|
|
} else {
|
|
d_.len_ = tmp_digit - digits_ + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_normalize) {
|
|
--digit_id;
|
|
while (digit_id >= 0) {
|
|
uint32_t &tmp_digit = digits_[digit_id];
|
|
if (tmp_digit == MAX_VALUED_DIGIT) {
|
|
--digit_id;
|
|
} else {
|
|
++tmp_digit;
|
|
break;
|
|
}
|
|
}
|
|
if (digit_id < 0) {
|
|
digits_[0] = 1;
|
|
d_.len_ = 1;
|
|
// check exp overflow ... ?
|
|
if (NEGATIVE == d_.sign_) {
|
|
--d_.exp_;
|
|
} else {
|
|
++d_.exp_;
|
|
}
|
|
} else {
|
|
d_.len_ = digit_id + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LOG_DEBUG("finish round_scale_v3_", KPC(this), K(scale), K(using_floating_scale), K(for_oracle_to_char));
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::round_precision(const int64_t precision)
|
|
{
|
|
static const uint64_t ROUND_POWS[] = {
|
|
1000000000,
|
|
100000000,
|
|
10000000,
|
|
1000000,
|
|
100000,
|
|
10000,
|
|
1000,
|
|
100,
|
|
10,
|
|
1000000000,
|
|
};
|
|
|
|
int ret = OB_SUCCESS;
|
|
if (OB_UNLIKELY(precision < 0)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("precision is neg, it should not happened", KPC(this), K(precision));
|
|
} else if (is_zero()) {
|
|
//do nothing
|
|
} else {
|
|
const int64_t digit_0_len = get_digit_len_v2(digits_[0]);
|
|
const int64_t expr_value = get_decode_exp(d_);
|
|
//xxx_length means xx digit array length
|
|
const int64_t integer_length = (expr_value >= 0 ? (expr_value + 1) : 0);
|
|
//e.g. 1,000000000,000000000, digits=[1], expr_value=2, len_=1, integer_length=3, valid_integer_length=1
|
|
const int64_t valid_integer_length = (d_.len_ > integer_length ? integer_length : d_.len_);
|
|
//xxx_count means xx digit count
|
|
const int64_t integer_count = (valid_integer_length > 0 ? (digit_0_len + (integer_length - 1) * DIGIT_LEN) : 0);
|
|
|
|
//len_ > integer_length means have decimal
|
|
const int64_t valid_decimal_length = (d_.len_ > integer_length ? (d_.len_ - integer_length) : 0);
|
|
//e.g. 0.000000000 000000001, digits=[1], expr_value=-2, len_=1, decimal_length=2, valid_decimal_length=1
|
|
const int64_t decimal_length = valid_decimal_length + ((0 == integer_length) ? (0 - expr_value - 1) : 0);
|
|
|
|
const int64_t decimal_prefix_zero_count = ((0 == integer_length) ? (0 - expr_value - 1 + DIGIT_LEN - digit_0_len) : 0);
|
|
int64_t decimal_non_zero_count = 0;
|
|
int64_t tail_decimal_zero_count = 0;
|
|
if (valid_decimal_length > 0) {
|
|
remove_back_zero(digits_[d_.len_ - 1], tail_decimal_zero_count);
|
|
decimal_non_zero_count = (DIGIT_LEN * valid_decimal_length
|
|
- (0 == integer_length ? (DIGIT_LEN - digit_0_len) : 0)
|
|
- tail_decimal_zero_count);
|
|
}
|
|
const int64_t decimal_count = decimal_length * DIGIT_LEN - tail_decimal_zero_count;
|
|
int64_t floating_scale = 0;
|
|
if (integer_count > 0) {
|
|
floating_scale = precision + 1 - integer_count;
|
|
} else {
|
|
floating_scale = (precision + 1) + (decimal_count - decimal_non_zero_count);
|
|
}
|
|
|
|
|
|
LOG_DEBUG("Number before round_precision", K(precision), K(floating_scale),
|
|
K(expr_value), K(integer_length), K(valid_integer_length), K(integer_count),
|
|
K(decimal_length), K(valid_decimal_length), K(decimal_count), K(decimal_prefix_zero_count),
|
|
K(decimal_non_zero_count), KPC(this));
|
|
|
|
int32_t digit_id = OB_INVALID_INDEX;
|
|
int32_t pow_id = OB_INVALID_INDEX;
|
|
if (floating_scale > 0) {
|
|
//round decimal
|
|
const int64_t round_length = floating_scale / DIGIT_LEN;
|
|
if (round_length >= decimal_length) {
|
|
// do nothing
|
|
// LOG_DEBUG("with no round", K(decimal_length), K(round_length), K(valid_decimal_length));
|
|
} else if (decimal_length > valid_decimal_length
|
|
&& round_length < (decimal_length - valid_decimal_length)) {
|
|
//e.g. value = 0.000000000 000000000 000000001 000000002, digits=[1,2], expr_value=-3, len_=2,
|
|
// decimal_length=4, valid_decimal_length=2
|
|
//round(value, 8), round(value, 9), round(value, 17)
|
|
set_zero();
|
|
} else {
|
|
//e.g. value = 1.000000000 000000000 000000001 000000002, digits=[1,0,0,1,2], expr_value=0, len_=5,
|
|
// decimal_length=4, valid_decimal_length=4
|
|
const int64_t round = floating_scale - round_length * DIGIT_LEN;
|
|
d_.len_ -= (decimal_length - (round_length + 1));
|
|
digit_id = d_.len_ - 1;
|
|
pow_id = round;
|
|
}
|
|
} else if (floating_scale < 0) {
|
|
floating_scale = 0 - floating_scale;
|
|
const int64_t round_length = floating_scale / DIGIT_LEN;
|
|
const int64_t round = floating_scale - round_length * DIGIT_LEN;
|
|
int64_t round_integer_length = round_length + (0 == round ? 0 : 1);
|
|
if (integer_length < round_integer_length) {
|
|
set_zero();
|
|
} else if (integer_length > valid_integer_length
|
|
&& round_integer_length <= (integer_length - valid_integer_length)) {
|
|
//e.g. 123,000000456, 000000000,000000000,
|
|
// digits=[123, 456], expr_value=3, len_=2, integer_length=4, valid_integer_length=2
|
|
//round(value, -8),round(value, -18),
|
|
//do nothing
|
|
} else {
|
|
d_.len_ = integer_length - round_integer_length + 1;
|
|
digit_id = d_.len_ - 1;
|
|
pow_id = 9 - round;
|
|
}
|
|
} else {
|
|
if (0 == decimal_length) {
|
|
//do nothing
|
|
} else if (valid_integer_length > 0) {
|
|
d_.len_ = valid_integer_length;
|
|
digit_id = valid_integer_length;
|
|
pow_id = 0;
|
|
} else {
|
|
uint32_t &digit_round = *(digits_ + 0);
|
|
if (digit_round >= (BASE / 2) && get_decode_exp(d_) == -1) {
|
|
set_one();
|
|
} else {
|
|
set_zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (digit_id != OB_INVALID_INDEX && pow_id != OB_INVALID_INDEX) {
|
|
uint32_t &digit_value = *(digits_ + digit_id);
|
|
const uint64_t tmp_round_pows = ROUND_POWS[pow_id];
|
|
const uint32_t residue = digit_value % tmp_round_pows;
|
|
bool need_normalize = false;
|
|
if (residue >= tmp_round_pows / 2) {
|
|
digit_value += (ROUND_POWS[pow_id] - residue);
|
|
need_normalize = (digit_value == BASE);
|
|
} else {
|
|
digit_value -= residue;
|
|
if (digit_value == 0) {
|
|
uint32_t *tmp_digit = digits_ + d_.len_ - 1;
|
|
while (tmp_digit >= digits_ && 0 == *tmp_digit) {
|
|
--tmp_digit;
|
|
}
|
|
if (tmp_digit < digits_) {
|
|
set_zero();
|
|
} else {
|
|
d_.len_ = tmp_digit - digits_ + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_normalize) {
|
|
--digit_id;
|
|
while (digit_id >= 0) {
|
|
uint32_t &tmp_digit = digits_[digit_id];
|
|
if (tmp_digit == MAX_VALUED_DIGIT) {
|
|
--digit_id;
|
|
} else {
|
|
++tmp_digit;
|
|
break;
|
|
}
|
|
}
|
|
if (digit_id < 0) {
|
|
digits_[0] = 1;
|
|
d_.len_ = 1;
|
|
// check exp overflow ... ?
|
|
if (NEGATIVE == d_.sign_) {
|
|
--d_.exp_;
|
|
} else {
|
|
++d_.exp_;
|
|
}
|
|
} else {
|
|
d_.len_ = digit_id + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LOG_DEBUG("finish round_precision", KPC(this), K(precision));
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
int ObNumber::round_scale_oracle_(const int64_t scale, const bool using_floating_scale,
|
|
int16_t *res_precision, int16_t *res_scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObDigitIterator di;
|
|
di.assign(d_.desc_, digits_);
|
|
|
|
uint32_t digit = 0;
|
|
bool from_integer = false;
|
|
bool last_decimal = false;
|
|
uint32_t integer_digits[MAX_CALC_LEN];
|
|
int64_t integer_length = 0;
|
|
int64_t integer_counter = 0;
|
|
bool integer_is_zero = true;
|
|
uint32_t decimal_digits[MAX_CALC_LEN];
|
|
int64_t decimal_length = 0;
|
|
int64_t decimal_not_zero_length = 0;
|
|
int64_t last_decimal_counter = 0;
|
|
int64_t valid_precision = OB_MAX_NUMBER_PRECISION_INNER;
|
|
int64_t r_precision = 0;
|
|
int64_t r_scale = 0;
|
|
int64_t decimal_prefix_zero = 0;
|
|
while (OB_SUCC(di.get_next_digit(digit, from_integer, last_decimal))) {
|
|
if (OB_UNLIKELY(MAX_CALC_LEN <= integer_length
|
|
|| MAX_CALC_LEN <= decimal_length)) {
|
|
LOG_WARN("buffer size overflow",
|
|
K(integer_length), K(decimal_length));
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
break;
|
|
}
|
|
if (from_integer) {
|
|
if (0 == integer_length) {
|
|
integer_counter += get_digit_len(digit);
|
|
} else {
|
|
integer_counter += DIGIT_LEN;
|
|
}
|
|
if (integer_is_zero && digit != 0) {
|
|
integer_is_zero = false;
|
|
}
|
|
integer_digits[integer_length++] = digit;
|
|
} else if (0 <= scale) {
|
|
if (0 == digit
|
|
&& 0 == integer_length
|
|
&& 0 == decimal_not_zero_length) {
|
|
decimal_prefix_zero += DIGIT_LEN;
|
|
if (using_floating_scale) {
|
|
continue;
|
|
}
|
|
} else {
|
|
if (0 == decimal_not_zero_length && !last_decimal) {
|
|
decimal_prefix_zero += DIGIT_LEN - get_digit_len(digit);
|
|
decimal_not_zero_length += get_digit_len(digit);
|
|
} else if (last_decimal) {
|
|
remove_back_zero(digit, last_decimal_counter);
|
|
decimal_not_zero_length += DIGIT_LEN - last_decimal_counter;
|
|
} else {
|
|
decimal_not_zero_length += DIGIT_LEN;
|
|
}
|
|
}
|
|
decimal_digits[decimal_length++] = digit;
|
|
if ((decimal_length * DIGIT_LEN) > (using_floating_scale ? (scale - integer_counter) : scale)) {
|
|
// LOG_DEBUG("Number need trunc decimal on oralce mode", K(decimal_length),
|
|
// K(scale), K(integer_counter), K(decimal_not_zero_length), K(using_floating_scale),
|
|
// K(decimal_prefix_zero));
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
ret = (OB_ITER_END == ret) ? OB_SUCCESS : ret;
|
|
valid_precision -= (0 == integer_counter ? decimal_prefix_zero : integer_counter) % 2;
|
|
int64_t decimal_cnt = decimal_length * DIGIT_LEN - last_decimal_counter;
|
|
LOG_DEBUG("Number process on oracle mode", K(ret), K(using_floating_scale), K(scale),
|
|
K(integer_counter), K(decimal_length), K(decimal_not_zero_length), K(valid_precision),
|
|
K(decimal_cnt), K(last_decimal_counter));
|
|
int64_t floating_scale = scale;
|
|
if (OB_SUCC(ret)) {
|
|
if (using_floating_scale) {
|
|
if (integer_counter > 0) {
|
|
floating_scale = valid_precision - integer_counter;
|
|
} else if (decimal_not_zero_length > valid_precision) {
|
|
/* decimal_cnt - non-zero decimal digit + valid decimal number */
|
|
floating_scale = MIN(scale,
|
|
(decimal_cnt - decimal_not_zero_length + valid_precision));
|
|
}
|
|
r_precision = PRECISION_UNKNOWN_YET;
|
|
r_scale = ORA_NUMBER_SCALE_UNKNOWN_YET;
|
|
} else {
|
|
if (integer_counter > 0) {
|
|
if (scale > 0) {
|
|
/* 1..30th.123 scale = 30 floating_scale = 0 res_p = 30 res_s = 0
|
|
* 1..20th.123 scale = 10 floating_scale = 10 res_p = 20
|
|
* 1...42th.123 scale = 10 floating_scale = -2 res_s = 10 res_p = 40, res_s = -2
|
|
* 1.000..20th0123 scale = 22 floating_scale = 22 res_s = 22 res_p = 22*/
|
|
floating_scale = MIN(valid_precision - integer_counter, scale);
|
|
r_precision = MIN(valid_precision, integer_counter + decimal_cnt);
|
|
r_scale = floating_scale < 0 ? floating_scale : MIN(floating_scale, decimal_cnt);
|
|
} else {
|
|
/* 1234.345 scale = -2, res_p = 2 res_s = -2
|
|
* 1234.324 scale = -10 res_p = 0 res_s = -10
|
|
* 1...42th.123 scale = -1 res_p = 40, res_s = -2*/
|
|
r_precision = MIN(valid_precision, MAX(integer_counter + scale, 0));
|
|
r_scale = MIN(integer_counter - valid_precision, scale);
|
|
}
|
|
} else if (decimal_not_zero_length > valid_precision) {
|
|
/* total_length - non-zero decimal digit + valid decimal number */
|
|
floating_scale = MIN(scale, (decimal_cnt - decimal_not_zero_length + valid_precision));
|
|
/* 0.345 scale = -2, res_p = 2 res_s = -2
|
|
* 1234.324 scale = -10 res_p = 0 res_s = -10
|
|
* 1...42th.123 scale = -1 res_p = 40, res_s = -2*/
|
|
if (scale > 0) {
|
|
r_precision = MIN(valid_precision, decimal_not_zero_length - (scale - decimal_cnt));
|
|
} else {
|
|
r_precision = 0;
|
|
}
|
|
r_scale = floating_scale;
|
|
}
|
|
}
|
|
if (NULL != res_precision && NULL != res_scale) {
|
|
if (using_floating_scale
|
|
|| (r_precision <= valid_precision
|
|
&& r_precision >= 0
|
|
&& r_scale <= OB_MAX_NUMBER_SCALE
|
|
&& r_scale >= OB_MIN_NUMBER_SCALE)) {
|
|
*res_precision = static_cast<int16_t>(r_precision);
|
|
*res_scale = static_cast<int16_t>(r_scale);
|
|
} else {
|
|
LOG_WARN("got invalid precision or scale on oracle mode, use builded",
|
|
K(r_precision), K(r_scale));
|
|
}
|
|
}
|
|
// LOG_DEBUG("Number round with:", K(using_floating_scale), K(floating_scale), K(decimal_length),
|
|
// K(r_precision), K(r_scale));
|
|
if (0 < floating_scale) {
|
|
if (OB_FAIL(round_decimal_(floating_scale, decimal_digits, decimal_length))) {
|
|
LOG_ERROR("fail to get round_decimal");
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, decimal_digits, decimal_length);
|
|
}
|
|
} else if (0 > floating_scale) {
|
|
if (OB_FAIL(round_integer_(-floating_scale, integer_digits, integer_length))) {
|
|
LOG_ERROR("fail to get round_integer");
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, NULL, 0);
|
|
}
|
|
} else if (0 == floating_scale) {
|
|
if (0 < decimal_length) {
|
|
if ((5 * BASE / 10) <= decimal_digits[0]) {
|
|
decimal_digits[0] = BASE;
|
|
} else {
|
|
decimal_digits[0] = 0;
|
|
}
|
|
ret = rebuild_digits_(integer_digits, integer_length, decimal_digits, 1);
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//if number=x.5 number.round_even_number() = nearest even number
|
|
int ObNumber::round_even_number()
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (is_zero()) {
|
|
//do nothing
|
|
} else {
|
|
const int64_t expr_value = get_decode_exp(d_);
|
|
//xxx_length means xx digit array length
|
|
const int64_t integer_length = (expr_value >= 0 ? (expr_value + 1) : 0);
|
|
//e.g. 1,000000000,000000000, digits=[1], expr_value=2, len_=1, integer_length=3, valid_integer_length=1
|
|
const int64_t valid_integer_length = (d_.len_ > integer_length ? integer_length : d_.len_);
|
|
//xxx_count means xx digit count
|
|
//const int64_t integer_count = (valid_integer_length > 0 ? (get_digit_len_v2(digits_[0]) + (integer_length - 1) * DIGIT_LEN) : 0);
|
|
|
|
//len_ > integer_length means have decimal
|
|
const int64_t valid_decimal_length = (d_.len_ > integer_length ? (d_.len_ - integer_length) : 0);
|
|
//e.g. 0.000000000 000000001, digits=[1], expr_value=-2, len_=1, decimal_length=2, valid_decimal_length=1
|
|
const int64_t decimal_length = valid_decimal_length + ((0 == integer_length) ? (0 - expr_value - 1) : 0);
|
|
|
|
bool need_normalize = false;
|
|
if (0 == decimal_length) {
|
|
//do nothing
|
|
} else {
|
|
if (valid_integer_length > 0) {
|
|
need_normalize = true;
|
|
d_.len_ = valid_integer_length;
|
|
uint32_t &digit_round = *(digits_ + valid_integer_length);
|
|
uint32_t &lowest_number_integer = *(digits_ + valid_integer_length - 1);
|
|
if ((BASE / 2) < digit_round) {
|
|
digits_[d_.len_ - 1] += 1;
|
|
} else if ((BASE / 2) == digit_round) {
|
|
if (1 < decimal_length || (1 == decimal_length && 1 == (lowest_number_integer % 2))) {
|
|
digits_[d_.len_ - 1] += 1;
|
|
}
|
|
}
|
|
} else {
|
|
uint32_t &digit_round = *(digits_ + 0);
|
|
if (get_decode_exp(d_) == -1 && ((digit_round > (BASE / 2)) || ((BASE / 2) == digit_round && 1 < decimal_length))){
|
|
set_one();
|
|
} else {
|
|
set_zero();
|
|
}
|
|
}
|
|
}
|
|
if (need_normalize) {
|
|
if (OB_FAIL(normalize_v3_(true, true))) {
|
|
LOG_ERROR("fail to normalize_v3_", KPC(this), K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG("finish round_even_number_", KPC(this));
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::round_integer_(
|
|
const int64_t scale, // >= 1
|
|
uint32_t *integer_digits,
|
|
int64_t &integer_length)
|
|
{
|
|
static const uint64_t ROUND_POWS[] = {0, 10, 100, 1000, 10000, 100000,
|
|
1000000, 10000000, 100000000};
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(integer_digits) || OB_UNLIKELY(0 == DIGIT_LEN || scale < 1)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null or the DIGIT_LEN IS ZERO or the scale < 1", K(ret));
|
|
} else {
|
|
int64_t round_length = scale / DIGIT_LEN;
|
|
int64_t round = scale % DIGIT_LEN;
|
|
LOG_DEBUG("round_integer_", K(round_length), K(round), K(integer_length));
|
|
if (integer_length < round_length) {
|
|
integer_length = 0;
|
|
} else {
|
|
if (0 == round) { // scale >= 9, round_length >= 1
|
|
integer_length -= (round_length - 1);
|
|
if ((5 * BASE / 10) <= integer_digits[integer_length - 1]) {
|
|
integer_digits[integer_length - 1] = BASE;
|
|
} else {
|
|
integer_digits[integer_length - 1] = 0;
|
|
}
|
|
} else {
|
|
integer_length -= round_length;
|
|
uint64_t roundv = (uint64_t)integer_digits[integer_length - 1]
|
|
+ 5 * ROUND_POWS[round] / 10;
|
|
roundv /= ROUND_POWS[round];
|
|
roundv *= ROUND_POWS[round];
|
|
integer_digits[integer_length - 1] = (uint32_t)roundv;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::round_decimal_(
|
|
const int64_t scale, // >= 1
|
|
uint32_t *decimal_digits,
|
|
int64_t &decimal_length)
|
|
{
|
|
static const uint64_t ROUND_POWS[] = {0, 100000000, 10000000, 1000000,
|
|
100000, 10000, 1000, 100, 10};
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(decimal_digits) || OB_UNLIKELY(0 == DIGIT_LEN || scale < 1)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null or the DIGIT_LEN IS ZERO or the scale < 1", K(ret));
|
|
} else {
|
|
int64_t round_length = scale / DIGIT_LEN;
|
|
int64_t round = scale % DIGIT_LEN;
|
|
|
|
if (decimal_length <= round_length) {
|
|
// do nothing
|
|
LOG_DEBUG("with no round", K(decimal_length), K(round_length));
|
|
} else {
|
|
decimal_length = std::min(decimal_length, round_length + 1);
|
|
if (0 == round) { // scale >= 9, decimal_length >= 2
|
|
if ((5 * BASE / 10) <= decimal_digits[decimal_length - 1]) {
|
|
decimal_digits[decimal_length - 1] = BASE;
|
|
} else {
|
|
decimal_digits[decimal_length - 1] = 0;
|
|
}
|
|
} else {
|
|
uint64_t roundv = (uint64_t)decimal_digits[decimal_length - 1] + 5 * ROUND_POWS[round] / 10;
|
|
roundv /= ROUND_POWS[round];
|
|
roundv *= ROUND_POWS[round];
|
|
decimal_digits[decimal_length - 1] = (uint32_t)roundv;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::trunc_scale_(int64_t scale, bool using_floating_scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObDigitIterator di;
|
|
di.assign(d_.desc_, digits_);
|
|
uint32_t digit = 0;
|
|
bool from_integer = false;
|
|
bool last_decimal = false;
|
|
uint32_t integer_digits[MAX_CALC_LEN];
|
|
int64_t integer_length = 0;
|
|
int64_t integer_counter = 0;
|
|
uint32_t decimal_digits[MAX_CALC_LEN];
|
|
int64_t decimal_length = 0;
|
|
while (OB_SUCC(di.get_next_digit(digit, from_integer, last_decimal))) {
|
|
if (OB_UNLIKELY(MAX_CALC_LEN <= integer_length
|
|
|| MAX_CALC_LEN <= decimal_length)) {
|
|
LOG_WARN("buffer size overflow", K(integer_length), K(decimal_length));
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
break;
|
|
}
|
|
if (from_integer) {
|
|
if (0 == integer_length) {
|
|
integer_counter += get_digit_len(digit);
|
|
} else {
|
|
integer_counter += DIGIT_LEN;
|
|
}
|
|
integer_digits[integer_length ++] = digit;
|
|
} else if (0 <= scale) {
|
|
if (using_floating_scale
|
|
&& 0 == digit
|
|
&& 0 == integer_length
|
|
&& 0 == decimal_length) {
|
|
continue;
|
|
}
|
|
decimal_digits[decimal_length ++] = digit;
|
|
if ((decimal_length * DIGIT_LEN) > scale) {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
ret = (OB_ITER_END == ret) ? OB_SUCCESS : ret;
|
|
int64_t floating_scale = scale;
|
|
if (OB_SUCCESS == ret
|
|
&& using_floating_scale) {
|
|
floating_scale -= integer_counter;
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
// do nothing
|
|
} else if (0 < floating_scale) {
|
|
if (OB_FAIL(trunc_decimal_(floating_scale, decimal_digits, decimal_length))) {
|
|
LOG_ERROR("fail to get trunc decimal", K(ret));
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, decimal_digits, decimal_length);
|
|
}
|
|
} else if (0 > floating_scale) {
|
|
// todo ouxing
|
|
if (OB_FAIL(trunc_integer_(-floating_scale, integer_digits, integer_length))) {
|
|
LOG_ERROR("fail to get trunc integer", K(ret));
|
|
} else {
|
|
ret = rebuild_digits_(integer_digits, integer_length, NULL, 0);
|
|
}
|
|
|
|
} else if (0 == floating_scale) {
|
|
// todo ouxing
|
|
ret = rebuild_digits_(integer_digits, integer_length, NULL, 0);
|
|
} else { /* Do nothing */ }
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::trunc_integer_(
|
|
const int64_t scale,
|
|
uint32_t *integer_digits,
|
|
int64_t &integer_length)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
static const uint64_t TRUNC_POWS[] = {0, 10, 100, 1000, 10000, 100000,
|
|
1000000, 10000000, 100000000};
|
|
if (OB_UNLIKELY(0 == DIGIT_LEN || scale <= 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(ERROR, "DIGIT_LEN is zero or scale is not positive", K(ret), K(scale));
|
|
} else if (integer_length > 0) {
|
|
//tips: in terms of "3333123456789555555555.123"
|
|
//integer_length = 3 and integer_digits[0]=3333, integer_digits[1] = 123456789 integer_digits[2] = 555555555
|
|
int64_t trunc_length = (scale - 1) / DIGIT_LEN + 1;
|
|
int64_t trunc = scale % DIGIT_LEN;
|
|
if (integer_length < trunc_length) {
|
|
integer_length = 0;
|
|
} else {
|
|
if (0 == trunc) { //such as "3333123456789555555555.123" and scale = 9
|
|
integer_length -= trunc_length;
|
|
} else { //such as "3333123456789555555555.123" and scale = 3, change 555555555 to 555555000
|
|
//integer_length >= trunc_length and trunc_length > 0 , so it holds that
|
|
//integer_length - trunc_length + 1 - 1 = integer_length - trunc_length >= 0 and
|
|
//integer_length - trunc_length + 1 - 1 = integer_length - trunc_length < integer_length
|
|
//we need to check the bound here and can use integer_digits[integer_length - 1] safety
|
|
integer_length -= (trunc_length - 1);
|
|
uint64 truncdv = static_cast<uint64_t>(integer_digits[integer_length - 1]);
|
|
truncdv /= TRUNC_POWS[trunc];
|
|
truncdv *= TRUNC_POWS[trunc];
|
|
integer_digits[integer_length - 1] = static_cast<uint32_t>(truncdv);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::trunc_decimal_(
|
|
const int64_t scale,
|
|
uint32_t *decimal_digits,
|
|
int64_t &decimal_length)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
static const uint64_t TRUNC_POWS[] = {0, 100000000, 10000000, 1000000,
|
|
100000, 10000, 1000, 100, 10};
|
|
if (OB_UNLIKELY(0 == DIGIT_LEN || scale <= 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the DIGIT_LEN is zero or scale is not positive", K(ret), K(scale));
|
|
} else if (decimal_length > 0) {
|
|
int64_t trunc_length = (scale - 1) / DIGIT_LEN + 1;
|
|
int64_t trunc = scale % DIGIT_LEN;
|
|
if (decimal_length < trunc_length) {
|
|
// do nothing
|
|
} else {
|
|
decimal_length = trunc_length;
|
|
if (0 == trunc) {
|
|
//such as : "1.123456789123456789" when scale = 9 trunc will be 0
|
|
//decimal_length will be changed from 2 to 1 (trunc_length).
|
|
//and, we do nothing here.
|
|
} else {
|
|
uint64 truncdv = static_cast<uint64_t>(decimal_digits[decimal_length - 1]);
|
|
truncdv /= TRUNC_POWS[trunc];
|
|
truncdv *= TRUNC_POWS[trunc];
|
|
decimal_digits[decimal_length - 1] = static_cast<uint32_t>(truncdv);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// handle decoded digits without tail flag
|
|
int ObNumber::normalize_(uint32_t *digits, const int64_t length)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(digits)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null", K(ret));
|
|
} else {
|
|
int64_t tmp_length = length;
|
|
int64_t start = 0;
|
|
for (int64_t i = 0; i < tmp_length; ++i) {
|
|
if (0 != digits[i]) {
|
|
break;
|
|
}
|
|
++start;
|
|
}
|
|
|
|
uint32_t carry = 0;
|
|
for (int64_t i = tmp_length - 1; i >= start; --i) {
|
|
digits[i] += carry;
|
|
if (BASE <= digits[i]) {
|
|
digits[i] -= (uint32_t)BASE;
|
|
carry = 1;
|
|
} else {
|
|
carry = 0;
|
|
}
|
|
}
|
|
// carry may generate extra zero, so reduce zero on tail, must after handle carry
|
|
int64_t end = tmp_length - 1;
|
|
for (int64_t i = tmp_length - 1; i >= start; --i) {
|
|
if (0 != digits[i]
|
|
&& BASE != digits[i]) {
|
|
break;
|
|
}
|
|
--end;
|
|
}
|
|
tmp_length = end - start + 1;
|
|
tmp_length = (0 < tmp_length) ? tmp_length : 0;
|
|
|
|
if (0 < carry) {
|
|
if (OB_UNLIKELY(0 != tmp_length)) {
|
|
LOG_WARN("unexpected length", K(tmp_length));
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else {
|
|
digits_[0] = carry;
|
|
d_.len_ = 1;
|
|
if (NEGATIVE == d_.sign_) {
|
|
--d_.exp_;
|
|
} else {
|
|
++d_.exp_;
|
|
}
|
|
}
|
|
} else {
|
|
if (digits_ != &digits[start]) {
|
|
memmove(digits_, &digits[start], tmp_length * ITEM_SIZE(digits_));
|
|
}
|
|
d_.len_ = (uint8_t)tmp_length;
|
|
if (0 == d_.len_) {
|
|
set_zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// handle decoded digits without tail flag
|
|
int ObNumber::normalize_v2_(const bool from_calc/*true*/, const bool only_normalize_tailer /*false*/)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(digits_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null", K(ret));
|
|
} else {
|
|
int64_t tmp_length = d_.len_;
|
|
int64_t start = 0;
|
|
if (!only_normalize_tailer) {
|
|
for (int64_t i = 0; i < tmp_length; ++i) {
|
|
if (0 != digits_[i]) {
|
|
break;
|
|
}
|
|
++start;
|
|
}
|
|
}
|
|
|
|
uint32_t carry = 0;
|
|
int64_t end = tmp_length - 1;
|
|
if (from_calc) {
|
|
for (int64_t i = tmp_length - 1; i >= start; --i) {
|
|
digits_[i] += carry;
|
|
if (digits_[i] >= BASE) {
|
|
digits_[i] -= (uint32_t)BASE;
|
|
carry = 1;
|
|
} else {
|
|
carry = 0;
|
|
}
|
|
}
|
|
|
|
// carry may generate extra zero, so reduce zero on tail, must after handle carry
|
|
for (int64_t i = tmp_length - 1; i >= start; --i) {
|
|
if (0 != digits_[i]) {
|
|
break;
|
|
}
|
|
--end;
|
|
}
|
|
}
|
|
tmp_length = end - start + 1;
|
|
if (tmp_length < 0) {
|
|
tmp_length = 0;
|
|
}
|
|
|
|
if (0 < carry) {
|
|
if (OB_UNLIKELY(0 != tmp_length)) {
|
|
LOG_WARN("unexpected length", K(tmp_length));
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else {
|
|
digits_[0] = carry;
|
|
d_.len_ = 1;
|
|
if (NEGATIVE == d_.sign_) {
|
|
--d_.exp_;
|
|
} else {
|
|
++d_.exp_;
|
|
}
|
|
}
|
|
} else {
|
|
if (digits_ != digits_ + start) {
|
|
memmove(digits_, digits_ + start, tmp_length * ITEM_SIZE(digits_));
|
|
}
|
|
d_.len_ = (uint8_t)tmp_length;
|
|
if (0 == d_.len_) {
|
|
set_zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// handle decoded digits without tail flag
|
|
int ObNumber::normalize_v3_(const bool from_calc/*true*/, const bool only_normalize_tailer /*false*/)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(digits_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null", K(ret));
|
|
} else {
|
|
int64_t tmp_length = d_.len_;
|
|
int64_t start = 0;
|
|
if (!only_normalize_tailer) {
|
|
for (int64_t i = 0; i < tmp_length && 0 == digits_[i]; ++i) {
|
|
++start;
|
|
}
|
|
}
|
|
|
|
uint32_t carry = 0;
|
|
int64_t end = tmp_length - 1;
|
|
if (from_calc) {
|
|
for (int64_t i = end; i >= start; --i) {
|
|
uint32_t &tmp_digit = digits_[i];
|
|
tmp_digit += carry;
|
|
if (BASE == tmp_digit) {
|
|
tmp_digit = 0;
|
|
--end;
|
|
carry = 1;
|
|
} else if (0 == tmp_digit) {
|
|
--end;
|
|
carry = 0;
|
|
} else {
|
|
carry = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
tmp_length = end - start + 1;
|
|
if (tmp_length < 0) {
|
|
tmp_length = 0;
|
|
}
|
|
|
|
if (carry > 0) {
|
|
if (OB_UNLIKELY(0 != tmp_length)) {
|
|
LOG_WARN("unexpected length", K(tmp_length));
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else {
|
|
digits_[0] = carry;
|
|
d_.len_ = 1;
|
|
if (NEGATIVE == d_.sign_) {
|
|
--d_.exp_;
|
|
} else {
|
|
++d_.exp_;
|
|
}
|
|
}
|
|
} else {
|
|
if (0 == tmp_length) {
|
|
set_zero();
|
|
} else {
|
|
if (digits_ != digits_ + start) {
|
|
memmove(digits_, digits_ + start, tmp_length * ITEM_SIZE(digits_));
|
|
}
|
|
d_.len_ = (uint8_t)tmp_length;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::rebuild_digits_(
|
|
const uint32_t *integer_digits,
|
|
const int64_t integer_length,
|
|
const uint32_t *decimal_digits,
|
|
const int64_t decimal_length)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
uint32_t digits[MAX_CALC_LEN] = {};
|
|
int64_t length = 0;
|
|
if (NULL != integer_digits
|
|
&& 0 < integer_length) {
|
|
if (OB_UNLIKELY(MAX_CALC_LEN <= (length + integer_length))) {
|
|
_OB_LOG(WARN, "buffer size overflow, cap=%ld length=%ld integer_length=%ld",
|
|
MAX_CALC_LEN, length, integer_length);
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
} else {
|
|
MEMCPY(digits, integer_digits, integer_length * ITEM_SIZE(digits_));
|
|
length = integer_length;
|
|
}
|
|
}
|
|
if (OB_SUCCESS == ret
|
|
&& NULL != decimal_digits
|
|
&& 0 < decimal_length) {
|
|
if (OB_UNLIKELY(MAX_CALC_LEN <= (length + decimal_length))) {
|
|
_OB_LOG(WARN, "buffer size overflow, cap=%ld length=%ld decimal_length=%ld",
|
|
MAX_CALC_LEN, length, decimal_length);
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
} else {
|
|
MEMCPY(&digits[length], decimal_digits, decimal_length * ITEM_SIZE(digits));
|
|
length += decimal_length;
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
ret = normalize_(digits, length);
|
|
}
|
|
OB_LOG(DEBUG, "succ to rebuild_digits_", K(integer_length), K(decimal_length), K(length), KPC(this));
|
|
return ret;
|
|
}
|
|
|
|
const char *ObNumber::format() const
|
|
{
|
|
// TODO: To Be Removed. Use stack local instead.
|
|
static const int64_t BUFFER_NUM = 64;
|
|
static const int64_t BUFFER_SIZE = MAX_PRINTABLE_SIZE;
|
|
char *buffers = (char*)GET_TSI_MULT(ByteBuf<BUFFER_NUM * BUFFER_SIZE>, 3);
|
|
RLOCAL(uint64_t, i);
|
|
char *buffer = &buffers[0] + (i++ % BUFFER_NUM) * BUFFER_SIZE;
|
|
int64_t length = 0;
|
|
if (OB_ISNULL(buffers)) {
|
|
buffer = nullptr;
|
|
} else if(OB_UNLIKELY(OB_SUCCESS != format(buffer, BUFFER_SIZE, length, -1))) {
|
|
buffer = nullptr;
|
|
LOG_ERROR_RET(OB_ERROR, "fail to format buffer");
|
|
} else {
|
|
buffer[length] = '\0';
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
int ObNumber::format_v1(char *buf, const int64_t buf_len, int64_t &pos, int16_t scale) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
uint32_t digits[MAX_STORE_LEN] = {0};
|
|
bool prev_from_integer = true;
|
|
int64_t pad_zero_count = scale;
|
|
if (is_oracle_mode()) {
|
|
pad_zero_count = 0;
|
|
}
|
|
ObNumber buf_nmb;
|
|
const ObNumber *nmb = this;
|
|
if (scale >= 0) {
|
|
if (OB_FAIL(buf_nmb.from_(*this, digits))) {
|
|
} else if (OB_FAIL(buf_nmb.round(scale))) {
|
|
} else {
|
|
nmb = &buf_nmb;
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && 0 == nmb->d_.len_) {
|
|
ret = databuff_printf(buf, buf_len, pos, "0");
|
|
} else {
|
|
if (OB_SUCC(ret)) {
|
|
if (nmb->is_negative()) {
|
|
ret = databuff_printf(buf, buf_len, pos, "-");
|
|
}
|
|
// oracle模式下小数的整数部分不补0,0.2345用.2345表示,-0.2345用-.2345表示
|
|
if (OB_SUCCESS == ret && nmb->is_decimal() && !is_oracle_mode()) {
|
|
ret = databuff_printf(buf, buf_len, pos, "0");
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
ObDigitIterator di;
|
|
di.assign(nmb->get_desc_value(), nmb->get_digits());
|
|
uint32_t digit = 0;
|
|
bool head_flag = true;
|
|
bool from_integer = false;
|
|
bool last_decimal = false;
|
|
int tmp_ret = OB_SUCCESS;//used for iteration of get_next_digit
|
|
while (OB_SUCCESS == ret
|
|
&& OB_SUCCESS == (tmp_ret = di.get_next_digit(digit, from_integer, last_decimal))) {
|
|
if (prev_from_integer && !from_integer) {
|
|
// dot
|
|
ret = databuff_printf(buf, buf_len, pos, ".");
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (!from_integer && !last_decimal) {
|
|
pad_zero_count -= DIGIT_LEN;
|
|
}
|
|
if (head_flag && from_integer) {
|
|
// first integer
|
|
ret = databuff_printf(buf, buf_len, pos, "%u", digit);
|
|
} else if (last_decimal) {
|
|
// last decimal
|
|
int64_t counter = 0;
|
|
uint32_t tmp = remove_back_zero(digit, counter);
|
|
ret = databuff_printf(buf, buf_len, pos, ObNumber::BACK_DIGIT_FORMAT[counter], tmp);
|
|
pad_zero_count -= (DIGIT_LEN - counter);
|
|
} else {
|
|
// normal digit
|
|
ret = databuff_printf(buf, buf_len, pos, DIGIT_FORMAT, digit);
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
prev_from_integer = from_integer;
|
|
head_flag = false;
|
|
}
|
|
}//end while. end iteration
|
|
if (OB_SUCC(ret)) {
|
|
ret = (OB_ITER_END == tmp_ret) ? OB_SUCCESS : tmp_ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// pad zero.
|
|
if (OB_SUCCESS == ret && pad_zero_count > 0) {
|
|
if (prev_from_integer) {
|
|
ret = databuff_printf(buf, buf_len, pos, ".");
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_UNLIKELY(pad_zero_count > FLOATING_SCALE)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the param is invalid", K(ret), K(pad_zero_count));
|
|
} else {
|
|
char zeros[FLOATING_SCALE + 1] = "000000000000000000000000000000000000000000000000000000000000000000000000";
|
|
zeros[pad_zero_count] = 0;
|
|
ret = databuff_printf(buf, buf_len, pos, "%s", zeros);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::format_int64(char *buf, int64_t &pos, const int16_t scale, bool &is_finish) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_finish = true;
|
|
const int64_t orig_pos = pos;
|
|
if (is_zero()) {
|
|
buf[pos++] = '0';
|
|
} else {
|
|
if (is_negative()) {
|
|
buf[pos++] = '-';
|
|
}
|
|
const int32_t expr_value = get_decode_exp(d_);
|
|
if (!is_integer(expr_value)) {
|
|
is_finish = false;
|
|
} else if (is_int32(expr_value)) {
|
|
const int64_t format_length = ObFastFormatInt::format_unsigned(digits_[0], buf + pos);
|
|
if (OB_UNLIKELY(format_length > DIGIT_LEN)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("digits is unexpected", "digit", digits_[0], KPC(this), K(ret));
|
|
//print number will not report error ret, we need make use find it
|
|
MEMCPY(buf + pos, NUMBER_ERRMSG.ptr(), NUMBER_ERRMSG.length());
|
|
} else {
|
|
pos += format_length;
|
|
}
|
|
} else if (is_int64_without_expr(expr_value)) {
|
|
const int64_t format_length = ObFastFormatInt::format_unsigned(digits_[0], buf + pos);
|
|
if (OB_UNLIKELY(format_length > DIGIT_LEN)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("digits is unexpected", "digit", digits_[0], KPC(this), K(ret));
|
|
//print number will not report error ret, we need make use find it
|
|
MEMCPY(buf + pos, NUMBER_ERRMSG.ptr(), NUMBER_ERRMSG.length());
|
|
} else {
|
|
pos += format_length;
|
|
ObFastFormatInt ffi(digits_[1]);
|
|
if (OB_UNLIKELY(ffi.length() > DIGIT_LEN)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("digits is unexpected", "digit", digits_[1], KPC(this), K(ret));
|
|
//print number will not report error ret, we need make use find it
|
|
MEMCPY(buf + pos, NUMBER_ERRMSG.ptr(), NUMBER_ERRMSG.length());
|
|
} else {
|
|
MEMSET(buf + pos, '0', DIGIT_LEN - ffi.length());
|
|
MEMCPY(buf + pos + DIGIT_LEN - ffi.length(), ffi.ptr(), ffi.length());
|
|
pos += DIGIT_LEN;
|
|
}
|
|
}
|
|
} else if (is_int64_with_expr(expr_value)) {
|
|
const int64_t format_length = ObFastFormatInt::format_unsigned(digits_[0], buf + pos);
|
|
if (OB_UNLIKELY(format_length > DIGIT_LEN)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("digits is unexpected", "digit", digits_[0], KPC(this), K(ret));
|
|
//print number will not report error ret, we need make use find it
|
|
MEMCPY(buf + pos, NUMBER_ERRMSG.ptr(), NUMBER_ERRMSG.length());
|
|
} else {
|
|
pos += format_length;
|
|
MEMSET(buf + pos, '0', DIGIT_LEN);
|
|
pos += DIGIT_LEN;
|
|
}
|
|
} else {
|
|
is_finish = false;
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && is_finish) {
|
|
// pad zero.
|
|
if (!is_oracle_mode() && scale > 0) {
|
|
buf[pos++] = '.';
|
|
if (OB_UNLIKELY(scale > FLOATING_SCALE)) {
|
|
is_finish = false;
|
|
pos = orig_pos;
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("scale is to large", K(scale), K(ret));
|
|
} else {
|
|
MEMCPY(buf + pos, FLOATING_ZEROS, scale);
|
|
pos += scale;
|
|
buf[pos] = '\0';
|
|
}
|
|
} else {
|
|
buf[pos] = '\0';
|
|
}
|
|
ObString tmp_str(pos - orig_pos, buf);
|
|
LOG_DEBUG("finish format int64", KPC(this), K(scale), K(is_oracle_mode()), K(tmp_str));
|
|
} else {
|
|
pos = orig_pos;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::format_v2(
|
|
char *buf,
|
|
const int64_t buf_len,
|
|
int64_t &pos,
|
|
int16_t scale,
|
|
const bool need_to_sci/*false*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_finish = false;
|
|
const int64_t orig_pos = pos;
|
|
const int64_t max_need_size = get_max_format_length() + ((!is_oracle_mode() && scale > 0) ? scale : 0);
|
|
if (OB_ISNULL(buf) || OB_UNLIKELY(max_need_size < 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("argument is invalid", KP(buf), K(max_need_size), K(ret));
|
|
} else if (OB_UNLIKELY((buf_len - pos) < max_need_size)) {
|
|
ret = OB_SIZE_OVERFLOW;
|
|
LOG_TRACE("size is overflow", K(buf_len), K(pos), K(max_need_size),
|
|
KPC(this), K(scale), K(ret));
|
|
} else if (OB_FAIL(format_int64(buf, pos, scale, is_finish))) {
|
|
LOG_ERROR("format_int64 failed", KPC(this), K(ret));
|
|
} else if (is_finish) {
|
|
//fast path succ
|
|
} else {
|
|
pos = orig_pos;
|
|
bool prev_from_integer = true;
|
|
int64_t pad_zero_count = (is_oracle_mode() ? 0 : scale);
|
|
ObNumber buf_nmb;
|
|
uint32_t digits[MAX_STORE_LEN] = {0};
|
|
const ObNumber *nmb = this;
|
|
if (!is_integer(get_decode_exp(d_)) && scale >= 0) {
|
|
if (OB_FAIL(buf_nmb.from_(*this, digits))) {
|
|
} else if (OB_FAIL(buf_nmb.round(scale))) {
|
|
} else if (OB_UNLIKELY(buf_len - pos < buf_nmb.get_max_format_length())) {
|
|
ret = OB_SIZE_OVERFLOW;
|
|
LOG_WARN("size is overflow", "buf_size", buf_len - pos,
|
|
"max_need_size", buf_nmb.get_max_format_length(), K(scale),
|
|
K(buf_nmb), K(ret));
|
|
} else {
|
|
nmb = &buf_nmb;
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
} else if (nmb->is_zero()) {
|
|
buf[pos++] = '0';
|
|
} else {
|
|
if (is_negative()) {
|
|
buf[pos++] = '-';
|
|
}
|
|
// oracle模式下小数的整数部分不补0,0.2345用.2345表示,-0.2345用-.2345表示
|
|
if (nmb->is_decimal() && !is_oracle_mode()) {
|
|
buf[pos++] = '0';
|
|
}
|
|
|
|
ObDigitIterator di;
|
|
di.assign(nmb->get_desc_value(), nmb->get_digits());
|
|
uint32_t digit = 0;
|
|
ObDigitIterator::NextDigitEnum nd_enum = ObDigitIterator::NextDigitEnum::ND_END;
|
|
while (OB_SUCC(ret) && ObDigitIterator::NextDigitEnum::ND_END != (nd_enum = di.get_next_digit(digit))) {
|
|
switch (nd_enum) {
|
|
case ObDigitIterator::NextDigitEnum::ND_HEAD_INTEGER: {
|
|
const int64_t format_length = ObFastFormatInt::format_unsigned(digit, buf + pos);
|
|
if (OB_UNLIKELY(format_length > DIGIT_LEN)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("digits is unexpected", "digit", digit, KPC(this), K(ret));
|
|
//print number will not report error ret, we need make use find it
|
|
MEMCPY(buf + pos, NUMBER_ERRMSG.ptr(), NUMBER_ERRMSG.length());
|
|
} else {
|
|
pos += format_length;
|
|
}
|
|
break;
|
|
}
|
|
case ObDigitIterator::NextDigitEnum::ND_BODY_DECIMAL:
|
|
if (prev_from_integer) {
|
|
// dot
|
|
buf[pos++] = '.';
|
|
prev_from_integer = false;
|
|
}
|
|
pad_zero_count -= DIGIT_LEN;
|
|
case ObDigitIterator::NextDigitEnum::ND_BODY_INTEGER: {
|
|
// normal digit
|
|
ObFastFormatInt ffi(digit);
|
|
if (OB_UNLIKELY(ffi.length() > DIGIT_LEN)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("digits is unexpected", "ffi", ffi.str(), K(digit), KPC(this), K(ret));
|
|
//print number will not report error ret, we need make use find it
|
|
MEMCPY(buf + pos, NUMBER_ERRMSG.ptr(), NUMBER_ERRMSG.length());
|
|
} else {
|
|
MEMSET(buf + pos, '0', DIGIT_LEN - ffi.length());
|
|
MEMCPY(buf + pos + DIGIT_LEN - ffi.length(), ffi.ptr(), ffi.length());
|
|
pos += DIGIT_LEN;
|
|
}
|
|
break;
|
|
}
|
|
case ObDigitIterator::NextDigitEnum::ND_TAIL_DECIMAL: {
|
|
// last decimal
|
|
if (prev_from_integer) {
|
|
// dot
|
|
buf[pos++] = '.';
|
|
prev_from_integer = false;
|
|
}
|
|
ObFastFormatInt ffi(digit);
|
|
if (OB_UNLIKELY(ffi.length() > DIGIT_LEN)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("digits is unexpected", "ffi", ffi.str(), K(digit), KPC(this), K(ret));
|
|
//print number will not report error ret, we need make use find it
|
|
MEMCPY(buf + pos, NUMBER_ERRMSG.ptr(), NUMBER_ERRMSG.length());
|
|
} else {
|
|
const int64_t tail_zero_count = ffi.get_tail_zero_count();
|
|
MEMSET(buf + pos, '0', DIGIT_LEN - ffi.length());
|
|
MEMCPY(buf + pos + DIGIT_LEN - ffi.length(), ffi.ptr(), ffi.length() - tail_zero_count);
|
|
pos += (DIGIT_LEN - tail_zero_count);
|
|
pad_zero_count -= (DIGIT_LEN - tail_zero_count);
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
LOG_ERROR("it should not arrive here", K(nd_enum));
|
|
}
|
|
}
|
|
}//end while. end iteration
|
|
}
|
|
|
|
// pad zero.
|
|
if (OB_SUCC(ret) && pad_zero_count > 0) {
|
|
if (prev_from_integer) {
|
|
buf[pos++] = '.';
|
|
}
|
|
if (OB_UNLIKELY(pad_zero_count > FLOATING_SCALE)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the param is invalid", K(ret), K(pad_zero_count));
|
|
} else if (OB_UNLIKELY(pos + pad_zero_count > buf_len)) {
|
|
ret = OB_SIZE_OVERFLOW;
|
|
LOG_WARN("size is overflow", K(pos), K(pad_zero_count), K(buf_len), K(ret));
|
|
} else {
|
|
MEMCPY(buf + pos, FLOATING_ZEROS, pad_zero_count);
|
|
pos += pad_zero_count;
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
buf[pos] = '\0';
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_UNLIKELY(pos >= buf_len)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("size is overflow, it should not happened", K(pos), K(buf_len), KPC(this),
|
|
K(scale), K(ret));
|
|
OB_ASSERT(pos < buf_len);
|
|
} else if (OB_UNLIKELY(pos - orig_pos > max_need_size)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("calc size is too litter", "write_len", pos - orig_pos,
|
|
K(max_need_size), K(buf_len), KPC(this), K(scale), K(ret));
|
|
OB_ASSERT(pos - orig_pos <= max_need_size);
|
|
} else if (is_oracle_mode() && need_to_sci && pos - orig_pos > SCI_NUMBER_LENGTH) {
|
|
ObString num_str(pos - orig_pos, buf + orig_pos);
|
|
pos = orig_pos;
|
|
if (OB_FAIL(to_sci_str_(num_str, buf, buf_len, pos))) {
|
|
LOG_ERROR("fail to conv to sci str", K(buf_len), K(orig_pos));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::to_sci_str_(ObString &num_str, char *buf,
|
|
const int64_t buf_len, int64_t &pos) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t COSNT_BUF_SIZE = 256;
|
|
char ptr[COSNT_BUF_SIZE];
|
|
int64_t origin = pos;
|
|
int64_t str_len = num_str.length();
|
|
if (OB_UNLIKELY(pos > buf_len || buf_len < 0 || pos < 0 || OB_ISNULL(buf))) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid value", K(ret), K(pos), K(buf_len), KP(buf));
|
|
} else if (OB_UNLIKELY(buf_len - pos < SCI_NUMBER_LENGTH || str_len >= COSNT_BUF_SIZE)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid value", K(ret), K(pos));
|
|
} else {
|
|
MEMCPY(ptr, num_str.ptr(), str_len);
|
|
int64_t raw_pos = 0;
|
|
// exponent part of the str
|
|
char pow_str[6];
|
|
int64_t pow_pos = 0;
|
|
// exponent value
|
|
int64_t pow_size = 0;
|
|
bool pre_dot = false;
|
|
bool is_negative = false;
|
|
// exponent value str width
|
|
int64_t width_count = 0;
|
|
pow_str[pow_pos++] = 'E';
|
|
pow_str[pow_pos++] = '+';
|
|
if ('-' == ptr[raw_pos]) {
|
|
raw_pos++;
|
|
buf[pos++] = '-';
|
|
is_negative = true;
|
|
}
|
|
if ('.' == ptr[raw_pos]) {
|
|
raw_pos++;
|
|
pre_dot = true;
|
|
pow_str[pow_pos - 1] = '-';
|
|
}
|
|
int64_t zero_count = 0;
|
|
//find the first non-zero number
|
|
while ('0' == ptr[raw_pos] && raw_pos < str_len) {
|
|
raw_pos++;
|
|
zero_count++;
|
|
}
|
|
buf[pos++] = ptr[raw_pos++];
|
|
buf[pos++] = '.';
|
|
// determine the exponent part and the number part according to
|
|
// whether a decimal point appears in the front
|
|
if (pre_dot) {
|
|
pow_size = zero_count + 1;
|
|
if (pow_size >= 0 && pow_size <= 9) {
|
|
width_count = 1;
|
|
} else if (pow_size >= 10 && pow_size <= 99) {
|
|
width_count = 2;
|
|
} else {
|
|
width_count = 3;
|
|
}
|
|
// fill 0 if the sci number is less than 40 bytes
|
|
while (pos < SCI_NUMBER_LENGTH - width_count - pow_pos + origin) {
|
|
if (raw_pos >= str_len) {
|
|
buf[pos++] = '0';
|
|
} else {
|
|
buf[pos++] = ptr[raw_pos++];
|
|
}
|
|
}
|
|
} else if (!pre_dot && 0 == zero_count) {
|
|
int64_t count = 0;
|
|
// if the number is greater than 10, always need to traverse the number string array
|
|
// to get the final exponent value. stop traversing when the decimal point appears.
|
|
// the exponent value will affect the number of bytes
|
|
for (int64_t i = raw_pos; i < str_len && ptr[i] != '.'; ++i) {
|
|
count++;
|
|
}
|
|
if (count >= 0 && count <= 9) {
|
|
width_count = 1;
|
|
} else if (count >= 10 && count <= 99) {
|
|
width_count = 2;
|
|
} else {
|
|
width_count = 3;
|
|
}
|
|
while (pos < SCI_NUMBER_LENGTH - pow_pos - width_count + origin) {
|
|
if (raw_pos >= str_len) {
|
|
buf[pos++] = '0';
|
|
} else if ('.' == ptr[raw_pos]) {
|
|
raw_pos++;
|
|
} else {
|
|
buf[pos++] = ptr[raw_pos++];
|
|
}
|
|
}
|
|
pow_size = count;
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("the number raw str is unexpected", K(ret));
|
|
}
|
|
// round to the last digit and handle the carry
|
|
if (OB_SUCC(ret)) {
|
|
int64_t carry = 0;
|
|
int64_t carry_pos = pos;
|
|
// if it is a negative number, need to ignore the leading '-'
|
|
int64_t digit_start_pos = is_negative ? origin + 1 : origin;
|
|
if (raw_pos < str_len && ptr[raw_pos] >= '5' && ptr[raw_pos] <= '9') {
|
|
carry = 1;
|
|
carry_pos--;
|
|
while (carry && carry_pos >= digit_start_pos && OB_SUCC(ret)) {
|
|
if (buf[carry_pos] >= '0' && buf[carry_pos] <= '8') {
|
|
buf[carry_pos] = (char)((int)buf[carry_pos] + carry);
|
|
carry = 0;
|
|
carry_pos--;
|
|
} else if ('9' == buf[carry_pos]) {
|
|
carry = 1;
|
|
buf[carry_pos--] = '0';
|
|
} else if ('.' == buf[carry_pos]) {
|
|
carry_pos--;
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("It's unexpected to round the number sci", K(ret));
|
|
}
|
|
}
|
|
// if there is a carry in the last digit, move one byte to the right
|
|
// eg: 10.000000000000000000000000000000000E+1 --> 1.0000000000000000000000000000000000E+2
|
|
if (1 == carry && digit_start_pos - 1 == carry_pos && OB_SUCC(ret)) {
|
|
for (int64_t i = pos - 1; i >= digit_start_pos + 1; --i) {
|
|
if (buf[i - 1] != '.') {
|
|
buf[i] = buf[i - 1];
|
|
}
|
|
}
|
|
buf[digit_start_pos] = '1';
|
|
buf[digit_start_pos + 1] = '.';
|
|
if ('-' == pow_str[1]) {
|
|
--pow_size;
|
|
} else {
|
|
++pow_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// fill exponent part
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(databuff_printf(pow_str, sizeof(pow_str), pow_pos, "%ld", pow_size))) {
|
|
LOG_WARN("fail to generate pow str", K(ret));
|
|
} else {
|
|
for (int i = 0; i < pow_pos; ++i) {
|
|
buf[pos++] = pow_str[i];
|
|
}
|
|
}
|
|
}
|
|
// check pos
|
|
if (OB_SUCC(ret)) {
|
|
if (str_len > SCI_NUMBER_LENGTH && pos - origin != SCI_NUMBER_LENGTH) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("the value of pos is invalid after number to char in oracle mode",
|
|
KCSTRING(buf), K(pos), K(origin), K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//oracle max store 20 bytes number(39~40 number). when using to_char, only store 40 number at most
|
|
int ObNumber::format_with_oracle_limit(char *buf, const int64_t buf_len, int64_t &pos, int16_t scale) const
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
bool need_to_sci = true;
|
|
number::ObNumber tmp_number;
|
|
char buf_alloc[ObNumber::MAX_BYTE_LEN];
|
|
ObDataBuffer allocator(buf_alloc, ObNumber::MAX_BYTE_LEN);
|
|
//need deep copy before round
|
|
if (OB_FAIL(tmp_number.from(*this, allocator))) {
|
|
LOG_WARN("fail to deep_copy", K(ret), K(tmp_number));
|
|
} else if (OB_FAIL(tmp_number.round_for_sci((-MIN_SCI_SIZE), true))) {
|
|
LOG_WARN("fail to round", K(ret), K(tmp_number));
|
|
} else if (OB_FAIL(tmp_number.format_v2(buf, buf_len, pos, scale, need_to_sci))) {
|
|
LOG_WARN("fail to format", K(ret), K(tmp_number));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::get_npi_(double n, ObNumber& out, ObIAllocator &alloc, const bool do_rounding) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 1;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
const int64_t MAX_DOUBLE_PRINT_SIZE = 512;
|
|
char buf[MAX_DOUBLE_PRINT_SIZE] = {0};
|
|
(void)ob_gcvt_opt(n, OB_GCVT_ARG_DOUBLE, static_cast<int32_t>(sizeof(buf) - 1), buf, NULL, lib::is_oracle_mode(), TRUE);
|
|
|
|
ObNumber n_obnum;
|
|
ObNumber pi = get_pi();
|
|
size_t str_len = strlen(buf);
|
|
if (0 >= str_len) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("len of number string should not less than zero", K(ret), K(str_len));
|
|
} else if (OB_FAIL(n_obnum.from_sci_opt(buf, str_len, local_alloc, NULL, NULL, do_rounding))) {
|
|
LOG_WARN("n_obnum.from(n) failed", K(n), K(ret));
|
|
} else if (OB_FAIL(pi.mul_v3(n_obnum, out, alloc, true, do_rounding))) {
|
|
LOG_WARN("pi.mul(n_obnum) failed", K(n_obnum), K(pi), K(out));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::get_npi_(int64_t n, ObNumber& out, ObIAllocator &alloc, const bool do_rounding) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 1;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
ObNumber n_obnum;
|
|
ObNumber pi = get_pi();
|
|
if (OB_FAIL(n_obnum.from(n, local_alloc))) {
|
|
LOG_WARN("n_obnum.from(n) failed", K(n), K(ret));
|
|
} else if (OB_FAIL(pi.mul_v3(n_obnum, out, alloc, true, do_rounding))) {
|
|
LOG_WARN("pi.mul(n_obnum) failed", K(n_obnum), K(pi), K(out));
|
|
} else {
|
|
// done
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// 根据前一项的阶乘计算本项阶乘,每次只用乘两个数即可,不需要从头计算
|
|
// 见sin/cos的泰勒展开式
|
|
int ObNumber::simple_factorial_for_sincos_(int64_t start, ObIAllocator &allocator, ObNumber &result) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber y;
|
|
result.set_one();
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 3;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
if (OB_FAIL(y.from(start, local_alloc))) {
|
|
LOG_WARN("y.from(start) failed", K(y), K(start), K(ret));
|
|
} else if (OB_FAIL(result.mul_v3(y, result, local_alloc, true, false))) {
|
|
LOG_WARN("result.mul_v3(y) failed", K(result), K(y), K(ret));
|
|
} else if (OB_FAIL(y.add_v3(ObNumber::get_positive_one(), y, local_alloc, true, false))) {
|
|
LOG_WARN("y.add(1) failed", K(y), K(ret));
|
|
} else if (OB_FAIL(result.mul_v3(y, result, allocator, true, false))) {
|
|
LOG_WARN("result.mul(y) failed", K(result), K(y), K(ret));
|
|
} else {
|
|
// done
|
|
LOG_DEBUG("factorial done", K(result));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::taylor_series_sin_(const ObNumber &transformed_x, ObNumber &out, ObIAllocator &allocator, const ObNumber &range_low, const ObNumber &range_high) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
if (!(range_low <= transformed_x && transformed_x <= range_high)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("transform x before do taylor series expansion", K(transformed_x));
|
|
} else {
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 5;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
ObNumber iter_result;
|
|
ObNumber divisor;
|
|
ObNumber square_x;
|
|
ObNumber tmp_out;
|
|
|
|
if (OB_FAIL(iter_result.from(transformed_x, local_alloc))) {
|
|
LOG_WARN("iter_result.from(transformed_x) failed", K(iter_result), K(transformed_x), K(ret));
|
|
} else if (OB_FAIL(square_x.from(transformed_x, local_alloc))) {
|
|
LOG_WARN("square_x.from(transformed_x) failed", K(square_x), K(transformed_x), K(ret));
|
|
} else if (OB_FAIL(square_x.mul_v3(transformed_x, square_x, local_alloc, true, false))) {
|
|
LOG_WARN("square_x.mul(transformed_x) failed", K(square_x), K(transformed_x), K(ret));
|
|
} else if (OB_FAIL(tmp_out.from(transformed_x, local_alloc))) {
|
|
LOG_WARN("tmp_out.from(transformed_x) failed", K(tmp_out), K(transformed_x), K(ret));
|
|
} else if (OB_FAIL(divisor.from(ObNumber::get_positive_one(), local_alloc))) {
|
|
LOG_WARN("divisor.from(1) failed", K(divisor), K(ret));
|
|
} else {
|
|
size_t idx = 1;
|
|
size_t start_div_num = 2;
|
|
const int64_t LOCAL_ALLOC_TIMES_FOR_LOOP = 4;
|
|
const int64_t LOCAL_BUF_SIZE_FOR_LOOP = LOCAL_ALLOC_TIMES_FOR_LOOP * MAX_CALC_BYTE_LEN;
|
|
char local_buf_for_loop_1[LOCAL_BUF_SIZE_FOR_LOOP];
|
|
char local_buf_for_loop_2[LOCAL_BUF_SIZE_FOR_LOOP];
|
|
ObDataBuffer local_alloc_for_loop_1(local_buf_for_loop_1, LOCAL_BUF_SIZE_FOR_LOOP);
|
|
ObDataBuffer local_alloc_for_loop_2(local_buf_for_loop_2, LOCAL_BUF_SIZE_FOR_LOOP);
|
|
|
|
LOG_DEBUG("start to calc taylor series expansion for sin", K(iter_result), K(tmp_out), K(square_x), K(divisor));
|
|
while (OB_SUCC(ret) && (false == iter_result.is_zero())) {
|
|
if (OB_FAIL(iter_result.mul_v3(square_x, iter_result, local_alloc_for_loop_1, true, false))) {
|
|
LOG_WARN("iter_result.mul_v3(square_x) failed", K(ret), K(iter_result), K(square_x));
|
|
} else if (OB_FAIL(simple_factorial_for_sincos_(start_div_num, local_alloc_for_loop_1, divisor))) {
|
|
LOG_WARN("simple_factorial_for_sincos_(start_div_num) failed", K(ret));
|
|
} else if (OB_FAIL(iter_result.div_v3(divisor, iter_result, local_alloc_for_loop_1, ObNumber::OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("iter_result.div_v3(divisor) failed", K(ret), K(iter_result));
|
|
} else {
|
|
if (idx & 1) { // 奇数次
|
|
if (OB_FAIL(tmp_out.sub_v3(iter_result, tmp_out, local_alloc_for_loop_1, true, false))) {
|
|
LOG_WARN("tmp_out.sub_v3(iter_result) failed", K(ret), K(tmp_out));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(tmp_out.add_v3(iter_result, tmp_out, local_alloc_for_loop_1, true, false))) {
|
|
LOG_WARN("tmp_out.add_v3(iter_result) failed", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
LOG_DEBUG("iteration computing", K(iter_result), K(tmp_out));
|
|
local_alloc_for_loop_2.free();
|
|
std::swap(local_alloc_for_loop_1, local_alloc_for_loop_2);
|
|
idx += 1;
|
|
start_div_num += 2;
|
|
}
|
|
}
|
|
LOG_DEBUG("iteration done", K(tmp_out));
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(out.from(tmp_out, allocator))) {
|
|
LOG_WARN("out.from(tmp_out) failed", K(out), K(tmp_out));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// 1. 将输入转换到[0, 2π]
|
|
// 2. 将输入转换到[0, π]
|
|
// 3. 通过泰勒展开计算sin结果
|
|
int ObNumber::sin(ObNumber &out, ObIAllocator &allocator, const bool do_rounding) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool neg = false;;
|
|
ObNumber pi;
|
|
ObNumber range_low; // 0
|
|
ObNumber range_high; // 2π
|
|
ObNumber tmp_out;
|
|
ObNumber transformed_x; // between [0, π]
|
|
ObNumber x = *this;
|
|
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 7;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
if (x.is_zero()) {
|
|
if (OB_FAIL(out.from(static_cast<int64_t>(0), allocator))) {
|
|
LOG_WARN("out.from(0) failed", K(ret), K(out));
|
|
}
|
|
} else if (OB_FAIL(get_npi_(static_cast<int64_t>(2), range_high, local_alloc, false))) {
|
|
LOG_WARN("get_npi_(2) failed", K(ret));
|
|
} else if (OB_FAIL(get_npi_(static_cast<int64_t>(1), pi, local_alloc, false))) {
|
|
LOG_WARN("get_npi_(1) failed", K(ret));
|
|
} else {
|
|
range_low.set_zero();
|
|
if (x.is_negative()) {
|
|
neg = !neg;
|
|
if (OB_FAIL(x.negate(x, local_alloc))) {
|
|
LOG_WARN("x.negate() failed", K(x), K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (!(range_low <= x && x <= range_high)) {
|
|
if (OB_FAIL(x.rem(range_high, transformed_x, local_alloc))) {
|
|
LOG_WARN("x.rem(range_high) failed", K(ret), K(x), K(range_high));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(transformed_x.from(x, local_alloc))) {
|
|
LOG_WARN("transformed_x.from(x) failed", K(x), K(ret));
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
// transformed_x now in [0, 2π]
|
|
// make it in [0, π]
|
|
if (pi < transformed_x) {
|
|
neg = !neg;
|
|
if (OB_FAIL(transformed_x.sub_v3(pi, transformed_x, local_alloc, true, false))) {
|
|
LOG_WARN("transformed_x.sub_v3(pi) failed", K(transformed_x), K(pi));
|
|
}
|
|
}
|
|
}
|
|
LOG_DEBUG("transform input done, start to taylor series expansion", K(neg), K(transformed_x), K(x));
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(taylor_series_sin_(transformed_x, tmp_out, local_alloc, range_low, pi))) {
|
|
LOG_WARN("taylor_series_sin_(transformed_x) failed", K(ret), K(transformed_x));
|
|
} else {
|
|
if (neg) {
|
|
if (OB_FAIL(tmp_out.negate(tmp_out, local_alloc))) {
|
|
LOG_WARN("tmp_out.negate() failed", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(tmp_out.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(out.from(tmp_out, allocator))) {
|
|
LOG_WARN("out.from(tmp_out) failed", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LOG_DEBUG("sin done", K(out));
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::taylor_series_cos_(const ObNumber &transformed_x, ObNumber &out, ObIAllocator &allocator, const ObNumber &range_low, const ObNumber &range_high) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
if (!(range_low <= transformed_x && transformed_x <= range_high)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("transform x before do taylor series expansion", K(transformed_x));
|
|
} else {
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 6;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
ObNumber iter_result;
|
|
ObNumber divisor;
|
|
ObNumber square_x;
|
|
ObNumber two;
|
|
ObNumber one = ObNumber::get_positive_one();
|
|
ObNumber tmp_out;
|
|
|
|
if (OB_FAIL(square_x.from(transformed_x, local_alloc))) {
|
|
LOG_WARN("square_x.from failed", K(square_x), K(transformed_x), K(ret));
|
|
} else if (OB_FAIL(square_x.mul_v3(transformed_x, square_x, local_alloc, true, false))) {
|
|
LOG_WARN("square_x.mul failed", K(square_x), K(transformed_x), K(ret));
|
|
} else if (OB_FAIL(two.from(static_cast<int64_t>(2), local_alloc))) {
|
|
LOG_WARN("two.from(2) failed", K(ret));
|
|
} else if (OB_FAIL(divisor.from(two, local_alloc))) {
|
|
LOG_WARN("divisor.from(1) failed", K(divisor), K(ret));
|
|
} else if (OB_FAIL(square_x.div_v3(two, iter_result, local_alloc, ObNumber::OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("square_x.div_v3(two) failed", K(square_x), K(ret));
|
|
} else if (OB_FAIL(one.sub_v3(iter_result, tmp_out, local_alloc, true, false))) {
|
|
LOG_WARN("one.sub_v3(iter_result) failed", K(iter_result), K(one), K(ret));
|
|
} else {
|
|
size_t idx = 2;
|
|
size_t start_div_num = 3;
|
|
|
|
const int64_t LOCAL_ALLOC_TIMES_FOR_LOOP = 4;
|
|
const int64_t LOCAL_BUF_SIZE_FOR_LOOP = LOCAL_ALLOC_TIMES_FOR_LOOP * MAX_CALC_BYTE_LEN;
|
|
char local_buf_for_loop_1[LOCAL_BUF_SIZE_FOR_LOOP];
|
|
char local_buf_for_loop_2[LOCAL_BUF_SIZE_FOR_LOOP];
|
|
ObDataBuffer local_alloc_for_loop_1(local_buf_for_loop_1, LOCAL_BUF_SIZE_FOR_LOOP);
|
|
ObDataBuffer local_alloc_for_loop_2(local_buf_for_loop_2, LOCAL_BUF_SIZE_FOR_LOOP);
|
|
|
|
LOG_DEBUG("before while", K(iter_result), K(tmp_out), K(square_x), K(divisor));
|
|
while (OB_SUCC(ret) && (false == iter_result.is_zero())) {
|
|
if (OB_FAIL(iter_result.mul_v3(square_x, iter_result, local_alloc_for_loop_1, true, false))) {
|
|
LOG_WARN("iter_result.mul_v3(square_x) failed", K(ret), K(iter_result));
|
|
} else if (OB_FAIL(simple_factorial_for_sincos_(start_div_num, local_alloc_for_loop_1, divisor))) {
|
|
LOG_WARN("simple_factorial_for_sincos_(start_div_num) failed", K(ret));
|
|
} else if (OB_FAIL(iter_result.div_v3(divisor, iter_result, local_alloc_for_loop_1, ObNumber::OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("iter_result.div_v3(divisor) failed", K(ret), K(iter_result), K(divisor));
|
|
} else {
|
|
if (idx & 1) { // 奇数次循环
|
|
if (OB_FAIL(tmp_out.sub_v3(iter_result, tmp_out, local_alloc_for_loop_1, true, false))) {
|
|
LOG_WARN("tmp_out.sub_v3(iter_result) failed", K(ret), K(tmp_out), K(iter_result));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(tmp_out.add_v3(iter_result, tmp_out, local_alloc_for_loop_1, true, false))) {
|
|
LOG_WARN("tmp_out.add_v3(iter_result) failed", K(ret), K(tmp_out), K(iter_result));
|
|
}
|
|
}
|
|
LOG_DEBUG("iteration computing", K(idx), K(tmp_out));
|
|
local_alloc_for_loop_2.free();
|
|
std::swap(local_alloc_for_loop_1, local_alloc_for_loop_2);
|
|
idx += 1;
|
|
start_div_num += 2;
|
|
}
|
|
}
|
|
LOG_DEBUG("iteration done", K(tmp_out));
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(out.from(tmp_out, allocator))) {
|
|
LOG_WARN("out.from(tmp_out) failed", K(out), K(tmp_out));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// 1. 将输入转换到[-π/2, 3π/2]
|
|
// 2. 将输入转换到[-π/2, π/2]
|
|
// 3. 通过泰勒展开计算cos结果
|
|
// 也可以通过cos(x) = sin(x+π/2),或者cos(x) = sqrt(1 - sin(x)^2)来计算cos,
|
|
// 这样速度更快,但是精度不如用泰勒展开
|
|
int ObNumber::cos(ObNumber &out, ObIAllocator &allocator, const bool do_rounding) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
if (is_zero()) {
|
|
if (OB_FAIL(out.from(static_cast<int64_t>(1), allocator))) {
|
|
LOG_WARN("out.from(1) failed", K(ret));
|
|
}
|
|
} else {
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 10;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
bool neg = false;
|
|
ObNumber x = *this;
|
|
ObNumber pi;
|
|
ObNumber tmp_out;
|
|
ObNumber half_pi; // π/2
|
|
ObNumber double_pi; // 2π
|
|
ObNumber range_low; // -π/2
|
|
ObNumber range_high; // 3π/2
|
|
ObNumber transformed_x;
|
|
|
|
if (x.is_negative()) {
|
|
// cos(x)是偶函数,不需要设置neg
|
|
if (OB_FAIL(x.negate(x, local_alloc))) {
|
|
LOG_WARN("x.negate failed", K(ret), K(x));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(get_npi_(static_cast<double>(-1)/2, range_low, local_alloc, false))) {
|
|
LOG_WARN("get_npi_(-1/2) failed", K(ret));
|
|
} else if (OB_FAIL(get_npi_(static_cast<double>(1)/2, half_pi, local_alloc, false))) {
|
|
LOG_WARN("get_npi_(1/2) failed", K(ret));
|
|
} else if (OB_FAIL(get_npi_(static_cast<double>(3)/2, range_high, local_alloc, false))) {
|
|
LOG_WARN("get_npi_(3/2) failed", K(ret));
|
|
} else if (OB_FAIL(get_npi_(static_cast<int64_t>(2), double_pi, local_alloc, false))) {
|
|
LOG_WARN("get_npi_(2) failed", K(ret));
|
|
} else if (OB_FAIL(get_npi_(static_cast<int64_t>(1), pi, local_alloc, false))) {
|
|
LOG_WARN("get_npi_(1) failed", K(ret));
|
|
} else {
|
|
if (range_low <= x && x <= range_high) {
|
|
if (OB_FAIL(transformed_x.from(x, local_alloc))) {
|
|
LOG_WARN("transformed_x.from(x) failed", K(ret), K(transformed_x));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(x.rem(double_pi, transformed_x, local_alloc))) {
|
|
LOG_WARN("x.rem(double_pi) failed", K(ret), K(x), K(double_pi));
|
|
} else if (range_high < transformed_x) {
|
|
if (OB_FAIL(transformed_x.sub_v3(double_pi, transformed_x, local_alloc, true, false))) {
|
|
LOG_WARN("transformed_x.sub_v3(double_pi) failed", K(ret), K(transformed_x), K(double_pi));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
// transformed_x no in[-π/2, 3π/2]
|
|
// make in [-π/2, π/2]
|
|
if (half_pi < transformed_x) {
|
|
neg = !neg;
|
|
if (OB_FAIL(transformed_x.sub_v3(pi, transformed_x, local_alloc, true, false))) {
|
|
LOG_WARN("transformed_x.sub_v3(pi) failed", K(ret), K(transformed_x), K(pi));
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG("transform x done, starto taylor series expansion", K(double_pi), K(range_low),
|
|
K(range_high), K(transformed_x),
|
|
K(x), K(tmp_out));
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(taylor_series_cos_(transformed_x, tmp_out, local_alloc, range_low, half_pi))) {
|
|
LOG_WARN("taylor_series_cos_(transformed_x) failed", K(ret), K(transformed_x), K(tmp_out));
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(tmp_out.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (neg) {
|
|
if (OB_FAIL(tmp_out.negate(tmp_out, local_alloc))) {
|
|
LOG_WARN("tmp_out.negate() failed", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(out.from(tmp_out, allocator))) {
|
|
LOG_WARN("out.from(tmp_out) failed", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// 直接使用sin/cos来计算tan
|
|
int ObNumber::tan(ObNumber &out, ObIAllocator &allocator, const bool do_rounding) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
// remember to change this
|
|
const int64_t LOCAL_ALLOC_TIMES = 3;
|
|
const int64_t LOCAL_BUF_SIZE = LOCAL_ALLOC_TIMES * MAX_CALC_BYTE_LEN;
|
|
char local_buf[LOCAL_BUF_SIZE];
|
|
ObDataBuffer local_alloc(local_buf, LOCAL_BUF_SIZE);
|
|
|
|
bool sin_is_zero = false;
|
|
bool cos_is_zero = false;
|
|
ObNumber sin_out;
|
|
ObNumber cos_out;
|
|
ObNumber tmp_out;
|
|
|
|
if (OB_FAIL(sin(sin_out, local_alloc, false))) {
|
|
LOG_WARN("sin(x) failed", K(*this), K(ret));
|
|
} else if (sin_out.is_zero()) {
|
|
sin_is_zero = true;
|
|
}
|
|
|
|
if (OB_SUCC(ret) && !sin_is_zero) {
|
|
if (OB_FAIL(cos(cos_out, local_alloc, false))) {
|
|
LOG_WARN("cos(x) failed", K(*this), K(ret));
|
|
} else if (cos_out.is_zero()) {
|
|
cos_is_zero = true;
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG("start to calc sin/cos", K(*this), K(sin_out), K(cos_out), K(sin_is_zero), K(cos_is_zero));
|
|
if (OB_SUCC(ret)) {
|
|
if (sin_is_zero) {
|
|
out.set_zero();
|
|
} else if (cos_is_zero) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
LOG_WARN("cos(x) is zero", K(*this), K(ret));
|
|
} else {
|
|
if (OB_FAIL(sin_out.div_v3(cos_out, tmp_out, local_alloc, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("sin/cos failed", K(sin_out), K(cos_out), K(ret));
|
|
} else if (do_rounding) {
|
|
if (OB_FAIL(tmp_out.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("tmp_out.round_scale_v3_() fail", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(out.from(tmp_out, allocator))) {
|
|
LOG_WARN("out.from(tmp_out) failed", K(ret), K(tmp_out));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//asin(x) = atan(x/sqrt(1-x^2))
|
|
int ObNumber::asin(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t LOCAL_ALLOCATE_TIME = 8;
|
|
const char half_pi_buf[] = "1.57079632679489661923132169163975144209858469968755291048747229615";
|
|
char buf_alloc_local[ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME];
|
|
ObDataBuffer allocator_local(buf_alloc_local, ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME);
|
|
ObNumber res;
|
|
ObNumber one;
|
|
|
|
if (OB_FAIL(one.from(static_cast<int64_t>(1), allocator_local))) {
|
|
LOG_WARN("create const one failed", K(one), K(ret));
|
|
} else if (abs_compare(one) >= 0) {
|
|
if (OB_UNLIKELY(1 == abs_compare(one))) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
LOG_WARN("parameter abs larger than 1", K(ret));
|
|
} else if (0 == compare(one)) {
|
|
if (OB_FAIL(res.from(half_pi_buf, allocator,NULL,NULL,false))) {
|
|
LOG_WARN("res from pi/2 failed", K(res), K(ret));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(res.from(half_pi_buf, allocator_local,NULL,NULL,false))) {
|
|
LOG_WARN("res from pi/2 failed", K(res), K(ret));
|
|
} else if(OB_FAIL(res.negate(res, allocator))) {
|
|
LOG_WARN("res from pi/2 negate failed", K(res), K(ret));
|
|
}
|
|
}
|
|
} else {
|
|
ObNumber atan_arg;
|
|
if (OB_FAIL(mul_v3(*this, atan_arg, allocator_local, true, false))) {
|
|
LOG_WARN("*this mul *this failed", K(*this), K(atan_arg), K(ret));
|
|
} else if (OB_FAIL(one.sub_v3(atan_arg, atan_arg, allocator_local, true, false))) {
|
|
LOG_WARN("one sub atan_arg failed", K(one), K(atan_arg), K(ret));
|
|
} else if (OB_FAIL(atan_arg.sqrt(atan_arg, allocator_local, false))) {
|
|
LOG_WARN("atan_arg sqrt failed", K(atan_arg), K(ret));
|
|
} else if (OB_FAIL(div_v3(atan_arg, atan_arg, allocator_local, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("*this div atan_arg failed", K(*this), K(atan_arg), K(ret));
|
|
} else if (OB_FAIL(atan_arg.atan(res, allocator, false))) {
|
|
LOG_WARN("atan_arg atan failed", K(atan_arg), K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "asin [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
//acos(x) = pi/2 - asin(x)
|
|
int ObNumber::acos(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t LOCAL_ALLOCATE_TIME = 2;
|
|
const char half_pi_buf[] = "1.57079632679489661923132169163975144209858469968755291048747229615";
|
|
char buf_alloc_local[ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME];
|
|
ObDataBuffer allocator_local(buf_alloc_local, ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME);
|
|
ObNumber res;
|
|
|
|
if (OB_UNLIKELY((*this) > static_cast<int64_t>(1) || (*this) < static_cast<int64_t>(-1))) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
LOG_WARN("parameter abs larger than 1", K(ret));
|
|
} else if ((*this) == static_cast<int64_t>(1)) {
|
|
if (OB_FAIL(res.from(static_cast<int64_t>(0), allocator))) {
|
|
LOG_WARN("res from const 1 failed", K(res), K(ret));
|
|
}
|
|
} else {
|
|
ObNumber half_pi;
|
|
if (OB_FAIL(half_pi.from(half_pi_buf, allocator_local,NULL,NULL,false))) {
|
|
LOG_WARN("half_pi from const pi/2 failed", K(half_pi), K(ret));
|
|
} else if (OB_FAIL(asin(res, allocator_local, false))) {
|
|
LOG_WARN("res=this->asin failed", K(*this), K(res), K(ret));
|
|
} else if (OB_FAIL(half_pi.sub_v3(res, res, allocator, true, false))) {
|
|
LOG_WARN("res = half_pi sub res failed", K(res), K(half_pi), K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "acos [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
//parameter reduction : atan(x) = 2atan(x/(1+sqrt(1+x^2)))
|
|
//taylor series: atan(x) = x-(x^3)/3+(x^5)/5-(x^7)/7+... when |x|<1
|
|
int ObNumber::atan(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t MAX_LOOP_ALLOCATE_TIME = 5;
|
|
const int64_t MAX_TAYLOR_SERIES_COUNT = 200;
|
|
const char PARAM_MAX_VALUE[5] = "0.15";
|
|
char buf_alloc_doublex[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc_this[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc_res[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc_tmp[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc_iter1[ObNumber::MAX_BYTE_LEN * MAX_LOOP_ALLOCATE_TIME];
|
|
char buf_alloc_iter2[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc_const1[ObNumber::MAX_BYTE_LEN];
|
|
char buf_alloc_const2[ObNumber::MAX_BYTE_LEN];
|
|
ObDataBuffer allocator_doublex(buf_alloc_doublex, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator_this(buf_alloc_this, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator_res(buf_alloc_res, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator_tmp(buf_alloc_tmp, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator_iter1(buf_alloc_iter1, ObNumber::MAX_BYTE_LEN * MAX_LOOP_ALLOCATE_TIME);
|
|
ObDataBuffer allocator_iter2(buf_alloc_iter2, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator_const1(buf_alloc_const1, ObNumber::MAX_BYTE_LEN);
|
|
ObDataBuffer allocator_const2(buf_alloc_const2, ObNumber::MAX_BYTE_LEN);
|
|
ObNumber res;
|
|
ObNumber copy_this;
|
|
ObNumber param_bound;
|
|
const char half_pi_buf[] = "1.57079632679489661923132169163975144209858469968755291048747229615";
|
|
|
|
if (OB_FAIL(res.from(static_cast<int64_t>(0), allocator_res))) {
|
|
LOG_WARN("res from const 0 failed", K(res), K(ret));
|
|
} else if (OB_FAIL(param_bound.from(PARAM_MAX_VALUE, allocator_const1))) {
|
|
LOG_WARN("create const 0.5 failed", K(param_bound), K(ret));
|
|
} else if (OB_FAIL(copy_this.from(*this, allocator_this))) {
|
|
LOG_WARN("copy *this failed", K(copy_this), K(ret));
|
|
} else {
|
|
ObNumber one;
|
|
ObNumber tmp;
|
|
uint8_t reduction_count = 0;
|
|
//parameter reduction: arctan x = 2arctan(x/(1+sqrt(1+x*x)))
|
|
while (OB_SUCC(ret) && 1 == copy_this.abs_compare(param_bound)) {
|
|
if (0 == reduction_count && OB_FAIL(one.from(static_cast<int64_t>(1),allocator_const2))) {
|
|
LOG_WARN("create const 1 failed", K(one), K(ret));
|
|
} else if (OB_FAIL(tmp.from(copy_this, allocator_iter1))) {
|
|
LOG_WARN("tmp copy from copy_this failed", K(copy_this), K(tmp), K(ret));
|
|
} else if (OB_FAIL(tmp.mul_v3(copy_this, tmp, allocator_iter1, true, false))) {
|
|
if (OB_INTEGER_PRECISION_OVERFLOW == ret) {
|
|
ret = OB_SUCCESS;
|
|
copy_this.set_zero();
|
|
allocator_res.free();
|
|
if (OB_FAIL(res.from(half_pi_buf, allocator_res,NULL,NULL,false))) {
|
|
LOG_WARN("res from pi/2 failed", K(res), K(ret));
|
|
} else if (is_negative()) {
|
|
res = res.negate();
|
|
}
|
|
} else {
|
|
LOG_WARN("tmp = tmp mul copy_this failed", K(copy_this), K(tmp), K(ret));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(tmp.add_v3(one, tmp, allocator_iter1, true, false))) {
|
|
LOG_WARN("tmp add one failed", K(copy_this), K(tmp), K(one), K(ret));
|
|
} else if (OB_FAIL(tmp.sqrt(tmp, allocator_iter1, false))) {
|
|
LOG_WARN("tmp sqrt failed", K(copy_this), K(tmp), K(ret));
|
|
} else if (OB_FAIL(tmp.add_v3(one, tmp, allocator_iter1, true, false))) {
|
|
LOG_WARN("tmp add one failed", K(copy_this), K(tmp), K(one), K(ret));
|
|
} else if (OB_FAIL(copy_this.div_v3(tmp, copy_this, allocator_tmp, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("copy_this div tmp failed", K(copy_this), K(tmp), K(ret));
|
|
} else {
|
|
std::swap(allocator_tmp, allocator_this);
|
|
allocator_tmp.free();
|
|
allocator_iter1.free();
|
|
reduction_count++;
|
|
}
|
|
}
|
|
}
|
|
ObNumber doublex;
|
|
ObNumber taylor_series;
|
|
ObNumber iter_const;
|
|
int64_t count = 0;
|
|
allocator_const1.free();
|
|
//taylor series: atan(x) = x-(x^3)/3+(x^5)/5-(x^7)/7+... when |x|<1
|
|
if (OB_FAIL(taylor_series.from(copy_this, allocator_iter2))) {
|
|
LOG_WARN("taylor series from copy_this failed", K(copy_this), K(taylor_series), K(ret));
|
|
} else if (OB_FAIL(copy_this.mul_v3(copy_this, doublex, allocator_doublex, true, false))) {
|
|
LOG_WARN("doublex = copy_this*copy_this failed", K(copy_this), K(doublex), K(ret));
|
|
} else if (OB_FAIL(iter_const.from(static_cast<int64_t>(1), allocator_const1))){
|
|
LOG_WARN("iter_const from 1 failed", K(iter_const), K(ret));
|
|
} else {
|
|
while (OB_SUCC(ret) && !taylor_series.is_zero() && count < MAX_TAYLOR_SERIES_COUNT) {
|
|
if (OB_FAIL(res.add_v3(taylor_series, res, allocator_tmp, true, false))) {
|
|
LOG_WARN("res add taylor failed", K(res), K(taylor_series), K(ret));
|
|
} else if (OB_FAIL(taylor_series.mul_v3(iter_const, taylor_series, allocator_iter1, true, false))) {
|
|
LOG_WARN("taylor_series mul iter_const failed", K(taylor_series), K(iter_const), K(ret));
|
|
} else {
|
|
std::swap(allocator_tmp, allocator_res);
|
|
allocator_tmp.free();
|
|
allocator_iter2.free();
|
|
allocator_const1.free();
|
|
count++;
|
|
if (OB_FAIL(iter_const.from(static_cast<int64_t>(2 * count + 1), allocator_const1))) {
|
|
LOG_WARN("iter_const from 2*count+1 failed", K(count), K(iter_const), K(ret));
|
|
} else if (OB_FAIL(taylor_series.div_v3(iter_const, taylor_series, allocator_iter1, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("taylor_series div iter_const failed", K(taylor_series), K(iter_const), K(ret));
|
|
} else if (OB_FAIL(taylor_series.mul_v3(doublex, taylor_series, allocator_iter1, true, false))) {
|
|
LOG_WARN("taylor_series mul doublex failed", K(taylor_series), K(doublex), K(ret));
|
|
} else if (OB_FAIL(taylor_series.negate(taylor_series, allocator_iter2))) {
|
|
LOG_WARN("taylor_series negate failed", K(taylor_series), K(ret));
|
|
} else {
|
|
allocator_iter1.free();
|
|
}
|
|
}
|
|
}
|
|
while (OB_SUCC(ret) && reduction_count > 0) {
|
|
if (OB_FAIL(res.add_v3(res, res, allocator_tmp, true, false))) {
|
|
LOG_WARN("res=res+res failed", K(res), K(ret));
|
|
} else {
|
|
std::swap(allocator_tmp, allocator_res);
|
|
allocator_tmp.free();
|
|
reduction_count--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else if (OB_FAIL(value.from(res, allocator))){
|
|
LOG_WARN("value copy from res failed", K(ret), K(res));
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "atan [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::atan2(const ObNumber &other, ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
const char half_pi_buf[] = "1.57079632679489661923132169163975144209858469968755291048747229615";
|
|
const char pi_buf[] = "3.14159265358979323846264338327950288419716939937510582097494459231";
|
|
if (is_zero()) {
|
|
if (OB_UNLIKELY(other.is_zero())) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
LOG_WARN("atan2 numeric overflow", K(ret));
|
|
} else if (other.is_negative()) {
|
|
if (OB_FAIL(res.from(pi_buf, allocator, NULL, NULL, false))) {
|
|
LOG_WARN("res from pi failed", K(ret), K(res));
|
|
}
|
|
} else if (OB_FAIL(res.from("0", allocator, NULL, NULL, false))) {
|
|
LOG_WARN("res from 0 failed", K(ret), K(res));
|
|
}
|
|
} else if (other.is_zero()) {
|
|
if (OB_FAIL(res.from(half_pi_buf, allocator,NULL,NULL,false))) {
|
|
LOG_WARN("res from pi/2 failed", K(res), K(ret));
|
|
} else if (is_negative()) {
|
|
res = res.negate();
|
|
}
|
|
} else {
|
|
bool other_negative = other.is_negative();
|
|
int64_t buf_len = other_negative ? ObNumber::MAX_BYTE_LEN * 3 : ObNumber::MAX_BYTE_LEN;
|
|
char buf_alloc1[buf_len];
|
|
ObDataBuffer allocator1(buf_alloc1, buf_len);
|
|
ObNumber quotient;
|
|
if (OB_FAIL(div_v3(other, quotient, allocator1, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("this div other failed", K(*this), K(other), K(ret));
|
|
} else if (OB_FAIL(quotient.atan(res, other_negative ? allocator1 : allocator, false))) {
|
|
LOG_WARN("quotient atan_ failed", K(quotient), K(ret));
|
|
} else if (other_negative) {
|
|
ObNumber num_pi;
|
|
if (OB_FAIL(num_pi.from(pi_buf, allocator1, NULL, NULL, false))) {
|
|
LOG_WARN("num from pi failed", K(ret));
|
|
} else if (is_negative() && OB_FAIL(res.sub_v3(num_pi, res, allocator, true, false))) {
|
|
LOG_WARN("res sub pi failed", K(ret));
|
|
} else if (!is_negative() && OB_FAIL(res.add_v3(num_pi, res, allocator, true, false))) {
|
|
LOG_WARN("res add pi failed", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "atan2 [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::add_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc augend_desc;
|
|
Desc addend_desc;
|
|
augend_desc.desc_ = d_.desc_;
|
|
addend_desc.desc_ = other.d_.desc_;
|
|
LOG_DEBUG("add_", K(ret), KPC(this), K(other));
|
|
if (is_zero()) {
|
|
ret = res.deep_copy(other, allocator);
|
|
} else if (other.is_zero()) {
|
|
ret = res.deep_copy(*this, allocator);
|
|
} else if (augend_desc.sign_ == addend_desc.sign_) {
|
|
ObCalcVector augend;
|
|
ObCalcVector addend;
|
|
int64_t shift = exp_integer_align_(augend_desc, addend_desc);
|
|
exp_shift_(shift, augend_desc);
|
|
exp_shift_(shift, addend_desc);
|
|
if (OB_FAIL(augend.init(augend_desc.desc_, digits_))) {
|
|
LOG_WARN("fail to assign values", K(ret));
|
|
} else if (OB_FAIL(addend.init(addend_desc.desc_, other.digits_))) {
|
|
LOG_WARN("fail to assign values", K(ret));
|
|
} else {
|
|
ObCalcVector sum;
|
|
int64_t sum_size = std::max(augend.size(), addend.size()) + 1;
|
|
if (OB_FAIL(sum.ensure(sum_size))) {
|
|
LOG_WARN("Fail to ensure sum_size", K(ret));
|
|
} else if (OB_FAIL(poly_poly_add(augend, addend, sum))) {
|
|
_OB_LOG(WARN, "[%s] add [%s] fail ret=%d", to_cstring(*this), to_cstring(other), ret);
|
|
} else {
|
|
Desc res_desc = exp_max_(augend_desc, addend_desc);
|
|
bool carried = (0 != sum.at(0));
|
|
exp_shift_(-shift + (carried ? 1 : 0), res_desc);
|
|
ret = res.from_v2_(res_desc.desc_, sum, allocator);
|
|
}
|
|
}
|
|
} else {
|
|
ObNumber subtrahend;
|
|
StackAllocator stack_allocator;
|
|
if (OB_FAIL(other.negate(subtrahend, stack_allocator))) {
|
|
_OB_LOG(WARN, "nagate [%s] fail, ret=%d", to_cstring(other), ret);
|
|
} else {
|
|
ret = sub_(subtrahend, res, allocator);
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "add_ [%s] + [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::sub_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc minuend_desc;
|
|
Desc subtrahend_desc;
|
|
minuend_desc.desc_ = d_.desc_;
|
|
subtrahend_desc.desc_ = other.d_.desc_;
|
|
LOG_DEBUG("sub", K(ret), KPC(this), K(other));
|
|
if (is_zero()) {
|
|
ret = other.negate_(res, allocator);
|
|
} else if (other.is_zero()) {
|
|
ret = res.from_(*this, allocator);
|
|
} else if (minuend_desc.sign_ == subtrahend_desc.sign_) {
|
|
ObCalcVector minuend;
|
|
ObCalcVector subtrahend;
|
|
int64_t shift = exp_integer_align_(minuend_desc, subtrahend_desc);
|
|
exp_shift_(shift, minuend_desc);
|
|
exp_shift_(shift, subtrahend_desc);
|
|
if (OB_FAIL(minuend.init(minuend_desc.desc_, digits_))) {
|
|
LOG_WARN("fail to assign values", K(ret));
|
|
} else if (OB_FAIL(subtrahend.init(subtrahend_desc.desc_, other.digits_))) {
|
|
LOG_WARN("fail to assign values", K(ret));
|
|
} else {
|
|
ObCalcVector remainder;
|
|
int64_t remainder_size = std::max(minuend.size(), subtrahend.size());
|
|
|
|
bool sub_negative = false;
|
|
if (OB_FAIL(remainder.ensure(remainder_size))) {
|
|
LOG_WARN("remainder.ensure(remainder_size) fails", K(ret));
|
|
} else if (OB_FAIL(poly_poly_sub(minuend, subtrahend, remainder, sub_negative))) {
|
|
_OB_LOG(WARN, "[%s] sub [%s] fail ret=%d", to_cstring(*this), to_cstring(other), ret);
|
|
} else {
|
|
Desc res_desc = exp_max_(minuend_desc, subtrahend_desc);
|
|
for (int64_t i = 0; i < remainder.size() - 1; ++i) {
|
|
if (0 != remainder.at(i)) {
|
|
break;
|
|
}
|
|
++shift;
|
|
}
|
|
exp_shift_(-shift, res_desc);
|
|
|
|
bool arg_negative = (NEGATIVE == minuend_desc.sign_);
|
|
if (sub_negative == arg_negative) {
|
|
res_desc.sign_ = POSITIVE;
|
|
} else {
|
|
res_desc.sign_ = NEGATIVE;
|
|
}
|
|
|
|
if (res_desc.sign_ != minuend_desc.sign_) {
|
|
res_desc.exp_ = (0x7f & ~res_desc.exp_);
|
|
++res_desc.exp_;
|
|
}
|
|
|
|
ret = res.from_v2_(res_desc.desc_, remainder, allocator);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
ObNumber addend;
|
|
StackAllocator stack_allocator;
|
|
if (OB_FAIL(other.negate(addend, stack_allocator))) {
|
|
_OB_LOG(WARN, "nagate [%s] fail, ret=%d", to_cstring(other), ret);
|
|
} else {
|
|
ret = add_(addend, res, allocator);
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "sub_ [%s] - [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::calc_desc_and_check_(const uint32_t base_desc, Desc &desc, int64_t exp, uint8_t len) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
Desc d;
|
|
d.desc_ = base_desc;
|
|
d.len_ = len;
|
|
d.reserved_ = 0; // keep always 0.
|
|
d.exp_ = 0x7f & (uint8_t)(EXP_ZERO + exp);
|
|
if (d.sign_ == NEGATIVE) {
|
|
d.exp_ = (0x7f & (~d.exp_));
|
|
++d.exp_;
|
|
}
|
|
if (OB_FAIL(exp_check_(d, lib::is_oracle_mode()))) {
|
|
} else {
|
|
desc = d;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::add_v2_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc augend_desc;
|
|
Desc addend_desc;
|
|
augend_desc.desc_ = d_.desc_;
|
|
addend_desc.desc_ = other.d_.desc_;
|
|
LOG_DEBUG("add_v2_", K(ret), KPC(this), K(other));
|
|
if (is_zero()) {
|
|
ret = res.deep_copy(other, allocator);
|
|
} else if (other.is_zero()) {
|
|
ret = res.deep_copy(*this, allocator);
|
|
} else if (augend_desc.sign_ == addend_desc.sign_) {
|
|
int64_t augend_exp = get_decode_exp(augend_desc.desc_);
|
|
int64_t addend_exp = get_decode_exp(addend_desc.desc_);
|
|
|
|
if (augend_exp >= addend_exp + OB_MAX_DECIMAL_DIGIT) {
|
|
ret = res.deep_copy(*this, allocator);
|
|
} else if (addend_exp >= augend_exp + OB_MAX_DECIMAL_DIGIT) {
|
|
ret = res.deep_copy(other, allocator);
|
|
} else {
|
|
int64_t sum_exp = std::max(augend_exp, addend_exp);
|
|
int32_t offset = 0;
|
|
|
|
uint32_t *augend_digits = digits_;
|
|
uint32_t *addend_digits = other.digits_;
|
|
uint32_t sum_digits[OB_CALC_BUFFER_SIZE] = {};
|
|
|
|
for (int64_t i = 1, curr_exp = sum_exp, augend_id = 0, addend_id = 0;
|
|
i <= OB_MAX_DECIMAL_DIGIT;
|
|
++i, --curr_exp) {
|
|
uint32_t &tmp_digit = sum_digits[i];
|
|
if (curr_exp == augend_exp && augend_id < augend_desc.len_) {
|
|
tmp_digit += augend_digits[augend_id];
|
|
--augend_exp;
|
|
++augend_id;
|
|
}
|
|
if (curr_exp == addend_exp && addend_id < addend_desc.len_) {
|
|
tmp_digit += addend_digits[addend_id];
|
|
--addend_exp;
|
|
++addend_id;
|
|
}
|
|
// LOG_DEBUG("add_v2 ", K(sum_digits[i]), K(i), K(curr_exp), K(augend_exp), K(augend_id), K(addend_exp), K(addend_id));
|
|
}
|
|
|
|
for (int64_t i = OB_MAX_DECIMAL_DIGIT; i > 0; --i) {
|
|
if (sum_digits[i] >= BASE) {
|
|
sum_digits[i] -= BASE;
|
|
++sum_digits[i - 1];
|
|
}
|
|
}
|
|
|
|
if (sum_digits[0] != 0) {
|
|
offset = 0;
|
|
++sum_exp;
|
|
// normalize_digit_(sum_digits, OB_MAX_DECIMAL_DIGIT);
|
|
} else {
|
|
offset = 1;
|
|
}
|
|
|
|
uint8_t len = OB_MAX_DECIMAL_DIGIT;
|
|
while (len >= offset && sum_digits[len] == 0) {
|
|
--len;
|
|
}
|
|
len += (1 - offset);
|
|
|
|
Desc sum_desc;
|
|
uint32_t *digit_mem = NULL;
|
|
if (OB_FAIL(calc_desc_and_check_(d_.desc_,sum_desc, sum_exp, len))) {
|
|
LOG_WARN("fail to calc_desc_and_check_", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + offset, sizeof(uint32_t) * len);
|
|
res.assign(sum_desc.desc_, digit_mem);
|
|
}
|
|
}
|
|
} else {
|
|
ObNumber subtrahend = other.negate();
|
|
ret = sub_v2_(subtrahend, res, allocator);
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(res.round_scale_v2_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "add_v2 [%s] + [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::add_v3(const ObNumber &other, ObNumber &value, ObIAllocator &allocator,
|
|
const bool strict_mode/*true*/, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
const bool use_oracle_mode = is_oracle_mode();
|
|
LOG_DEBUG("add_v3", K(ret), KPC(this), K(other));
|
|
if (OB_UNLIKELY(is_zero())) {
|
|
ret = res.deep_copy_v3(other, allocator);
|
|
} else if (OB_UNLIKELY(other.is_zero())) {
|
|
ret = res.deep_copy_v3(*this, allocator);
|
|
} else if (d_.sign_ == other.d_.sign_) {
|
|
const int64_t this_exp = get_decode_exp(d_);
|
|
const int64_t other_exp = get_decode_exp(other.d_);
|
|
|
|
if (OB_UNLIKELY(this_exp >= other_exp + OB_MAX_DECIMAL_DIGIT)) {
|
|
ret = res.deep_copy_v3(*this, allocator);
|
|
} else if (OB_UNLIKELY(other_exp >= this_exp + OB_MAX_DECIMAL_DIGIT)) {
|
|
ret = res.deep_copy_v3(other, allocator);
|
|
} else {
|
|
Desc augend_desc(d_.desc_);
|
|
Desc addend_desc(other.d_.desc_);
|
|
int64_t sum_exp = this_exp;
|
|
int64_t augend_exp = this_exp;
|
|
int64_t addend_exp = other_exp;
|
|
uint32_t *augend_digits = digits_;
|
|
uint32_t *addend_digits = other.digits_;
|
|
|
|
if (this_exp < other_exp || (this_exp == other_exp && d_.len_ < other.d_.len_)) {
|
|
//make augend is always the larger expr one
|
|
augend_desc.desc_ = other.d_.desc_;
|
|
addend_desc.desc_ = d_.desc_;
|
|
augend_exp = other_exp;
|
|
addend_exp = this_exp;
|
|
augend_digits = other.digits_;
|
|
addend_digits = digits_;
|
|
sum_exp = other_exp;
|
|
}
|
|
int32_t offset = 1;
|
|
uint32_t tmp_sum_digits[OB_CALC_BUFFER_SIZE];//[0] store carry bit
|
|
uint32_t *sum_digits = NULL;
|
|
if (strict_mode) {
|
|
sum_digits = tmp_sum_digits;
|
|
} else {
|
|
if (OB_ISNULL(sum_digits = (uint32_t *)allocator.alloc(sizeof(uint32_t) * OB_CALC_BUFFER_SIZE))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * OB_CALC_BUFFER_SIZE, K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
MEMSET(sum_digits, 0, sizeof(uint32_t) * OB_CALC_BUFFER_SIZE);
|
|
MEMCPY(sum_digits + 1, augend_digits, min(augend_desc.len_, OB_MAX_DECIMAL_DIGIT) * sizeof(uint32_t));
|
|
|
|
//inverse traversal
|
|
const int64_t cur_augend_exp = augend_exp - (augend_desc.len_ - 1);
|
|
int64_t cur_addend_exp = addend_exp - (addend_desc.len_ - 1);
|
|
int64_t cur_addend_id = addend_desc.len_ - 1;
|
|
int64_t cur_exp = std::min(cur_augend_exp, cur_addend_exp);
|
|
uint8_t sum_len = sum_exp - cur_exp + 1;
|
|
if (sum_len > OB_MAX_DECIMAL_DIGIT) {
|
|
const int64_t movex = sum_len - OB_MAX_DECIMAL_DIGIT;
|
|
cur_exp += movex;
|
|
cur_addend_exp += movex;
|
|
cur_addend_id -= movex;
|
|
sum_len = OB_MAX_DECIMAL_DIGIT;
|
|
}
|
|
|
|
uint32_t carry = 0;
|
|
bool check_sum_len = true;
|
|
for (int64_t i = sum_len; i > 0; --i, ++cur_exp) {
|
|
uint32_t &tmp_digit = sum_digits[i];
|
|
tmp_digit += carry;
|
|
if (cur_exp == cur_addend_exp && cur_addend_id >= 0) {
|
|
tmp_digit += addend_digits[cur_addend_id];
|
|
++cur_addend_exp;
|
|
--cur_addend_id;
|
|
}
|
|
|
|
if (tmp_digit >= BASE) {
|
|
tmp_digit -= BASE;
|
|
carry = 1;
|
|
} else {
|
|
carry = 0;
|
|
}
|
|
|
|
if (check_sum_len) {
|
|
if (0 == tmp_digit) {
|
|
--sum_len;
|
|
} else {
|
|
check_sum_len = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (carry != 0) {
|
|
sum_digits[0] = 1;
|
|
++sum_exp;
|
|
++sum_len;
|
|
offset = 0;
|
|
}
|
|
|
|
Desc sum_desc;
|
|
uint32_t *digit_mem = NULL;
|
|
if (OB_FAIL(calc_desc_and_check(d_.desc_, sum_exp, sum_len, sum_desc, use_oracle_mode))) {
|
|
LOG_WARN("fail to calc_desc_and_check_", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (strict_mode) {
|
|
if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * sum_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * sum_len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + offset, sizeof(uint32_t) * sum_len);
|
|
res.assign(sum_desc.desc_, digit_mem);
|
|
}
|
|
} else {
|
|
res.assign(sum_desc.desc_, sum_digits + offset);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ret = sub_v3(other.negate(), res, allocator, strict_mode, do_rounding);
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && res.need_round_after_arithmetic(use_oracle_mode)
|
|
&& OB_FAIL(res.round_scale_v3_(use_oracle_mode ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "add_v3 [%s] + [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::sub_v2_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc minuend_desc;
|
|
Desc subtrahend_desc;
|
|
minuend_desc.desc_ = d_.desc_;
|
|
subtrahend_desc.desc_ = other.d_.desc_;
|
|
LOG_DEBUG("sub_v2_", K(ret), KPC(this), K(other));
|
|
if (is_zero()) {
|
|
ret = other.negate_v2_(res, allocator);
|
|
} else if (other.is_zero()) {
|
|
ret = res.deep_copy(*this, allocator);
|
|
} else if (minuend_desc.sign_ == subtrahend_desc.sign_) {
|
|
|
|
int64_t minuend_exp = get_decode_exp(minuend_desc.desc_);
|
|
int64_t subtrahend_exp = get_decode_exp(subtrahend_desc.desc_);
|
|
|
|
if (minuend_exp >= subtrahend_exp + OB_MAX_DECIMAL_DIGIT) {
|
|
ret = res.deep_copy(*this, allocator);
|
|
} else if (subtrahend_exp >= minuend_exp + OB_MAX_DECIMAL_DIGIT) {
|
|
ret = other.negate_v2_(res, allocator);
|
|
} else {
|
|
int64_t sum_exp = std::max(minuend_exp, subtrahend_exp);
|
|
int32_t sum_digits[OB_CALC_BUFFER_SIZE] = {};
|
|
int first_digit_flag = 0;
|
|
bool arg_negative = (NEGATIVE == minuend_desc.sign_);
|
|
bool sub_negative = false;
|
|
uint32_t *minuend_digits = digits_;
|
|
uint32_t *subtrahend_digits = other.digits_;
|
|
|
|
for (int64_t i=0, curr_exp = sum_exp, minuend_id = 0, subtrahend_id = 0;
|
|
i <= OB_MAX_DECIMAL_DIGIT;
|
|
++i, --curr_exp) {
|
|
int32_t &tmp_digit = sum_digits[i];
|
|
if (curr_exp == minuend_exp && minuend_id < minuend_desc.len_) {
|
|
tmp_digit += minuend_digits[minuend_id];
|
|
--minuend_exp;
|
|
++minuend_id;
|
|
}
|
|
if (curr_exp == subtrahend_exp && subtrahend_id < subtrahend_desc.len_) {
|
|
tmp_digit -= subtrahend_digits[subtrahend_id];
|
|
--subtrahend_exp;
|
|
++subtrahend_id;
|
|
}
|
|
if (0 == first_digit_flag && tmp_digit != 0) {
|
|
first_digit_flag = (tmp_digit > 0 ? 1 : -1);
|
|
}
|
|
// LOG_DEBUG("sub_v2 ", K(sum_digits[i]), K(i), K(curr_exp), K(minuend_exp), K(minuend_id), K(subtrahend_exp), K(subtrahend_id), K(first_digit_flag));
|
|
}
|
|
|
|
sub_negative = (-1 == first_digit_flag);
|
|
if (sub_negative) {
|
|
for (int64_t i = OB_MAX_DECIMAL_DIGIT; i >= 0; --i) {
|
|
int32_t &tmp_digit = sum_digits[i];
|
|
tmp_digit = 0 - tmp_digit;
|
|
if (tmp_digit < 0) {
|
|
tmp_digit += BASE;
|
|
++sum_digits[i - 1];//not -1
|
|
}
|
|
}
|
|
} else {
|
|
for (int64_t i = OB_MAX_DECIMAL_DIGIT; i >= 0; --i) {
|
|
int32_t &tmp_digit = sum_digits[i];
|
|
if (tmp_digit < 0) {
|
|
tmp_digit += BASE;
|
|
--sum_digits[i - 1];
|
|
}
|
|
}
|
|
}
|
|
|
|
// normalize_digit_(sum_digits, OB_MAX_DECIMAL_DIGIT);
|
|
int64_t start_id = 0;
|
|
for (; start_id <= OB_MAX_DECIMAL_DIGIT && sum_digits[start_id] == 0; ++start_id, --sum_exp);
|
|
int32_t len = (OB_MAX_DECIMAL_DIGIT - start_id + 1);
|
|
for (int64_t i = OB_MAX_DECIMAL_DIGIT; i >= start_id && sum_digits[i] == 0; --len, --i);
|
|
|
|
if (start_id > OB_MAX_DECIMAL_DIGIT) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
} else {
|
|
uint32_t *digit_mem = NULL;
|
|
Desc sum_desc;
|
|
Desc tmp_desc;
|
|
tmp_desc.desc_ = d_.desc_;
|
|
tmp_desc.sign_ = (arg_negative == sub_negative ? POSITIVE : NEGATIVE);
|
|
if (OB_FAIL(calc_desc_and_check_(tmp_desc.desc_, sum_desc, sum_exp, len))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + start_id, sizeof(uint32_t) * len);
|
|
res.assign(sum_desc.desc_, (uint32_t*)digit_mem);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ObNumber addend = other.negate();
|
|
ret = add_v2_(addend, res, allocator);
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(res.round_scale_v2_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "sub_v2 [%s] - [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::sub_v3(const ObNumber &other, ObNumber &value, ObIAllocator &allocator,
|
|
const bool strict_mode/*true*/, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
const bool use_oracle_mode = is_oracle_mode();
|
|
LOG_DEBUG("sub_v3", K(ret), KPC(this), K(other));
|
|
if (OB_UNLIKELY(is_zero())) {
|
|
ret = other.negate_v3_(res, allocator);
|
|
} else if (OB_UNLIKELY(other.is_zero())) {
|
|
ret = res.deep_copy_v3(*this, allocator);
|
|
} else if (d_.sign_ == other.d_.sign_) {
|
|
const int64_t this_exp = get_decode_exp(d_);
|
|
const int64_t other_exp = get_decode_exp(other.d_);
|
|
|
|
if (OB_UNLIKELY(this_exp >= other_exp + OB_MAX_DECIMAL_DIGIT)) {
|
|
ret = res.deep_copy_v3(*this, allocator);
|
|
} else if (OB_UNLIKELY(other_exp >= this_exp + OB_MAX_DECIMAL_DIGIT)) {
|
|
ret = other.negate_v3_(res, allocator);
|
|
} else {
|
|
int64_t start_id = 0;
|
|
bool sub_negative = false;
|
|
bool is_equal = false;
|
|
if (this_exp > other_exp) {
|
|
sub_negative = false;
|
|
} else if (this_exp < other_exp) {
|
|
sub_negative = true;
|
|
} else {
|
|
const int64_t min_id = std::min(d_.len_, other.d_.len_) - 1;
|
|
while (start_id <= min_id && digits_[start_id] == other.digits_[start_id]) {
|
|
++start_id;
|
|
}
|
|
if (start_id > min_id) {
|
|
if (d_.len_ == other.d_.len_) {
|
|
is_equal = true;
|
|
} else {
|
|
sub_negative = (d_.len_ < other.d_.len_);
|
|
}
|
|
} else {
|
|
sub_negative = (digits_[start_id] < other.digits_[start_id]);
|
|
}
|
|
}
|
|
// LOG_DEBUG("sub_v3", K(is_equal), K(sub_negative), K(start_id));
|
|
|
|
if (is_equal) {
|
|
res.set_zero();
|
|
} else {
|
|
Desc minuend_desc(d_.desc_);
|
|
Desc subtrahend_desc(other.d_.desc_);
|
|
int64_t minuend_exp = this_exp;
|
|
int64_t subtrahend_exp = other_exp;
|
|
uint32_t *minuend_digits = digits_;
|
|
uint32_t *subtrahend_digits = other.digits_;
|
|
if (sub_negative) {
|
|
//always make the minuend is larger one
|
|
minuend_desc.desc_ = other.d_.desc_;
|
|
subtrahend_desc.desc_ = d_.desc_;
|
|
minuend_exp = other_exp;
|
|
subtrahend_exp = this_exp;
|
|
minuend_digits = other.digits_;
|
|
subtrahend_digits = digits_;
|
|
}
|
|
|
|
int32_t tmp_sum_digits[OB_CALC_BUFFER_SIZE];
|
|
int32_t *sum_digits = NULL;
|
|
if (strict_mode) {
|
|
sum_digits = tmp_sum_digits;
|
|
} else {
|
|
if (OB_ISNULL(sum_digits = (int32_t *)allocator.alloc(sizeof(uint32_t) * OB_CALC_BUFFER_SIZE))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * OB_CALC_BUFFER_SIZE, K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
MEMSET(sum_digits, 0, sizeof(uint32_t) * OB_CALC_BUFFER_SIZE);
|
|
MEMCPY(sum_digits + start_id, minuend_digits + start_id, (minuend_desc.len_ - start_id) * sizeof(uint32_t));
|
|
|
|
int64_t sum_exp = minuend_exp;
|
|
const int64_t cur_minuend_exp = minuend_exp - (minuend_desc.len_ - 1);
|
|
int64_t cur_subtrahend_exp = subtrahend_exp - (subtrahend_desc.len_ - 1);
|
|
int64_t cur_subtrahend_id = subtrahend_desc.len_ - 1;
|
|
int64_t cur_exp = std::min(cur_minuend_exp, cur_subtrahend_exp);
|
|
uint8_t sum_len = sum_exp - cur_exp + 1;
|
|
if (sum_len > OB_MAX_DECIMAL_DIGIT) {
|
|
const int64_t movex = sum_len - OB_MAX_DECIMAL_DIGIT;
|
|
cur_exp += movex;
|
|
cur_subtrahend_exp += movex;
|
|
cur_subtrahend_id -= movex;
|
|
sum_len = OB_MAX_DECIMAL_DIGIT;
|
|
}
|
|
|
|
// LOG_DEBUG("sub_v3", K(cur_subtrahend_exp), K(cur_subtrahend_id), K(sum_exp), K(cur_exp), K(sum_len));
|
|
|
|
int32_t carry = 0;
|
|
bool check_sum_len = true;
|
|
for (int64_t i = sum_len - 1; i >= start_id; --i, ++cur_exp) {
|
|
int32_t &tmp_digit = sum_digits[i];
|
|
// LOG_DEBUG("sub_v3", K(i), K(cur_exp), K(tmp_digit), K(carry), K(cur_subtrahend_exp), K(cur_subtrahend_id), K(subtrahend_digits[cur_subtrahend_id]));
|
|
tmp_digit += carry;
|
|
|
|
if (cur_exp == cur_subtrahend_exp && cur_subtrahend_id >= start_id) {
|
|
tmp_digit -= subtrahend_digits[cur_subtrahend_id--];
|
|
++cur_subtrahend_exp;
|
|
}
|
|
|
|
if (tmp_digit < 0) {
|
|
tmp_digit += BASE;
|
|
carry = -1;
|
|
} else {
|
|
carry = 0;
|
|
}
|
|
|
|
if (check_sum_len) {
|
|
if (0 == tmp_digit) {
|
|
--sum_len;
|
|
} else {
|
|
check_sum_len = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
sum_digits[sum_len] = BASE;//end flag
|
|
int32_t *tmp_digit = sum_digits + start_id;
|
|
while (0 == *tmp_digit) {
|
|
++tmp_digit;
|
|
}
|
|
int64_t new_start_id = tmp_digit - sum_digits;
|
|
sum_len -= new_start_id;
|
|
const int64_t new_sum_exp = sum_exp - new_start_id;
|
|
// LOG_DEBUG("sub_v3", K(start_id), K(new_start_id), K(sum_len), K(new_sum_exp));
|
|
|
|
if (sum_len <= 0) {
|
|
res.set_zero();
|
|
} else {
|
|
uint32_t *digit_mem = NULL;
|
|
Desc sum_desc(d_);
|
|
sum_desc.sign_ = ((NEGATIVE == minuend_desc.sign_) == sub_negative ? POSITIVE : NEGATIVE);
|
|
if (OB_FAIL(calc_desc_and_check(sum_desc.desc_, new_sum_exp, sum_len, sum_desc, use_oracle_mode))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (strict_mode) {
|
|
if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * sum_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * sum_len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + new_start_id, sizeof(uint32_t) * sum_len);
|
|
res.assign(sum_desc.desc_, (uint32_t*)digit_mem);
|
|
}
|
|
} else {
|
|
res.assign(sum_desc.desc_, (uint32_t*)(sum_digits + new_start_id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ret = add_v3(other.negate(), res, allocator, strict_mode, do_rounding);
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && res.need_round_after_arithmetic(use_oracle_mode)
|
|
&& OB_FAIL(res.round_scale_v3_(use_oracle_mode ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "sub_v3 [%s] - [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObNumber::negate_(ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
if (is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
ObCalcVector cv;
|
|
if (OB_FAIL(cv.init(d_.desc_, digits_))) {
|
|
LOG_WARN("fail to assign values", K(ret));
|
|
} else {
|
|
int64_t size2alloc = d_.len_;
|
|
if (POSITIVE == d_.sign_) {
|
|
res.d_.sign_ = NEGATIVE;
|
|
} else {
|
|
res.d_.sign_ = POSITIVE;
|
|
}
|
|
res.d_.exp_ = (0x7f & ~d_.exp_);
|
|
++res.d_.exp_;
|
|
|
|
if (OB_ISNULL(res.digits_ = res.alloc_(allocator, size2alloc))) {
|
|
_OB_LOG(WARN, "alloc digits fail, length=%ld", size2alloc);
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
} else if (OB_FAIL(res.normalize_(cv.get_digits(), cv.size()))) {
|
|
_OB_LOG(WARN, "normalize [%s] fail ret=%d", to_cstring(res), ret);
|
|
} else {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "negate [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::negate_v2_(ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
if (is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
res = this->negate();
|
|
if (OB_ISNULL(res.digits_ = res.alloc_(allocator, d_.len_))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
OB_LOG(WARN, "alloc digits fail", K(d_.len_), K(ret));
|
|
} else {
|
|
MEMCPY(res.digits_, digits_, d_.len_ * sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "negate_v2 [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::negate_v3_(ObNumber &value, ObIAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
if (is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
res = this->negate();
|
|
if (OB_ISNULL(res.digits_ = (uint32_t *)allocator.alloc(d_.len_ * sizeof(uint32_t)))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
OB_LOG(WARN, "alloc digits fail", K(d_.len_), K(ret));
|
|
} else {
|
|
MEMCPY(res.digits_, digits_, d_.len_ * sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "negate_v3 [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::mul_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc multiplicand_desc;
|
|
Desc multiplier_desc;
|
|
multiplicand_desc.desc_ = d_.desc_;
|
|
multiplier_desc.desc_ = other.d_.desc_;
|
|
if (is_zero() || other.is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
ObCalcVector multiplicand;
|
|
ObCalcVector multiplier;
|
|
if (OB_FAIL(multiplicand.init(multiplicand_desc.desc_, digits_))) {
|
|
LOG_WARN("fail to assign values", K(ret));
|
|
} else if (OB_FAIL(multiplier.init(multiplier_desc.desc_, other.digits_))) {
|
|
LOG_WARN("fail to assign values", K(ret));
|
|
} else {
|
|
ObCalcVector product;
|
|
int64_t product_size = multiplicand.size() + multiplier.size();
|
|
if (OB_FAIL(product.ensure(product_size))) {
|
|
LOG_WARN("product.ensure(product_size) fails", K(ret));
|
|
} else if (OB_FAIL(poly_poly_mul(multiplicand, multiplier, product))) {
|
|
_OB_LOG(WARN, "[%s] mul [%s] fail, ret=%d", to_cstring(*this), to_cstring(other), ret);
|
|
} else {
|
|
Desc res_desc = exp_mul_(multiplicand_desc, multiplier_desc);
|
|
bool carried = (0 != product.at(0));
|
|
exp_shift_((carried ? 1 : 0), res_desc);
|
|
ret = res.from_v2_(res_desc.desc_, product, allocator);
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "mul [%s] * [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::mul_v2_(const ObNumber &other, ObNumber &value, IAllocator &allocator,
|
|
const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc multiplicand_desc;
|
|
Desc multiplier_desc;
|
|
multiplicand_desc.desc_ = d_.desc_;
|
|
multiplier_desc.desc_ = other.d_.desc_;
|
|
LOG_DEBUG("mul_v2_", K(ret), KPC(this), K(other));
|
|
if (is_zero() || other.is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
int64_t sum_exp = get_decode_exp(multiplicand_desc.desc_) + get_decode_exp(multiplier_desc.desc_);
|
|
uint32_t *multiplicand_digits = digits_;
|
|
uint32_t *multiplier_digits = other.digits_;
|
|
uint64_t sum_digits[OB_MULT_BUFFER_SIZE] = {};
|
|
int64_t multiplicand_len = multiplicand_desc.len_;
|
|
int64_t multiplier_len = multiplier_desc.len_;
|
|
int64_t sum_len = multiplicand_len + multiplier_len;
|
|
int64_t digits_len = 0;
|
|
int offset = 0;
|
|
|
|
for (int64_t i = 0; i < multiplicand_len; ++i) {
|
|
for (int64_t j = 0; j < multiplier_len; ++j) {
|
|
int64_t k = i + j + 1;
|
|
sum_digits[k] += (uint64_t)multiplicand_digits[i] * multiplier_digits[j];
|
|
if (sum_digits[k] >= CARRY_BOUND) {
|
|
uint64_t carry = 0;
|
|
for (int64_t id = k; id >= 0; --id) {
|
|
uint64_t tmp = carry + sum_digits[id];
|
|
carry = tmp / BASE;
|
|
sum_digits[id] = tmp - carry * BASE;
|
|
if (0 == carry) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int64_t i = sum_len - 1; i >= 1; --i) {
|
|
if (sum_digits[i] >= BASE) {
|
|
int64_t tmp = sum_digits[i] / BASE;
|
|
sum_digits[i] -= tmp * BASE;
|
|
sum_digits[i-1] += tmp;
|
|
}
|
|
}
|
|
|
|
if (sum_digits[0] != 0) {
|
|
++sum_exp;
|
|
} else {
|
|
offset = 1;
|
|
}
|
|
while ((sum_len - 1) >= offset && sum_digits[sum_len - 1] == 0) {
|
|
--sum_len;
|
|
}
|
|
digits_len = sum_len - offset;
|
|
|
|
if (digits_len > OB_MAX_DECIMAL_DIGIT) {
|
|
digits_len = OB_MAX_DECIMAL_DIGIT;
|
|
// normalize_digit_(sum_digits + offset, OB_MAX_DECIMAL_DIGIT);
|
|
}
|
|
|
|
Desc sum_desc, tmp_desc;
|
|
tmp_desc.desc_ = d_.desc_;
|
|
tmp_desc.sign_ = (multiplicand_desc.sign_ == multiplier_desc.sign_ ? POSITIVE : NEGATIVE);
|
|
uint32_t *digit_mem = NULL;
|
|
if (OB_FAIL(calc_desc_and_check_(tmp_desc.desc_, sum_desc, sum_exp, (uint8_t)digits_len))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * digits_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * digits_len, K(ret));
|
|
} else {
|
|
for (int64_t i = 0; i < digits_len; ++i) {
|
|
digit_mem[i] = static_cast<uint32_t>(sum_digits[i + offset]);
|
|
}
|
|
res.assign(sum_desc.desc_, digit_mem);
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "mul_v2 [%s] * [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::mul_v3(const ObNumber &other, ObNumber &value, ObIAllocator &allocator,
|
|
const bool strict_mode/*true*/, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
const bool use_oracle_mode = is_oracle_mode();
|
|
LOG_DEBUG("mul_v3_", K(ret), KPC(this), K(other));
|
|
if (is_zero() || other.is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
Desc multiplicand_desc(d_.desc_);
|
|
Desc multiplier_desc(other.d_.desc_);
|
|
const uint32_t *multiplicand_digits = digits_;
|
|
const uint32_t *multiplier_digits = other.digits_;
|
|
uint64_t sum_digits[OB_MULT_BUFFER_SIZE] = {};
|
|
int64_t multiplicand_len = multiplicand_desc.len_;
|
|
int64_t multiplier_len = multiplier_desc.len_;
|
|
|
|
/* 保证 for 循环先循环长度较小的 */
|
|
if (d_.len_ > other.d_.len_) {
|
|
multiplicand_digits = other.digits_;
|
|
multiplier_digits = digits_;
|
|
multiplicand_len = multiplier_desc.len_;
|
|
multiplier_len = multiplicand_desc.len_;
|
|
}
|
|
int64_t sum_exp = get_decode_exp(multiplicand_desc.desc_) + get_decode_exp(multiplier_desc.desc_);
|
|
int64_t sum_len = multiplier_desc.len_ + multiplicand_desc.len_;
|
|
int64_t digits_len = 0;
|
|
int offset = 0;
|
|
bool check_sum_len = true;
|
|
|
|
uint32_t tmp_res_digits[OB_CALC_BUFFER_SIZE];
|
|
uint32_t *res_digits = NULL;
|
|
if (strict_mode) {
|
|
res_digits = tmp_res_digits;
|
|
} else {
|
|
if (OB_ISNULL(res_digits = (uint32_t *)allocator.alloc(sizeof(uint32_t) * OB_CALC_BUFFER_SIZE))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * OB_CALC_BUFFER_SIZE, K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (sum_len <= OB_MAX_DECIMAL_DIGIT) {
|
|
MEMSET(res_digits + sum_len, 0, sizeof(uint32_t) * (OB_MAX_DECIMAL_DIGIT - sum_len + 1));
|
|
}
|
|
|
|
const uint32_t *multiplicand_di = multiplicand_digits;
|
|
for (int64_t i = 0; i < multiplicand_len; ++i) {
|
|
const uint64_t tmp_multiplicand_di = static_cast<uint64_t>(*multiplicand_di++);
|
|
const uint32_t *tmp_multiplier_di = multiplier_digits;
|
|
uint64_t *sum_di = sum_digits + (i + 1);
|
|
for (int64_t j = 0; j < multiplier_len; ++j) {
|
|
(*sum_di++) += tmp_multiplicand_di * (*tmp_multiplier_di++);
|
|
}
|
|
}
|
|
|
|
|
|
uint64_t carry = 0;
|
|
for (int64_t i = sum_len - 1; i >= 0; --i) {
|
|
uint64_t tmp_digit = sum_digits[i];
|
|
tmp_digit += carry;
|
|
if (tmp_digit >= BASE) {
|
|
carry = tmp_digit / BASE;
|
|
tmp_digit -= carry * BASE;
|
|
} else {
|
|
carry = 0;
|
|
}
|
|
if (check_sum_len) {
|
|
if (0 == tmp_digit) {
|
|
-- sum_len;
|
|
} else {
|
|
check_sum_len = false;
|
|
}
|
|
}
|
|
if (i <= OB_MAX_DECIMAL_DIGIT) {
|
|
res_digits[i] = static_cast<uint32_t>(tmp_digit);
|
|
}
|
|
}
|
|
|
|
if (res_digits[0] != 0) {
|
|
++sum_exp;
|
|
} else {
|
|
offset = 1;
|
|
}
|
|
|
|
digits_len = sum_len - offset;
|
|
if (digits_len > OB_MAX_DECIMAL_DIGIT) {
|
|
digits_len = OB_MAX_DECIMAL_DIGIT;
|
|
}
|
|
|
|
Desc sum_desc(d_);
|
|
sum_desc.sign_ = (multiplicand_desc.sign_ == multiplier_desc.sign_ ? POSITIVE : NEGATIVE);
|
|
uint32_t *digit_mem = NULL;
|
|
if (OB_FAIL(calc_desc_and_check(sum_desc.desc_, sum_exp, (uint8_t)digits_len, sum_desc, use_oracle_mode))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (strict_mode) {
|
|
if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * digits_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * digits_len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, res_digits + offset, sizeof(uint32_t) * digits_len);
|
|
res.assign(sum_desc.desc_, digit_mem);
|
|
}
|
|
} else {
|
|
res.assign(sum_desc.desc_, res_digits + offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && res.need_round_after_arithmetic(use_oracle_mode)
|
|
&& OB_FAIL(res.round_scale_v3_(use_oracle_mode ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "mul_v3 [%s] * [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
|
|
//int ObNumber::div_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
//{
|
|
// int ret = OB_SUCCESS;
|
|
// ObNumber res;
|
|
// Desc dividend_desc;
|
|
// Desc divisor_desc;
|
|
// dividend_desc.desc_ = d_.desc_;
|
|
// divisor_desc.desc_ = other.d_.desc_;
|
|
// if (OB_UNLIKELY(other.is_zero())) {
|
|
// _OB_LOG(ERROR, "[%s] div zero [%s]", to_cstring(*this), to_cstring(other));
|
|
// ret = OB_DIVISION_BY_ZERO;
|
|
// } else if (is_zero()) {
|
|
// res.set_zero();
|
|
// } else {
|
|
// ObCalcVector dividend;
|
|
// ObCalcVector divisor;
|
|
// int64_t shift = get_extend_length_remainder_(dividend_desc, divisor_desc);
|
|
// shift = (MAX_STORE_LEN <= shift) ? shift : (MAX_STORE_LEN - shift);
|
|
// shift += get_decimal_extend_length_(dividend_desc);
|
|
// exp_shift_(shift, dividend_desc);
|
|
// if (OB_FAIL(dividend.init(dividend_desc.desc_, digits_))) {
|
|
// LOG_WARN("fail to assign values", K(ret));
|
|
// } else if (OB_FAIL(divisor.init(divisor_desc.desc_, other.digits_))) {
|
|
// LOG_WARN("fail to assign values", K(ret));
|
|
// } else {
|
|
// ObCalcVector quotient;
|
|
// ObCalcVector remainder;
|
|
// int64_t quotient_size = dividend.size() - divisor.size() + 1;
|
|
// int64_t remainder_size = divisor.size();
|
|
// if (OB_FAIL(quotient.ensure(quotient_size))) {
|
|
// LOG_WARN("quotient.ensure(quotient_size) fails", K(ret));
|
|
// } else if (OB_FAIL(remainder.ensure(remainder_size))) {
|
|
// LOG_WARN("remainder.ensure(remainder_size) fails", K(ret));
|
|
// } else if (OB_FAIL(poly_poly_div(dividend, divisor, quotient, remainder))) {
|
|
// _OB_LOG(WARN, "[%s] div [%s] fail ret=%d", to_cstring(*this), to_cstring(other), ret);
|
|
// } else {
|
|
// Desc res_desc = exp_div_(dividend_desc, divisor_desc);
|
|
// for (int64_t i = 0; i < quotient.size(); ++i) {
|
|
// if (0 != quotient.at(i)) {
|
|
// break;
|
|
// }
|
|
// ++shift;
|
|
// }
|
|
// exp_shift_(-shift, res_desc);
|
|
// ret = res.from_v2_(res_desc.desc_, quotient, allocator);
|
|
// }
|
|
// }
|
|
// }
|
|
// if (OB_SUCC(ret)) {
|
|
// value = res;
|
|
// }
|
|
// _OB_LOG(DEBUG, "div: [%s] / [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
// return ret;
|
|
//}
|
|
|
|
int ObNumber::div_v2_(const ObNumber &other, ObNumber &value, IAllocator &allocator, const int32_t qscale,
|
|
const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc dividend_desc;
|
|
Desc divisor_desc;
|
|
dividend_desc.desc_ = d_.desc_;
|
|
divisor_desc.desc_ = other.d_.desc_;
|
|
LOG_DEBUG("div_v2_", K(ret), KPC(this), K(other));
|
|
if (OB_UNLIKELY(other.is_zero())) {
|
|
_OB_LOG(ERROR, "[%s] div zero [%s]", to_cstring(*this), to_cstring(other));
|
|
ret = OB_DIVISION_BY_ZERO;
|
|
} else if (is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
/* remove ObCalcVector */
|
|
ObDivArray dividend, divisor, quotient;
|
|
int32_t extend_len = other.d_.len_ + qscale;
|
|
extend_len = std::min(extend_len, +OB_DIV_BUFFER_SIZE);
|
|
dividend.from(digits_, d_.len_, std::min(+OB_DIV_BUFFER_SIZE, extend_len));
|
|
divisor.from(other.digits_, other.d_.len_, other.d_.len_);
|
|
dividend.div(divisor, quotient);
|
|
|
|
if (quotient.is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
int64_t sum_exp = get_decode_exp(dividend_desc.desc_) - get_decode_exp(divisor_desc.desc_);
|
|
uint32_t sum_digits[OB_MULT_BUFFER_SIZE] = {};
|
|
quotient.get_uint32_digits(sum_digits, OB_MULT_BUFFER_SIZE);
|
|
|
|
int32_t offset = 0;
|
|
int32_t sum_len = quotient.len_;
|
|
while (offset < quotient.len_ && sum_digits[offset] == 0) {
|
|
++offset;
|
|
--sum_exp;
|
|
--sum_len;
|
|
}
|
|
if (sum_len > OB_MAX_DECIMAL_DIGIT) {
|
|
sum_len = OB_MAX_DECIMAL_DIGIT;
|
|
// normalize_digit_(sum_digits + offset, sum_len);
|
|
}
|
|
int32_t end = offset + sum_len - 1; // remove tailing zero
|
|
while (sum_digits[end] == 0) {
|
|
--end;
|
|
--sum_len;
|
|
}
|
|
|
|
Desc tmp_desc, sum_desc;
|
|
uint32_t *digit_mem = NULL;
|
|
tmp_desc.desc_ = d_.desc_;
|
|
tmp_desc.sign_ = (dividend_desc.sign_ == divisor_desc.sign_ ? POSITIVE : NEGATIVE);
|
|
if (OB_FAIL(calc_desc_and_check_(tmp_desc.desc_, sum_desc, sum_exp, (uint8_t)sum_len))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * sum_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * sum_len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + offset, sizeof(uint32_t) * sum_len);
|
|
res.assign(sum_desc.desc_, digit_mem);
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "div_v2: [%s] / [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::div_v3(const ObNumber &other, ObNumber &value, ObIAllocator &allocator,
|
|
const int32_t qscale/*OB_MAX_DECIMAL_DIGIT*/, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
const bool use_oracle_mode = is_oracle_mode();
|
|
LOG_DEBUG("div_v3_", K(ret), KPC(this), K(other));
|
|
if (OB_UNLIKELY(other.is_zero())) {
|
|
_OB_LOG(ERROR, "[%s] div zero [%s]", to_cstring(*this), to_cstring(other));
|
|
ret = OB_DIVISION_BY_ZERO;
|
|
} else if (is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
Desc dividend_desc(d_);
|
|
Desc divisor_desc(other.d_);
|
|
|
|
ObDivArray dividend;
|
|
ObDivArray divisor;
|
|
ObDivArray quotient;
|
|
int32_t extend_len = other.d_.len_ + qscale;
|
|
extend_len = std::min(extend_len, +OB_DIV_BUFFER_SIZE);
|
|
dividend.from(digits_, d_.len_, std::min(+OB_DIV_BUFFER_SIZE, extend_len));
|
|
divisor.from(other.digits_, other.d_.len_, other.d_.len_);
|
|
dividend.div_v2(divisor, quotient);
|
|
|
|
if (quotient.is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
int64_t sum_exp = get_decode_exp(dividend_desc.desc_) - get_decode_exp(divisor_desc.desc_);
|
|
uint32_t sum_digits[OB_MULT_BUFFER_SIZE] = {};
|
|
quotient.get_uint32_digits(sum_digits, OB_MULT_BUFFER_SIZE);
|
|
|
|
int32_t offset = 0;
|
|
int32_t sum_len = quotient.len_;
|
|
while (offset < quotient.len_ && sum_digits[offset] == 0) {
|
|
++offset;
|
|
--sum_exp;
|
|
--sum_len;
|
|
}
|
|
if (sum_len > OB_MAX_DECIMAL_DIGIT) {
|
|
sum_len = OB_MAX_DECIMAL_DIGIT;
|
|
}
|
|
const int32_t end = offset + sum_len - 1; // remove tailing zero
|
|
uint32_t *tmp_sum_digits = sum_digits + end;
|
|
while (*tmp_sum_digits == 0) {
|
|
--tmp_sum_digits;
|
|
}
|
|
sum_len -= (sum_digits + end - tmp_sum_digits);
|
|
|
|
uint32_t *digit_mem = NULL;
|
|
Desc sum_desc(d_);
|
|
sum_desc.sign_ = (dividend_desc.sign_ == divisor_desc.sign_ ? POSITIVE : NEGATIVE);
|
|
if (OB_FAIL(calc_desc_and_check(sum_desc.desc_, sum_exp, (uint8_t)sum_len, sum_desc, use_oracle_mode))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * sum_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * sum_len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + offset, sizeof(uint32_t) * sum_len);
|
|
res.assign(sum_desc.desc_, digit_mem);
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
LOG_DEBUG("round scale before", K(res));
|
|
if (do_rounding && res.need_round_after_arithmetic(use_oracle_mode)
|
|
&& OB_FAIL(res.round_scale_v3_(use_oracle_mode ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "div_v3: [%s] / [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
//sinh(x)=(e^x - e^(-x)) / 2
|
|
int ObNumber::sinh(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t LOCAL_ALLOCATE_TIME = 4;
|
|
char buf_alloc_local[ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME];
|
|
ObDataBuffer allocator_local(buf_alloc_local, ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME);
|
|
ObNumber res;
|
|
ObNumber e_power_x;
|
|
ObNumber e_power_negate_x;
|
|
const ObNumber const_zero_dot_five = get_positive_zero_dot_five();
|
|
|
|
if (OB_FAIL(e_power(e_power_x, allocator_local, false))) {
|
|
LOG_WARN("calc power(e,x) falied", K(ret), K(*this));
|
|
} else if (e_power_x.is_zero()) {
|
|
ObNumber negate_x;
|
|
if (OB_FAIL(negate(negate_x, allocator_local))) {
|
|
LOG_WARN("negate x failed", K(ret), K(*this));
|
|
} else if (OB_FAIL(negate_x.e_power(e_power_negate_x, allocator_local, false))) {
|
|
LOG_WARN("calc power(e,-x) failed", K(ret), K(negate_x));
|
|
} else if (OB_FAIL(e_power_negate_x.mul_v3(const_zero_dot_five, res, allocator_local, true, false))) {
|
|
LOG_WARN("e_power_negate_x mul 0.5 failed", K(ret), K(e_power_negate_x));
|
|
} else if (OB_FAIL(res.negate(res, allocator))) {
|
|
LOG_WARN("res negate failed", K(ret), K(res));
|
|
}
|
|
} else {
|
|
const ObNumber const_one = get_positive_one();
|
|
if (OB_FAIL(const_one.div_v3(e_power_x, e_power_negate_x, allocator_local, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("calc power(e,-x) by 1/power(e,x) failed", K(ret), K(e_power_x));
|
|
} else if (OB_FAIL(e_power_x.sub_v3(e_power_negate_x, res, allocator_local, true, false))) {
|
|
LOG_WARN("e_power_x add e_power_negate_x failed", K(ret), K(e_power_x), K(e_power_negate_x));
|
|
} else if (OB_FAIL(res.mul_v3(const_zero_dot_five, res, allocator, true, false))) {
|
|
LOG_WARN("res mul 0.5 failed", K(ret), K(res));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "sinh [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
//cosh(x)=(e^x + e^(-x)) / 2
|
|
int ObNumber::cosh(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t LOCAL_ALLOCATE_TIME = 3;
|
|
char buf_alloc_local[ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME];
|
|
ObDataBuffer allocator_local(buf_alloc_local, ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME);
|
|
ObNumber res;
|
|
ObNumber e_power_x;
|
|
ObNumber e_power_negate_x;
|
|
const ObNumber const_zero_dot_five = get_positive_zero_dot_five();
|
|
|
|
if (OB_FAIL(e_power(e_power_x, allocator_local, false))) {
|
|
LOG_WARN("calc power(e,x) falied", K(ret), K(*this));
|
|
} else if (e_power_x.is_zero()) {
|
|
ObNumber negate_x;
|
|
if (OB_FAIL(negate(negate_x, allocator_local))) {
|
|
LOG_WARN("negate x failed", K(ret), K(*this));
|
|
} else if (OB_FAIL(negate_x.e_power(e_power_negate_x, allocator_local, false))) {
|
|
LOG_WARN("calc power(e,-x) failed", K(ret), K(negate_x));
|
|
} else if (OB_FAIL(e_power_negate_x.mul_v3(const_zero_dot_five, res, allocator, true, false))) {
|
|
LOG_WARN("e_power_negate_x mul 0.5 failed", K(ret), K(e_power_negate_x));
|
|
}
|
|
} else {
|
|
const ObNumber const_one = get_positive_one();
|
|
ObNumber sum;
|
|
if (OB_FAIL(const_one.div_v3(e_power_x, e_power_negate_x, allocator_local, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("calc power(e,-x) by 1/power(e,x) failed", K(ret), K(e_power_x));
|
|
} else if (OB_FAIL(e_power_x.add_v3(e_power_negate_x, sum, allocator_local, true, false))) {
|
|
LOG_WARN("e_power_x add e_power_negate_x failed", K(ret), K(e_power_x), K(e_power_negate_x));
|
|
} else if (OB_FAIL(sum.mul_v3(const_zero_dot_five, res, allocator, true, false))) {
|
|
LOG_WARN("sum mul 0.5 failed", K(ret), K(sum));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "cosh [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
//tanh(x)= sinh(x) / cosh(x) = (e^x - e^(-x)) / (e^x + e^(-x))
|
|
int ObNumber::tanh(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const int64_t LOCAL_ALLOCATE_TIME = 4;
|
|
char buf_alloc_local[ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME];
|
|
ObDataBuffer allocator_local(buf_alloc_local, ObNumber::MAX_BYTE_LEN * LOCAL_ALLOCATE_TIME);
|
|
ObNumber res;
|
|
ObNumber e_power_x;
|
|
ObNumber e_power_negate_x;
|
|
|
|
if (OB_FAIL(e_power(e_power_x, allocator_local, false))) {
|
|
LOG_WARN("calc power(e,x) falied", K(ret), K(*this));
|
|
} else if (e_power_x.is_zero()) {
|
|
ObNumber negate_x;
|
|
if (OB_FAIL(negate(negate_x, allocator_local))) {
|
|
LOG_WARN("negate x failed", K(ret), K(*this));
|
|
} else if (OB_FAIL(negate_x.e_power(e_power_negate_x, allocator_local, false))) {
|
|
LOG_WARN("calc power(e,-x) failed", K(ret), K(negate_x));
|
|
} else if (OB_FAIL(res.from("-1", allocator))) {
|
|
LOG_WARN("result from -1 failed", K(ret));
|
|
}
|
|
} else {
|
|
const ObNumber const_one = get_positive_one();
|
|
ObNumber sum;
|
|
ObNumber diff;
|
|
if (OB_FAIL(const_one.div_v3(e_power_x, e_power_negate_x, allocator_local, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("calc power(e,-x) by 1/power(e,x) failed", K(ret), K(e_power_x));
|
|
} else if (OB_FAIL(e_power_x.add_v3(e_power_negate_x, sum, allocator_local, true, false))) {
|
|
LOG_WARN("e_power_x add e_power_negate_x failed", K(ret), K(e_power_x), K(e_power_negate_x));
|
|
} else if (OB_FAIL(e_power_x.sub_v3(e_power_negate_x, diff, allocator_local, true, false))) {
|
|
LOG_WARN("e_power_x sub e_power_negate_x failed", K(ret), K(e_power_x), K(e_power_negate_x));
|
|
} else if (OB_FAIL(diff.div_v3(sum, res, allocator, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("diff div sum failed", K(ret), K(diff), K(sum));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(res.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("round scale fail", K(ret), K(res));
|
|
} else {
|
|
value = res;
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "tanh [%s], ret=%d [%s]", this->format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
//int ObNumber::rem_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
//{
|
|
// int ret = OB_SUCCESS;
|
|
// ObNumber res;
|
|
// Desc dividend_desc;
|
|
// Desc divisor_desc;
|
|
// dividend_desc.desc_ = d_.desc_;
|
|
// divisor_desc.desc_ = other.d_.desc_;
|
|
// int cmp_ret = 0;
|
|
// if (OB_UNLIKELY(other.is_zero())) {
|
|
// _OB_LOG(ERROR, "[%s] div zero [%s]", to_cstring(*this), to_cstring(other));
|
|
// ret = OB_DIVISION_BY_ZERO;
|
|
// } else if (is_zero()) {
|
|
// res.set_zero();
|
|
// } else if (0 >= (cmp_ret = abs_compare(other))) {
|
|
// if (0 == cmp_ret) {
|
|
// res.set_zero();
|
|
// } else {
|
|
// res.from(*this, allocator);
|
|
// }
|
|
// } else {
|
|
// int64_t shift = std::max(get_decimal_extend_length_(dividend_desc),
|
|
// get_decimal_extend_length_(divisor_desc));
|
|
// exp_shift_(shift, dividend_desc);
|
|
// exp_shift_(shift, divisor_desc);
|
|
//
|
|
// ObCalcVector dividend;
|
|
// ObCalcVector divisor;
|
|
// if (OB_FAIL(dividend.init(dividend_desc.desc_, digits_))) {
|
|
// LOG_WARN("fail to assign values", K(ret));
|
|
// } else if (OB_FAIL(divisor.init(divisor_desc.desc_, other.digits_))) {
|
|
// LOG_WARN("fail to assign values", K(ret));
|
|
// } else {
|
|
// ObCalcVector dividend_amplify;
|
|
// ObCalcVector *dividend_ptr = NULL;
|
|
// if (dividend.size() < divisor.size()) {
|
|
// _OB_LOG(WARN, "dividend_size=%ld must not less than divisor_size=%ld", dividend.size(),
|
|
// divisor.size());
|
|
// ret = OB_ERR_UNEXPECTED;
|
|
// } else if (dividend.size() > divisor.size()) {
|
|
// dividend_ptr = ÷nd;
|
|
// } else {
|
|
// ObCalcVector divisor_amplify;
|
|
// if (OB_FAIL(divisor_amplify.ensure(divisor.size() + 1))) {
|
|
// LOG_WARN("divisor_amplify.ensure() fails", K(ret));
|
|
// } else if (OB_FAIL(poly_mono_mul(divisor, BASE - 1, divisor_amplify))) {
|
|
// _OB_LOG(WARN, "[%s] mul [%lu] fail, ret=%d", to_cstring(divisor), BASE - 1, ret);
|
|
// } else {
|
|
// int64_t sum_size = std::max(dividend.size(), divisor_amplify.size()) + 1;
|
|
// if (OB_FAIL(dividend_amplify.ensure(sum_size))) {
|
|
// LOG_WARN("ensure() fails", K(ret));
|
|
// } else if (OB_FAIL(poly_poly_add(dividend, divisor_amplify, dividend_amplify))) {
|
|
// _OB_LOG(WARN, "[%s] add [%s] fail, ret=%d",
|
|
// to_cstring(dividend), to_cstring(divisor_amplify), ret);
|
|
// } else {
|
|
// dividend_ptr = ÷nd_amplify;
|
|
// }
|
|
// }
|
|
// }
|
|
// if (OB_SUCC(ret)) {
|
|
// ObCalcVector quotient;
|
|
// ObCalcVector remainder;
|
|
// int64_t quotient_size = dividend_ptr->size() - divisor.size() + 1;
|
|
// int64_t remainder_size = divisor.size();
|
|
// if (OB_FAIL(quotient.ensure(quotient_size))) {
|
|
// LOG_WARN("ensure() fails", K(ret));
|
|
// } else if (OB_FAIL(remainder.ensure(remainder_size))) {
|
|
// LOG_WARN("ensure() fails", K(ret));
|
|
// } else if (OB_FAIL(poly_poly_div(*dividend_ptr, divisor, quotient, remainder))) {
|
|
// _OB_LOG(WARN, "[%s] div [%s] fail ret=%d",
|
|
// to_cstring(*dividend_ptr), to_cstring(divisor), ret);
|
|
// } else {
|
|
// Desc res_desc = exp_rem_(dividend_desc, divisor_desc);
|
|
// for (int64_t i = 0; i < remainder.size() - 1; ++i) {
|
|
// if (0 != remainder.at(i)) {
|
|
// break;
|
|
// }
|
|
// ++shift;
|
|
// }
|
|
// exp_shift_(-shift, res_desc);
|
|
// ret = res.from_v2_(res_desc.desc_, remainder, allocator);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// if (OB_SUCC(ret)) {
|
|
// value = res;
|
|
// }
|
|
// _OB_LOG(DEBUG, "[%s] %% [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
// return ret;
|
|
//}
|
|
|
|
int ObNumber::rem_v2_(const ObNumber &other, ObNumber &value, IAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc dividend_desc(d_);
|
|
Desc divisor_desc(other.d_);
|
|
LOG_DEBUG("rem_v2_", K(ret), KPC(this), K(other));
|
|
int cmp_ret = 0;
|
|
if (OB_UNLIKELY(other.is_zero())) {
|
|
_OB_LOG(ERROR, "[%s] div zero [%s]", to_cstring(*this), to_cstring(other));
|
|
ret = OB_DIVISION_BY_ZERO;
|
|
} else if (is_zero()) {
|
|
res.set_zero();
|
|
} else if (0 >= (cmp_ret = abs_compare(other))) {
|
|
if (0 == cmp_ret) {
|
|
res.set_zero();
|
|
} else {
|
|
res.from(*this, allocator);
|
|
}
|
|
} else { // promise : dividend > divisor
|
|
// remove ObCalcVector
|
|
ObDivArray dividend, divisor, rem;
|
|
int32_t dividend_move = d_.len_ - 1;
|
|
int32_t dividend_exp = get_decode_exp(dividend_desc.desc_) - dividend_move;
|
|
int32_t divisor_move = other.d_.len_ - 1;
|
|
int32_t divisor_exp = get_decode_exp(divisor_desc.desc_) - divisor_move;
|
|
dividend_move += std::max(0, dividend_exp - divisor_exp);
|
|
divisor_move += std::max(0, divisor_exp - dividend_exp);
|
|
|
|
dividend.from(digits_, d_.len_, dividend_move + 1);
|
|
divisor.from(other.digits_, other.d_.len_, divisor_move + 1);
|
|
|
|
dividend.rem(divisor, rem);
|
|
|
|
if (rem.is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
int64_t sum_exp = get_decode_exp(divisor_desc.desc_);
|
|
uint32_t sum_digits[OB_REM_BUFFER_SIZE] = {};
|
|
rem.get_uint32_digits(sum_digits, OB_REM_BUFFER_SIZE);
|
|
int32_t offset = 0;
|
|
int32_t sum_len = rem.len_;
|
|
while (offset < rem.len_ && sum_digits[offset] == 0) {
|
|
++offset;
|
|
--sum_exp;
|
|
--sum_len;
|
|
}
|
|
if (sum_len > OB_MAX_DECIMAL_DIGIT) {
|
|
sum_len = OB_MAX_DECIMAL_DIGIT;
|
|
// normalize_digit_(sum_digits + offset, OB_MAX_DECIMAL_DIGIT);
|
|
}
|
|
int32_t end = offset + sum_len - 1;
|
|
while (sum_digits[end] == 0) {
|
|
--end;
|
|
--sum_len;
|
|
}
|
|
uint32_t *digit_mem = NULL;
|
|
Desc sum_desc = exp_rem_(dividend_desc, divisor_desc);
|
|
if (OB_FAIL(calc_desc_and_check(sum_desc.desc_, sum_exp, (uint8_t)sum_len, sum_desc, is_oracle_mode()))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * sum_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * sum_len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + offset, sizeof(uint32_t) * sum_len);
|
|
res.assign(sum_desc.desc_, (uint32_t*)digit_mem);
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "rem_v2: [%s] %% [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObNumber::rem_v3(const ObNumber &other, ObNumber &value, ObIAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
Desc dividend_desc;
|
|
Desc divisor_desc;
|
|
dividend_desc.desc_ = d_.desc_;
|
|
divisor_desc.desc_ = other.d_.desc_;
|
|
LOG_DEBUG("rem_v3_", K(ret), KPC(this), K(other));
|
|
int cmp_ret = 0;
|
|
if (OB_UNLIKELY(other.is_zero())) {
|
|
_OB_LOG(ERROR, "[%s] div zero [%s]", to_cstring(*this), to_cstring(other));
|
|
ret = OB_DIVISION_BY_ZERO;
|
|
} else if (is_zero()) {
|
|
res.set_zero();
|
|
} else if (0 >= (cmp_ret = abs_compare(other))) {
|
|
if (0 == cmp_ret) {
|
|
res.set_zero();
|
|
} else {
|
|
res.deep_copy_v3(*this, allocator);
|
|
}
|
|
} else { // promise : dividend > divisor
|
|
// remove ObCalcVector
|
|
ObDivArray dividend, divisor, rem;
|
|
int32_t dividend_move = d_.len_ - 1;
|
|
int32_t dividend_exp = get_decode_exp(dividend_desc.desc_) - dividend_move;
|
|
int32_t divisor_move = other.d_.len_ - 1;
|
|
int32_t divisor_exp = get_decode_exp(divisor_desc.desc_) - divisor_move;
|
|
dividend_move += std::max(0, dividend_exp - divisor_exp);
|
|
divisor_move += std::max(0, divisor_exp - dividend_exp);
|
|
|
|
dividend.from(digits_, d_.len_, dividend_move + 1);
|
|
divisor.from(other.digits_, other.d_.len_, divisor_move + 1);
|
|
|
|
dividend.rem(divisor, rem);
|
|
if (rem.is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
int64_t sum_exp = get_decode_exp(divisor_desc.desc_);
|
|
uint32_t sum_digits[OB_REM_BUFFER_SIZE] = {};
|
|
rem.get_uint32_digits(sum_digits, OB_REM_BUFFER_SIZE);
|
|
int32_t offset = 0;
|
|
int32_t sum_len = rem.len_;
|
|
while (offset < rem.len_ && sum_digits[offset] == 0) {
|
|
++offset;
|
|
--sum_exp;
|
|
--sum_len;
|
|
}
|
|
if (sum_len > OB_MAX_DECIMAL_DIGIT) {
|
|
sum_len = OB_MAX_DECIMAL_DIGIT;
|
|
// normalize_digit_(sum_digits + offset, OB_MAX_DECIMAL_DIGIT);
|
|
}
|
|
int32_t end = offset + sum_len - 1;
|
|
while (sum_digits[end] == 0) {
|
|
--end;
|
|
--sum_len;
|
|
}
|
|
uint32_t *digit_mem = NULL;
|
|
Desc sum_desc = exp_rem_(dividend_desc, divisor_desc);
|
|
if (OB_FAIL(calc_desc_and_check(sum_desc.desc_, sum_exp, (uint8_t)sum_len, sum_desc, is_oracle_mode()))) {
|
|
LOG_WARN("fail to assign desc part", K(ret));
|
|
if (OB_DECIMAL_PRECISION_OVERFLOW == ret) {
|
|
res.set_zero();
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else if (OB_ISNULL(digit_mem = (uint32_t *)allocator.alloc(sizeof(uint32_t) * sum_len))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_ERROR("failed to alloc mem", "size", sizeof(uint32_t) * sum_len, K(ret));
|
|
} else {
|
|
MEMCPY(digit_mem, sum_digits + offset, sizeof(uint32_t) * sum_len);
|
|
res.assign(sum_desc.desc_, (uint32_t*)digit_mem);
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "rem_v3: [%s] %% [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::sqrt_first_guess_(ObNumber &value, ObIAllocator &allocator) const
|
|
{
|
|
// use std::sqrt(double) can get a much better guess and need much fewer loops
|
|
// in sqrt(). Not doing this for now because we can't do ObNumber <=> double
|
|
// convertion in ObNumber conveniently.
|
|
// same as pg's sqrt_var: half highest digit, half exp.
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
uint32_t digits[1] = {0};
|
|
if (is_zero()) {
|
|
// should not happen. sqrt(0) should be handled as a special case without
|
|
// using sqrt_first_guess_
|
|
res.set_zero();
|
|
} else if (is_negative()) {
|
|
LOG_WARN("cannot take sqrt guess of negative arg", KPC(this), K(ret));
|
|
ret = OB_ERR_ARGUMENT_OUT_OF_RANGE;
|
|
} else {
|
|
Desc guess_desc;
|
|
guess_desc.desc_ = d_.desc_;
|
|
// value must be positive here
|
|
int64_t decoded_exp = guess_desc.exp_ - EXP_ZERO;
|
|
// handle exp=-1 specially:
|
|
// 0.36 == (exp=-1, digits=[360000000,]),
|
|
// -1/2 = 0, 360000000/2 = 180000000,
|
|
// (exp=0, digits=[180000000,]) == 180000000 is very inappropriate.
|
|
// guess exp from exp -1 should be -1.
|
|
// guess of 0.36 will be (exp=-1, digits=[180000000,]) == 0.18
|
|
int64_t decoded_new_exp = (decoded_exp == -1) ? -1 : (decoded_exp / 2);
|
|
guess_desc.exp_ = decoded_new_exp + EXP_ZERO;
|
|
guess_desc.len_ = 1;
|
|
guess_desc.reserved_ = 0; // keep always 0.
|
|
uint32_t digit = get_digits()[0] / 2;
|
|
if (0 == digit) {
|
|
digit = 1; // avoid 0 guess
|
|
}
|
|
digits[0] = digit;
|
|
res.assign(guess_desc.desc_, digits);
|
|
LOG_DEBUG("sqrt_first_guess_, main path", KPC(this), K(res), K(ret));
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(value.from(res, allocator))) {
|
|
LOG_WARN("failed: deep_copy res to value", K(ret));
|
|
}
|
|
}
|
|
_OB_LOG(DEBUG, "sqrt_first_guess_ [%s], ret=%d [%s]", format(), ret, value.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::sqrt(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber result;
|
|
|
|
// this check is necessary to avoid "quotient = arg (0) / guess (0)"
|
|
// OB_DIVISION_BY_ZERO error
|
|
if (is_zero()) {
|
|
if (OB_FAIL(result.from(number::ObNumber::get_zero(), allocator))) {
|
|
LOG_WARN("set result to 0 failed", K(ret));
|
|
}
|
|
} else if (is_negative()) {
|
|
LOG_WARN("cannot take sqrt of negative arg", KPC(this), K(ret));
|
|
ret = OB_ERR_ARGUMENT_OUT_OF_RANGE;
|
|
} else {
|
|
const int NUM_ONE_TIME_CLAC = 2;
|
|
const int ONE_TIME_BUFFER_SIZE = NUM_ONE_TIME_CLAC * MAX_CALC_BYTE_LEN;
|
|
char one_time_buffer[ONE_TIME_BUFFER_SIZE];
|
|
// use one_time_allocator for all intermediate allocations and make sure
|
|
// that the passed-in allocator is only used once for the final result
|
|
ObDataBuffer one_time_allocator(one_time_buffer, ONE_TIME_BUFFER_SIZE);
|
|
|
|
number::ObNumber guess;
|
|
if (OB_FAIL(sqrt_first_guess_(guess, one_time_allocator))) {
|
|
LOG_WARN("failed: sqrt_first_guess_", KPC(this), K(ret));
|
|
} else {
|
|
// main loop of Newton's algorithm
|
|
number::ObNumber quotient;
|
|
number::ObNumber const_zero_point_five;
|
|
number::ObNumber new_guess;
|
|
if (OB_FAIL(const_zero_point_five.from("0.5", one_time_allocator))) {
|
|
LOG_WARN("fail to initialize const_zero_point_five", K(ret));
|
|
}
|
|
|
|
// max number of ObNumber calculations in the following loop. update this
|
|
// when modifying the following loop
|
|
const int NUM_MAX_CALC_IN_LOOP = 3;
|
|
const int LOOP_BUFFER_SIZE = NUM_MAX_CALC_IN_LOOP * MAX_CALC_BYTE_LEN;
|
|
char loop_buffer_1[LOOP_BUFFER_SIZE];
|
|
char loop_buffer_2[LOOP_BUFFER_SIZE];
|
|
ObDataBuffer loop_allocator_current(loop_buffer_1, LOOP_BUFFER_SIZE);
|
|
ObDataBuffer loop_allocator_next(loop_buffer_2, LOOP_BUFFER_SIZE);
|
|
|
|
bool guess_is_answer = false;
|
|
while (OB_SUCC(ret) && !guess_is_answer) {
|
|
if (OB_FAIL(div_v3(guess, quotient, loop_allocator_current, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("failed: quotient = this / guess", KPC(this), K(guess), K(ret));
|
|
} else if (OB_FAIL(guess.add_v3(quotient, new_guess, loop_allocator_current, true, false))) {
|
|
// new guess is the average of current guess and quotient
|
|
LOG_WARN("failed: new_guess = guess + quotient", K(guess), K(quotient), K(ret));
|
|
} else if (OB_FAIL(new_guess.mul(const_zero_point_five, new_guess, loop_allocator_current, false))) {
|
|
// *0.5 is faster than /2 ?
|
|
LOG_WARN("failed: new_guess *= 0.5", K(new_guess), K(ret));
|
|
} else {
|
|
// why not use "quotient == guess" as end condition:
|
|
// - arg = 3.16227766016837933199889354443271853372
|
|
// - guess = 1.77827941003892280122542119519268484474
|
|
// - quotient = 1.77827941003892280122542119519268484473
|
|
// - (guess+quotient)/2 = 1.77827941003892280122542119519268484474 = guess
|
|
// here, guess != quotient, infinite loop. Using guess == new_guess
|
|
// can avoid this.
|
|
if (new_guess.is_equal(guess)) {
|
|
guess_is_answer = true;
|
|
} else {
|
|
guess = new_guess;
|
|
}
|
|
loop_allocator_next.free();
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(result.from(guess, allocator))) {
|
|
LOG_WARN("failed: deep copy guess to result", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding && OB_FAIL(result.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE,
|
|
true, false))) {
|
|
LOG_WARN("result.round_scale_v3_() fail", K(ret), K(result));
|
|
} else {
|
|
value = result;
|
|
}
|
|
}
|
|
|
|
_OB_LOG(DEBUG, "sqrt [%s], ret=%d [%s], do_rounding=%d",
|
|
format(), ret, value.format(), do_rounding);
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::ln(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber result;
|
|
|
|
// check special value
|
|
if (is_zero()) {
|
|
LOG_WARN("cannot get logarithm of zero", KPC(this), K(ret));
|
|
ret = OB_ERR_ARGUMENT_OUT_OF_RANGE;
|
|
} else if (is_negative()) {
|
|
LOG_WARN("cannot get logarithm of a negative number", KPC(this), K(ret));
|
|
ret = OB_ERR_ARGUMENT_OUT_OF_RANGE;
|
|
// TODO check if this is 1 ?
|
|
} else {
|
|
// number of times one_time_allocator is used in this function. update this
|
|
// when modifying this function
|
|
const int NUM_ONE_TIME_CLAC = 14;
|
|
const int ONE_TIME_BUFFER_SIZE = NUM_ONE_TIME_CLAC * MAX_CALC_BYTE_LEN;
|
|
char one_time_buffer[ONE_TIME_BUFFER_SIZE];
|
|
ObDataBuffer one_time_allocator(one_time_buffer, ONE_TIME_BUFFER_SIZE);
|
|
|
|
// max number of ObNumber calculations in all loops in this function. update
|
|
// this when modifying while loop in this fun.
|
|
const int NUM_MAX_CALC_IN_LOOP = 4;
|
|
const int LOOP_BUFFER_SIZE = NUM_MAX_CALC_IN_LOOP * MAX_CALC_BYTE_LEN;
|
|
char loop_buffer_1[LOOP_BUFFER_SIZE];
|
|
char loop_buffer_2[LOOP_BUFFER_SIZE];
|
|
ObDataBuffer loop_allocator_current(loop_buffer_1, LOOP_BUFFER_SIZE);
|
|
ObDataBuffer loop_allocator_next(loop_buffer_2, LOOP_BUFFER_SIZE);
|
|
|
|
// Reduce *this into range (0.9, 1.1) with repeated sqrt() operations.
|
|
number::ObNumber const_two;
|
|
number::ObNumber const_zero_point_nine;
|
|
number::ObNumber const_one_point_one;
|
|
number::ObNumber reduced_arg;
|
|
number::ObNumber reduction_compensation;
|
|
if (OB_FAIL(const_two.from((int64_t)2, one_time_allocator))) {
|
|
LOG_WARN("fail to initialize const_two", K(ret));
|
|
} else if (OB_FAIL(const_zero_point_nine.from("0.9", one_time_allocator))) {
|
|
LOG_WARN("fail to initialize const_zero_point_nine", K(ret));
|
|
} else if (OB_FAIL(const_one_point_one.from("1.1", one_time_allocator))) {
|
|
LOG_WARN("fail to initialize const_one_point_one", K(ret));
|
|
} else if (OB_FAIL(reduced_arg.from(*this, one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy this to reduced_arg", KPC(this), K(ret));
|
|
} else if (OB_FAIL(reduction_compensation.from(const_two, one_time_allocator))) {
|
|
// reduction_compensation starts with 2 instead of 1, because the
|
|
// following taylor series will get 0.5 * ln(reduced_arg), not
|
|
// ln(reduced_arg)
|
|
LOG_WARN("failed to initialize reduction_compensation", K(ret));
|
|
} else {
|
|
// note: here this and reduced_arg > 0
|
|
while (OB_SUCC(ret) && reduced_arg.compare(const_zero_point_nine) <= 0) {
|
|
if (OB_FAIL(reduced_arg.sqrt(reduced_arg, loop_allocator_current, false))) {
|
|
LOG_WARN("sqrt reduced_arg failed", K(reduced_arg), K(ret));
|
|
} else if (OB_FAIL(reduction_compensation.mul(const_two, reduction_compensation,
|
|
loop_allocator_current, false))) {
|
|
LOG_WARN("reduction_compensation *= 2 failed", K(reduction_compensation), K(ret));
|
|
} else {
|
|
loop_allocator_next.free();
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
}
|
|
while (OB_SUCC(ret) && reduced_arg.compare(const_one_point_one) >= 0) {
|
|
if (OB_FAIL(reduced_arg.sqrt(reduced_arg, loop_allocator_current, false))) {
|
|
LOG_WARN("sqrt reduced_arg failed", K(reduced_arg), K(ret));
|
|
} else if (OB_FAIL(reduction_compensation.mul(const_two, reduction_compensation,
|
|
loop_allocator_current, false))) {
|
|
LOG_WARN("reduction_compensation *= 2 failed", K(reduction_compensation), K(ret));
|
|
} else{
|
|
loop_allocator_next.free();
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
// loop_calc_allocator will be reuse. move numbers that are used later
|
|
// to one_time_allocator
|
|
if (OB_FAIL(reduced_arg.deep_copy_to_allocator_(one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy reduced_arg to one_time_allocator", K(ret));
|
|
} else if (OB_FAIL(reduction_compensation.deep_copy_to_allocator_(one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy reduction_compensation to one_time_allocator", K(ret));
|
|
}
|
|
}
|
|
|
|
// Taylor series: 0.5 * ln(reduced_arg) = 0.5 * ln((1+z)/(1-z)) = z + z^3/3 + z^5/5 + ...,
|
|
// where z = (reduced_arg-1)/(reduced_arg+1)
|
|
if (OB_SUCC(ret)) {
|
|
// initialize vars
|
|
number::ObNumber series_number;
|
|
// z = (reduced_arg-1)/(reduced_arg+1)
|
|
number::ObNumber z;
|
|
// z_square = z^2
|
|
number::ObNumber z_square;
|
|
// tmp is only used to store (reduced_arg+1) when calculating z =
|
|
// (reduced_arg-1)/(reduced_arg+1)
|
|
number::ObNumber tmp;
|
|
// term_x_exponent is z^i
|
|
number::ObNumber term_x_exponent;
|
|
// term is z^i/i
|
|
number::ObNumber term;
|
|
if (OB_FAIL(series_number.from(number::ObNumber::get_positive_one(), one_time_allocator))) {
|
|
LOG_WARN("initialize series_number to 1 failed", K(ret));
|
|
} else if (OB_FAIL(reduced_arg.sub_v3(number::ObNumber::get_positive_one(), z,
|
|
one_time_allocator, true, false))) {
|
|
LOG_WARN("failed: z=reduced_arg-1", K(reduced_arg), K(z), K(ret));
|
|
} else if (OB_FAIL(reduced_arg.add_v3(number::ObNumber::get_positive_one(), tmp,
|
|
one_time_allocator, true, false))) {
|
|
LOG_WARN("failed: tmp=reduced_arg+1", K(reduced_arg), K(tmp), K(ret));
|
|
} else if (OB_FAIL(z.div(tmp, z, one_time_allocator, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
// z = (reduced_arg-1)/(reduced_arg+1) now
|
|
LOG_WARN("failed: z/=tmp, where tmp = reduced_arg+1", K(z), K(tmp), K(ret));
|
|
} else if (OB_FAIL(z.mul(z, z_square, one_time_allocator, false))) {
|
|
LOG_WARN("failed: z_square = z*z", K(z), K(ret));
|
|
} else if (OB_FAIL(term_x_exponent.from(z, one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy z to term_x_exponent", K(ret));
|
|
} else if (OB_FAIL(result.from(z, one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy z to result", K(z), K(ret));
|
|
} else {
|
|
bool term_reachs_zero = false;
|
|
while (OB_SUCC(ret) && !term_reachs_zero) {
|
|
// main loop of the Taylor series
|
|
if (OB_FAIL(series_number.add_v3(const_two, series_number,
|
|
loop_allocator_current, true, false))) {
|
|
LOG_WARN("failed: series_number+=2", K(series_number), K(ret));
|
|
} else if (OB_FAIL(term_x_exponent.mul(z_square, term_x_exponent,
|
|
loop_allocator_current, false))) {
|
|
LOG_WARN("failed: term_x_exponent *= z_square", K(term_x_exponent), K(z_square), K(ret));
|
|
} else if (OB_FAIL(term_x_exponent.div(series_number, term, loop_allocator_current,
|
|
OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("failed: term = term_x_exponent / series_number, K(ret)",
|
|
K(term_x_exponent), K(series_number));
|
|
} else { // now we have the term of this loop
|
|
if (term.is_zero()) {
|
|
term_reachs_zero = true;
|
|
} else {
|
|
if (OB_FAIL(result.add_v3(term, result, loop_allocator_current, true, false))) {
|
|
LOG_WARN("failed: result += term", K(result), K(term), K(ret));
|
|
}
|
|
}
|
|
// this iteration is ending. data from last iteration will not
|
|
// be used any more
|
|
loop_allocator_next.free();
|
|
// should swap internal pointers which point to loop_buffer_1,
|
|
// loop_buffer_2
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
// don't use one_time_allocator here, because result is shallow-copied
|
|
// to value later.
|
|
if (OB_FAIL(result.mul(reduction_compensation, result, allocator, false))) {
|
|
LOG_WARN("failed: result *= reduction_compensation",
|
|
K(result), K(reduction_compensation), K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(result.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE,
|
|
true, false))) {
|
|
LOG_WARN("result.round_scale_v3_() fail", K(ret), K(result));
|
|
} else {
|
|
value = result;
|
|
}
|
|
} else {
|
|
value = result;
|
|
}
|
|
}
|
|
|
|
_OB_LOG(DEBUG, "ln [%s], ret=%d [%s], do_rounding=%d",
|
|
format(), ret, value.format(), do_rounding);
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::e_power(ObNumber &value, ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
LOG_DEBUG("ObExprPower, e_power", K(*this), KPC(this), K(do_rounding));
|
|
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber result;
|
|
|
|
// number of times one_time_allocator is used in this function. update this
|
|
// when modifying this function
|
|
const int NUM_ONE_TIME_CLAC = 9;
|
|
const int ONE_TIME_BUFFER_SIZE = NUM_ONE_TIME_CLAC * MAX_CALC_BYTE_LEN;
|
|
char one_time_buffer[ONE_TIME_BUFFER_SIZE];
|
|
ObDataBuffer one_time_allocator(one_time_buffer, ONE_TIME_BUFFER_SIZE);
|
|
|
|
// max number of ObNumber calculations in all loops in this function. update
|
|
// this when modifying while loop in this fun.
|
|
const int NUM_MAX_CALC_IN_LOOP = 4;
|
|
const int LOOP_BUFFER_SIZE = NUM_MAX_CALC_IN_LOOP * MAX_CALC_BYTE_LEN;
|
|
char loop_buffer_1[LOOP_BUFFER_SIZE];
|
|
char loop_buffer_2[LOOP_BUFFER_SIZE];
|
|
ObDataBuffer loop_allocator_current(loop_buffer_1, LOOP_BUFFER_SIZE);
|
|
ObDataBuffer loop_allocator_next(loop_buffer_2, LOOP_BUFFER_SIZE);
|
|
|
|
if (is_zero()) {
|
|
// note: result.set_one() will raise error if not initialized.
|
|
if (OB_FAIL(result.from(number::ObNumber::get_positive_one(), one_time_allocator))) {
|
|
LOG_WARN("set result to 1 failed", K(ret));
|
|
}
|
|
} else {
|
|
number::ObNumber exponent_new;
|
|
if (OB_FAIL(exponent_new.from(*this, one_time_allocator))) {
|
|
LOG_WARN("deep copy failed", KPC(this), K(ret));
|
|
} else {
|
|
// Reduce exponent_new to the range [-0.01, 0.01] by dividing by 2^n, to
|
|
// improve the convergence rate of the Taylor series.
|
|
const char* small_fraction = "0.01";
|
|
number::ObNumber small_fraction_number;
|
|
number::ObNumber const_two;
|
|
int times_exponent_div_2 = 0;
|
|
if (OB_FAIL(small_fraction_number.from(small_fraction, one_time_allocator))) {
|
|
LOG_WARN("initialize small_fraction_number failed", K(ret));
|
|
} else if (OB_FAIL(const_two.from((int64_t)2, one_time_allocator))) {
|
|
LOG_WARN("fail to initialize const_two", K(ret));
|
|
} else {
|
|
while (OB_SUCC(ret) && exponent_new.abs_compare(small_fraction_number) > 0) {
|
|
if (OB_FAIL(exponent_new.div(const_two, exponent_new,
|
|
loop_allocator_current, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("exponent_new /= 2 failed", K(exponent_new), K(ret));
|
|
} else {
|
|
times_exponent_div_2 ++;
|
|
|
|
loop_allocator_next.free();
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(exponent_new.deep_copy_to_allocator_(one_time_allocator))) {
|
|
LOG_WARN("failed: move exponent_new to one_time_allocator", K(ret));
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG("reduce exponent_new done", K(exponent_new), K(times_exponent_div_2),
|
|
KCSTRING(exponent_new.format()), K(ret));
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
// Use the Taylor series: exp(x) = 1 + x + x^2/2! + x^3/3! + ..., here x
|
|
// is exponent_new
|
|
number::ObNumber term; // current term in the Taylor series
|
|
number::ObNumber series_index;
|
|
if (OB_FAIL(result.from(number::ObNumber::get_positive_one(), one_time_allocator))) {
|
|
LOG_WARN("deep copy positive one failed", K(ret));
|
|
} else if (OB_FAIL(term.from(exponent_new, one_time_allocator))) {
|
|
LOG_WARN("deep copy failed", K(exponent_new), K(ret));
|
|
} else if (OB_FAIL(series_index.from(number::ObNumber::get_positive_one(), one_time_allocator))) {
|
|
LOG_WARN("deep copy positive one failed", K(ret));
|
|
} else {
|
|
LOG_DEBUG("begin taylor series",
|
|
K(result), KCSTRING(result.format()),
|
|
K(term), KCSTRING(term.format()),
|
|
K(series_index), KCSTRING(series_index.format()),
|
|
K(ret));
|
|
do {
|
|
if (OB_FAIL(result.add_v3(term, result, loop_allocator_current, true, false))) {
|
|
LOG_WARN("failed: result += term", K(ret));
|
|
} else if (OB_FAIL(term.mul(exponent_new, term, loop_allocator_current, false))) {
|
|
LOG_WARN("failed: term *= exponent_new", K(ret));
|
|
} else if (OB_FAIL(series_index.add_v3(number::ObNumber::get_positive_one(),
|
|
series_index, loop_allocator_current, true, false))) {
|
|
LOG_WARN("failed: series_index += 1", K(ret));
|
|
} else if (OB_FAIL(term.div(series_index, term, loop_allocator_current,
|
|
OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("failed: term /= series_index", K(ret));
|
|
} else {
|
|
loop_allocator_next.free();
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
LOG_DEBUG("in taylor series",
|
|
K(result), KCSTRING(result.format()),
|
|
K(term), KCSTRING(term.format()),
|
|
K(series_index), KCSTRING(series_index.format()),
|
|
K(ret));
|
|
// end condition: term is so small that it is rounded to 0 in div()
|
|
} while (OB_SUCC(ret) && !term.is_zero());
|
|
LOG_DEBUG("after taylor series",
|
|
K(result), KCSTRING(result.format()),
|
|
K(term), KCSTRING(term.format()),
|
|
K(series_index), KCSTRING(series_index.format()),
|
|
K(ret));
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(result.deep_copy_to_allocator_(one_time_allocator))) {
|
|
LOG_WARN("failed: move result to one_time_allocator", K(ret));
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG("after taylor series, result.deep_copy_to_allocator_",
|
|
K(result), KCSTRING(result.format()), K(ret));
|
|
|
|
// Compensate for the argument range reduction
|
|
while (OB_SUCC(ret) && times_exponent_div_2-- > 0) {
|
|
if (OB_FAIL(result.mul(result, result, loop_allocator_current, false))) {
|
|
LOG_WARN("failed: result *= result", K(ret));
|
|
} else {
|
|
loop_allocator_next.free();
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
LOG_DEBUG("squaring result", K(result), KCSTRING(result.format()), K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(result.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("result.round_scale_v3_() fail", K(ret), K(result));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(value.from(result, allocator))) {
|
|
LOG_WARN("failed: deep copy result to value", K(ret));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
_OB_LOG(DEBUG, "e_power [%s], ret=%d [%s]", format(), ret, value.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::round_remainder(const ObNumber &other, ObNumber &value, ObIAllocator &allocator) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObNumber res;
|
|
char buf_alloc[number::ObNumber::MAX_CALC_BYTE_LEN * 2];
|
|
ObDataBuffer allocator2(buf_alloc, number::ObNumber::MAX_CALC_BYTE_LEN * 2);
|
|
if (OB_UNLIKELY(other.is_zero())) {
|
|
_OB_LOG(ERROR, "[%s] div zero [%s]", to_cstring(*this), to_cstring(other));
|
|
ret = OB_DIVISION_BY_ZERO;
|
|
} else if (is_zero()) {
|
|
res.set_zero();
|
|
} else {
|
|
/*a.round_remainder(b) = a - b * N;
|
|
q = a / b;
|
|
N = q.round_even_number;
|
|
p = b * N;
|
|
*/
|
|
ObNumber q,p;
|
|
if (OB_FAIL(div(other, q, allocator2))) {
|
|
LOG_WARN("division failed", K(*this), K(other));
|
|
} else if (OB_FAIL(q.round_even_number())) {
|
|
LOG_WARN("q round_even_number failed", K(*this), K(other), K(q));
|
|
} else if (OB_FAIL(other.mul(q, p, allocator2))) {
|
|
LOG_WARN("b multiply q failed", K(*this), K(other), K(q), K(p));
|
|
} else if (OB_FAIL(sub_v3(p, res, allocator))) {
|
|
LOG_WARN("a subtract (b*q) failed", K(*this), K(other), K(q), K(p));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
value = res;
|
|
}
|
|
_OB_LOG(DEBUG, "round_remainder_,[%s] %% [%s], ret=%d [%s]", this->format(), other.format(), ret, res.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::power(const int64_t exponent, ObNumber &value,
|
|
ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber result;
|
|
|
|
// max number of times one_time_allocator is used in this function. update
|
|
// this when modifying this function
|
|
const int NUM_ONE_TIME_CLAC = 3;
|
|
const int ONE_TIME_BUFFER_SIZE = NUM_ONE_TIME_CLAC * MAX_CALC_BYTE_LEN;
|
|
char one_time_buffer[ONE_TIME_BUFFER_SIZE];
|
|
ObDataBuffer one_time_allocator(one_time_buffer, ONE_TIME_BUFFER_SIZE);
|
|
|
|
// check special cases
|
|
bool done_in_special_cases = true;
|
|
if (is_zero() && exponent < 0) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
LOG_WARN("division by zero (base is 0 and exponent is negative)", K(ret),
|
|
KPC(this), K(exponent));
|
|
} else {
|
|
switch (exponent) {
|
|
case 0:
|
|
if (OB_FAIL(result.from(number::ObNumber::get_positive_one(), one_time_allocator))) {
|
|
LOG_WARN("copy one failed", K(ret));
|
|
}
|
|
break;
|
|
case 1:
|
|
if (OB_FAIL(result.from(*this, one_time_allocator))) {
|
|
LOG_WARN("copy base failed", K(ret));
|
|
}
|
|
break;
|
|
case -1:
|
|
if (OB_FAIL(number::ObNumber::get_positive_one().div(*this, result, one_time_allocator,
|
|
OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("result=1/base failed", KPC(this), K(ret));
|
|
}
|
|
break;
|
|
case 2:
|
|
if (OB_FAIL(mul(*this, result, one_time_allocator, false))) {
|
|
LOG_WARN("result=base^2 failed", KPC(this), K(ret));
|
|
}
|
|
break;
|
|
default:
|
|
if (is_zero()) {
|
|
if (OB_FAIL(result.from(number::ObNumber::get_zero(), one_time_allocator))) {
|
|
LOG_WARN("copy zero failed", K(ret));
|
|
}
|
|
} else {
|
|
done_in_special_cases = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && not done_in_special_cases) {
|
|
bool is_exponent_negative = (exponent < 0);
|
|
int abs_exponent = std::abs(exponent);
|
|
|
|
number::ObNumber base_product;
|
|
if (OB_FAIL(base_product.from(*this, one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy this to base_product", K(ret));
|
|
} else if ((abs_exponent & 1) != 0) {
|
|
if (OB_FAIL(result.from(*this, one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy base to result", K(ret));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(result.from(number::ObNumber::get_positive_one(), one_time_allocator))) {
|
|
LOG_WARN("failed: deep copy 1 to result", K(ret));
|
|
}
|
|
}
|
|
// max number of ObNumber calculations in all loops in this function. update
|
|
// this when modifying while loop in this fun.
|
|
const int NUM_MAX_CALC_IN_LOOP = 1;
|
|
const int LOOP_BUFFER_SIZE = NUM_MAX_CALC_IN_LOOP * MAX_CALC_BYTE_LEN;
|
|
char loop_buffer_1[LOOP_BUFFER_SIZE];
|
|
char loop_buffer_2[LOOP_BUFFER_SIZE];
|
|
ObDataBuffer loop_allocator_current(loop_buffer_1, LOOP_BUFFER_SIZE);
|
|
ObDataBuffer loop_allocator_next(loop_buffer_2, LOOP_BUFFER_SIZE);
|
|
|
|
// in the following while loop, when abs_exponent & 1 == 0, "result *=
|
|
// base_product" is not calculated and result is still store in the old
|
|
// allocator. because of this, we can't store it in loop_allocator_current
|
|
// without deep-copy. And to avoid deep-copying result in each loop where
|
|
// abs_exponent & 1 == 0, we use separated allocators for result.
|
|
const int RESULT_BUFFER_SIZE = MAX_CALC_BYTE_LEN;
|
|
char result_buffer_1[RESULT_BUFFER_SIZE];
|
|
char result_buffer_2[RESULT_BUFFER_SIZE];
|
|
ObDataBuffer result_allocator_current(result_buffer_1, RESULT_BUFFER_SIZE);
|
|
ObDataBuffer result_allocator_next(result_buffer_2, RESULT_BUFFER_SIZE);
|
|
while (OB_SUCC(ret) and (abs_exponent >>= 1) > 0) {
|
|
if (OB_FAIL(base_product.mul(base_product, base_product, loop_allocator_current, false))) {
|
|
LOG_WARN("failed: square base_product", K(ret));
|
|
} else if (abs_exponent & 1) {
|
|
if (OB_FAIL(result.mul(base_product, result, result_allocator_current, false))) {
|
|
LOG_WARN("failed: result *= base_product", K(ret));
|
|
} else {
|
|
result_allocator_next.free();
|
|
std::swap(result_allocator_current, result_allocator_next);
|
|
}
|
|
}
|
|
|
|
// TODO check overflow(positive exponent)/underflow(negative exponent)
|
|
if (OB_SUCC(ret)) {
|
|
loop_allocator_next.free();
|
|
std::swap(loop_allocator_current, loop_allocator_next);
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (is_exponent_negative) {
|
|
if (OB_FAIL(number::ObNumber::get_positive_one().div(result, result, one_time_allocator,
|
|
OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("failed: result = 1 / result", K(ret));
|
|
}
|
|
} else if (OB_FAIL(result.deep_copy_to_allocator_(one_time_allocator))) {
|
|
LOG_WARN("failed: move result to one_time_allocator", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(result.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("result.round_scale_v3_() fail", K(ret), K(result));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(value.from(result, allocator))) {
|
|
LOG_WARN("failed: deep copy result to value", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
_OB_LOG(DEBUG, "power (int exponent) ( [%s], [%ld] ), do_rounding=%d, ret=%d [%s]",
|
|
(ret == OB_SUCCESS ? format() : "error"), exponent, do_rounding, ret,
|
|
(ret == OB_SUCCESS ? result.format() : "error"));
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::power(const ObNumber &exponent, ObNumber &value,
|
|
ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber result;
|
|
|
|
LOG_DEBUG("power() start", KPC(this), K(exponent), K(do_rounding));
|
|
|
|
// max number of times one_time_allocator is used in this function. update
|
|
// this when modifying this function
|
|
const int NUM_ONE_TIME_CLAC = 3;
|
|
const int ONE_TIME_BUFFER_SIZE = NUM_ONE_TIME_CLAC * MAX_CALC_BYTE_LEN;
|
|
char one_time_buffer[ONE_TIME_BUFFER_SIZE];
|
|
ObDataBuffer one_time_allocator(one_time_buffer, ONE_TIME_BUFFER_SIZE);
|
|
|
|
// if exponent is an integer and can store in an int64_t, use power(int)
|
|
int64_t exponent_int = 0;
|
|
// TODO which method to use? is_integer(), is_valid_int(), ...
|
|
if (exponent.is_integer() && OB_SUCCESS == exponent.cast_to_int64(exponent_int)) {
|
|
LOG_DEBUG("use power(int)", KPC(this), K(exponent_int));
|
|
if (OB_FAIL(power(exponent_int, result, one_time_allocator, false))) {
|
|
LOG_WARN("failed: power(int)", KPC(this), K(exponent_int), K(result), K(ret));
|
|
}
|
|
} else if (is_zero()){
|
|
if (exponent.is_negative()) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
LOG_WARN("calc power failed, exp is negative and base is zero", K(ret));
|
|
} else if (exponent.is_zero()) {
|
|
// 0 ^ 0 = 1 is handled in power(int), not here.
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("cannot reach here", K(ret));
|
|
} else {
|
|
if (OB_FAIL(result.from(number::ObNumber::get_zero(), one_time_allocator))) {
|
|
LOG_WARN("failed: copy zero to result", K(ret));
|
|
}
|
|
}
|
|
// TODO check if base is 1 ?
|
|
} else {
|
|
// the main path: result = e^(ln(base)*exponent)
|
|
if (OB_FAIL(ln(result, one_time_allocator, false))) {
|
|
LOG_WARN("failed: result = ln(base)", K(ret));
|
|
} else if (OB_FAIL(result.mul(exponent, result, one_time_allocator, false))) {
|
|
LOG_WARN("failed: result *= exponent", K(ret));
|
|
} else if (OB_FAIL(result.e_power(result, one_time_allocator, false))) {
|
|
LOG_WARN("failed: result = e^result", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(result.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("result.round_scale_v3_() fail", K(ret), K(result));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(value.from(result, allocator))) {
|
|
LOG_WARN("failed: deep copy result to value", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
_OB_LOG(DEBUG, "power ( [%s], [%s] ), do_rounding=%d, ret=%d [%s]",
|
|
format(), exponent.format(), do_rounding, ret, result.format());
|
|
return ret;
|
|
}
|
|
|
|
int ObNumber::log(const ObNumber &base, ObNumber &value,
|
|
ObIAllocator &allocator, const bool do_rounding/*true*/) const
|
|
{
|
|
// log_b(x) = ln(x)/ln(b)
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber result;
|
|
|
|
LOG_DEBUG("log() start", KPC(this), K(base), K(do_rounding));
|
|
|
|
// max number of times one_time_allocator is used in this function. update
|
|
// this when modifying this function
|
|
const int NUM_ONE_TIME_CLAC = 3;
|
|
const int ONE_TIME_BUFFER_SIZE = NUM_ONE_TIME_CLAC * MAX_CALC_BYTE_LEN;
|
|
char one_time_buffer[ONE_TIME_BUFFER_SIZE];
|
|
ObDataBuffer one_time_allocator(one_time_buffer, ONE_TIME_BUFFER_SIZE);
|
|
|
|
// check arguments
|
|
if (base.is_zero() || base.is_negative()) {
|
|
ret = OB_ERR_ARGUMENT_OUT_OF_RANGE;
|
|
LOG_WARN("the base of logarithm has to be positive", K(base), K(ret));
|
|
} else if (base.compare(number::ObNumber::get_positive_one()) == 0) {
|
|
ret = OB_ERR_ARGUMENT_OUT_OF_RANGE;
|
|
LOG_WARN("the base of logarithm can't be 1", K(base), K(ret));
|
|
// log_b(x), the validity of x will be check in ln(x)
|
|
} else {
|
|
number::ObNumber ln_x;
|
|
number::ObNumber ln_base;
|
|
if (OB_FAIL(ln(ln_x, one_time_allocator, false))) {
|
|
LOG_WARN("failed: ln_x = ln(this)", KPC(this), K(ret));
|
|
} else if (OB_FAIL(base.ln(ln_base, one_time_allocator, false))) {
|
|
LOG_WARN("failed: ln_base = ln(base)", K(ret));
|
|
} else if (OB_FAIL(ln_x.div(ln_base, result, one_time_allocator, OB_MAX_DECIMAL_DIGIT, false))) {
|
|
LOG_WARN("failed: result = ln_x / ln_base", K(ln_x), K(ln_base), K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (do_rounding) {
|
|
if (OB_FAIL(result.round_scale_v3_(is_oracle_mode() ? MAX_SCALE : FLOATING_SCALE, true, false))) {
|
|
LOG_WARN("result.round_scale_v3_() fail", K(ret), K(result));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(value.from(result, allocator))) {
|
|
LOG_WARN("failed: deep copy result to value", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
_OB_LOG(DEBUG, "log ( [%s], [%s] ), do_rounding=%d, ret=%d [%s]",
|
|
format(), base.format(), do_rounding, ret, result.format());
|
|
return ret;
|
|
}
|
|
|
|
// int64_t range [-9,223,372,036,854,775,808, 9,223,372,036,854,775,807]
|
|
bool ObNumber::is_int64() const
|
|
{
|
|
bool b_ret = true;
|
|
if (is_zero()) {
|
|
b_ret = true;
|
|
} else {
|
|
const int32_t exp = get_decode_exp(d_);
|
|
if (!is_integer(exp)) { //exponential must great than or equal to 0
|
|
b_ret = false;
|
|
} else if (exp > 2) { //exponential must less than or equal to 2
|
|
b_ret = false;
|
|
} else if (2 == exp){
|
|
const uint32_t MAX_HIGH_NUM = 9;
|
|
const uint32_t MAX_MID_NUM = 223372036;
|
|
const uint32_t MAX_POSITIVE_LOW_NUM = 854775807;
|
|
const uint32_t MAX_NEGATIVE_LOW_NUM = 854775808;
|
|
// no need to check digits_ max length because it is already guaranteed in is_integer(exp)
|
|
// that: exp + 1 >= d_.len_. So here max length of digits_ is 3.
|
|
if (digits_[0] > MAX_HIGH_NUM) {
|
|
b_ret = false;
|
|
} else if (d_.len_ >= 2 && MAX_HIGH_NUM == digits_[0] && digits_[1] > MAX_MID_NUM) {
|
|
b_ret = false;
|
|
} else if (d_.len_ >= 3 && MAX_HIGH_NUM == digits_[0] && MAX_MID_NUM == digits_[1]) {
|
|
if (is_negative() && digits_[2] > MAX_NEGATIVE_LOW_NUM) {
|
|
b_ret = false;
|
|
} else if (!is_negative() && digits_[2] > MAX_POSITIVE_LOW_NUM) {
|
|
b_ret = false;
|
|
} else {
|
|
b_ret = true;
|
|
}
|
|
} else {
|
|
b_ret = true;
|
|
}
|
|
} else { // exponential is 0 or 1
|
|
b_ret = true;
|
|
}
|
|
}
|
|
return b_ret;
|
|
}
|
|
|
|
int ObNumber::cast_to_int64(int64_t &value) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_valid_integer = true;
|
|
uint64_t tmp_value = 0;
|
|
if (is_zero()) {
|
|
value = 0;
|
|
} else if (!is_int64()) {
|
|
is_valid_integer = false;
|
|
} else {
|
|
const int32_t expr_value = get_decode_exp(d_);
|
|
switch (expr_value) {
|
|
case 0: {
|
|
if (1 == d_.len_) {
|
|
tmp_value = digits_[0];
|
|
}
|
|
break;
|
|
}
|
|
case 1: {
|
|
if (1 == d_.len_) {
|
|
tmp_value = digits_[0] * BASE;
|
|
} else if (2 == d_.len_) {
|
|
tmp_value = digits_[0] * BASE + digits_[1];
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
switch (d_.len_) {
|
|
case 1: {
|
|
const uint64_t tmp_v1 = digits_[0] * BASE;
|
|
tmp_value = tmp_v1 * BASE;
|
|
break;
|
|
}
|
|
case 2: {
|
|
const uint64_t tmp_v1 = digits_[0] * BASE;
|
|
const uint64_t tmp_v2 = tmp_v1 * BASE;
|
|
const uint64_t tmp_v3 = digits_[1] * BASE;
|
|
tmp_value = tmp_v2 + tmp_v3;
|
|
break;
|
|
}
|
|
case 3: {
|
|
const uint64_t tmp_v1 = digits_[0] * BASE;
|
|
const uint64_t tmp_v2 = tmp_v1 * BASE;
|
|
const uint64_t tmp_v3 = digits_[1] * BASE;
|
|
tmp_value = tmp_v2 + tmp_v3 + digits_[2];
|
|
break;
|
|
}
|
|
default : {
|
|
is_valid_integer = false;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default : {
|
|
is_valid_integer = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (is_valid_integer) {
|
|
value = is_negative() ? 0 - tmp_value : tmp_value;
|
|
LOG_DEBUG("finish cast_to_int64", K(tmp_value), K(is_valid_integer), KPC(this), K(value));
|
|
}
|
|
}
|
|
|
|
if (OB_UNLIKELY(!is_valid_integer)) {
|
|
ret = OB_INTEGER_PRECISION_OVERFLOW;
|
|
LOG_DEBUG("this is not valid integer number", KPC(this), K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* check whether a sci format string has a valid exponent part
|
|
* valid : 1.8E-1/1.8E1 invalid : 1.8E, 1.8Ea, 1.8E-a
|
|
* @param str string need to parse
|
|
* @param length length of str
|
|
* @param e_pos index of 'E'
|
|
*/
|
|
bool ObNumber::is_valid_sci_tail_(const char *str,
|
|
const int64_t length,
|
|
const int64_t e_pos)
|
|
{
|
|
bool res = false;
|
|
if (e_pos == length - 1) {
|
|
//like 1.8e, false
|
|
} else if (e_pos < length - 1) {
|
|
if ('+' == str[e_pos + 1] || '-' == str[e_pos + 1]) {
|
|
if (e_pos < length - 2 && str[e_pos + 2] >= '0' && str[e_pos + 2] <= '9') {
|
|
res = true;
|
|
} else {
|
|
//like 1.8e+, false
|
|
}
|
|
} else if (str[e_pos + 1] >= '0' && str[e_pos + 1] <= '9') {
|
|
res = true;
|
|
} else {
|
|
//like 1.8ea, false
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void ObNumber::set_one()
|
|
{
|
|
if (OB_ISNULL(digits_)) {
|
|
_OB_LOG_RET(ERROR, OB_ERROR, "number digit ptr is null where set to one!");
|
|
right_to_die_or_duty_to_live();
|
|
} else {
|
|
d_.len_ = 1;
|
|
digits_[0] = 1;
|
|
d_.se_ = (POSITIVE == d_.sign_ ? 0xc0 : 0x40);
|
|
}
|
|
}
|
|
|
|
int64_t ObNumber::get_digit_len_v2(const uint32_t d)
|
|
{
|
|
int64_t ret = 0;
|
|
if (OB_UNLIKELY(BASE <= d)) {
|
|
LIB_LOG(ERROR, "d is out of range");
|
|
} else {
|
|
ret = ob_fast_digits10(d);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ObIntegerBuilder::ObIntegerBuilder() : exp_(0),
|
|
digit_pos_(ObNumber::MAX_CALC_LEN - 1),
|
|
digit_idx_(0)
|
|
{
|
|
}
|
|
|
|
ObIntegerBuilder::~ObIntegerBuilder()
|
|
{
|
|
}
|
|
|
|
int ObIntegerBuilder::push(const uint8_t d, const bool reduce_zero)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_UNLIKELY(get_length() > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
} else {
|
|
if (0 == digit_idx_) {
|
|
// Init current digit on first use
|
|
digits_[digit_pos_] = 0;
|
|
}
|
|
|
|
// 1234
|
|
// push [4]: 4 = 4
|
|
// push [3]: 3*10 + 4= 34
|
|
// push [2]: 2*100 + 34 = 234
|
|
// push [1]: 1*1000 + 234 = 1234
|
|
static const uint32_t POWS[ObNumber::DIGIT_LEN] = {
|
|
1,
|
|
10,
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000,
|
|
1000000,
|
|
10000000,
|
|
100000000
|
|
};
|
|
|
|
digits_[digit_pos_] += d * POWS[digit_idx_++];
|
|
|
|
if (ObNumber::DIGIT_LEN <= digit_idx_) {
|
|
if (!reduce_zero
|
|
|| 0 != digits_[ObNumber::MAX_CALC_LEN - 1]) {
|
|
--digit_pos_;
|
|
}
|
|
digit_idx_ = 0;
|
|
++exp_;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObIntegerBuilder::push_digit(const uint32_t d, const bool reduce_zero)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(digits_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null", K(ret));
|
|
} else if (OB_UNLIKELY(get_length() > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
LOG_WARN("numeric_overflow", K(digit_idx_), K(digit_pos_), K(ret));
|
|
} else {
|
|
digits_[digit_pos_] = d;
|
|
if (!reduce_zero || 0 != digits_[ObNumber::MAX_CALC_LEN - 1]) {
|
|
--digit_pos_ ;
|
|
}
|
|
++ exp_;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int64_t ObIntegerBuilder::get_exp() const
|
|
{
|
|
return (0 == digit_idx_) ? (exp_ - 1) : exp_;
|
|
}
|
|
|
|
int64_t ObIntegerBuilder::get_length() const
|
|
{
|
|
return (0 == digit_idx_) ? (ObNumber::MAX_CALC_LEN - digit_pos_ - 1) :
|
|
(ObNumber::MAX_CALC_LEN - digit_pos_);
|
|
}
|
|
|
|
const uint32_t *ObIntegerBuilder::get_digits() const
|
|
{
|
|
return (0 == get_length()) ? NULL : &digits_[ObNumber::MAX_CALC_LEN - get_length()];
|
|
}
|
|
|
|
void ObIntegerBuilder::reset()
|
|
{
|
|
exp_ = 0;
|
|
digit_pos_ = ObNumber::MAX_CALC_LEN - 1;
|
|
digit_idx_ = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ObDecimalBuilder::ObDecimalBuilder() : exp_(-1),
|
|
digit_pos_(0),
|
|
digit_idx_(0)
|
|
{
|
|
}
|
|
|
|
ObDecimalBuilder::~ObDecimalBuilder()
|
|
{
|
|
}
|
|
|
|
int ObDecimalBuilder::push(const uint8_t d, const bool reduce_zero)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_UNLIKELY(get_length() >= ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
} else {
|
|
if (0 == digit_idx_) {
|
|
// Init current digit on first use
|
|
digits_[digit_pos_] = 0;
|
|
}
|
|
|
|
// 0.1234
|
|
// push [1]: 0 + 1*100000000 = 100000000
|
|
// push [2]: 100000000 + 2*10000000 = 120000000
|
|
// push [3]: 120000000 + 3*1000000 = 123000000
|
|
// push [4]: 123000000 + 4*100000 = 123400000
|
|
static const uint32_t POWS[ObNumber::DIGIT_LEN] = {
|
|
100000000,
|
|
10000000,
|
|
1000000,
|
|
100000,
|
|
10000,
|
|
1000,
|
|
100,
|
|
10,
|
|
1
|
|
};
|
|
digits_[digit_pos_] += d * POWS[digit_idx_++];
|
|
|
|
if (ObNumber::DIGIT_LEN <= digit_idx_) {
|
|
if (!reduce_zero
|
|
|| 0 != digits_[0]) {
|
|
digit_pos_ += 1;
|
|
} else {
|
|
--exp_;
|
|
}
|
|
digit_idx_ = 0;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObDecimalBuilder::push_digit(const uint32_t d, const bool reduce_zero)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_UNLIKELY(get_length() >= ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
LOG_WARN("numeric_overflow", K(digit_idx_), K(digit_pos_), K(ret));
|
|
} else {
|
|
digits_[digit_pos_] = d;
|
|
if (!reduce_zero || 0 != digits_[0]) {
|
|
++digit_pos_;
|
|
}
|
|
else {
|
|
--exp_;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int64_t ObDecimalBuilder::get_exp() const
|
|
{
|
|
return exp_;
|
|
}
|
|
|
|
int64_t ObDecimalBuilder::get_length() const
|
|
{
|
|
return (0 == digit_idx_) ? digit_pos_ : (digit_pos_ + 1);
|
|
}
|
|
|
|
const uint32_t *ObDecimalBuilder::get_digits() const
|
|
{
|
|
return (0 == get_length()) ? NULL : &digits_[0];
|
|
}
|
|
|
|
void ObDecimalBuilder::reset()
|
|
{
|
|
exp_ = -1;
|
|
digit_pos_ = 0;
|
|
digit_idx_ = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ObNumberBuilder::reset()
|
|
{
|
|
number_.set_zero();
|
|
}
|
|
|
|
int ObNumberBuilder::build(const char *str,
|
|
int64_t length,
|
|
int &warning,
|
|
ObNumberFmtModel *fmt,
|
|
int16_t *precision,
|
|
int16_t *scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
reset();
|
|
bool negative = false;
|
|
int64_t integer_start = -1;
|
|
int64_t integer_end = -1;
|
|
int64_t decimal_start = -1;
|
|
bool integer_zero = false;
|
|
bool decimal_zero = false;
|
|
char new_str[ObNumber::MAX_TOTAL_SCALE];
|
|
int64_t comma_cnt = 0;
|
|
if (OB_ISNULL(number_.get_digits())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(WARN, "digits_ should not be null when this func is invoked", K(ret));
|
|
} else {
|
|
if (OB_ISNULL(fmt)) {
|
|
if (OB_FAIL(find_point_(str, length, integer_start, integer_end, decimal_start,
|
|
negative, integer_zero, decimal_zero, warning))) {
|
|
LIB_LOG(WARN, "lookup fail", K(ret), K(length), "str", ObString(length, str));
|
|
}
|
|
} else {
|
|
if (OB_FAIL(find_point_(str, fmt, &new_str[0], length, integer_start, integer_end, decimal_start,
|
|
negative, integer_zero, decimal_zero, comma_cnt))) {
|
|
LIB_LOG(WARN, "lookup fail ", K(ret), "str", ObString(length, str), KCSTRING(fmt->fmt_str_), K(fmt->has_b_), K(fmt->has_currency_),
|
|
K(fmt->has_d_), K(fmt->has_sign_));
|
|
} else {
|
|
LIB_LOG(DEBUG, "lookup success with fmt", K(ret), KCSTRING(fmt->fmt_str_), K(fmt->has_b_), K(fmt->has_currency_),
|
|
K(fmt->has_d_), K(fmt->has_sign_), KCSTRING(new_str), K(length), K(integer_start), K(integer_end),
|
|
K(decimal_start), K(negative), K(integer_zero), K(decimal_zero));
|
|
str = &new_str[0];
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(build_integer_(str, integer_start, integer_end, decimal_zero, fmt))) {
|
|
LIB_LOG(WARN, "build integer fail", K(ret), K(length), "str", ObString(length, str), K(integer_start), K(integer_end), K(decimal_zero));
|
|
} else if (OB_FAIL(build_decimal_(str, length, decimal_start, integer_zero))) {
|
|
LIB_LOG(WARN, "build decimal fail", K(ret), K(length), "str", ObString(length, str), K(decimal_start), K(integer_zero));
|
|
} else if (OB_UNLIKELY(ib_.get_length() < 0 || db_.get_length() < 0)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(ERROR, "unexpected integer length or decimal length. ",
|
|
K(ret), K(ib_.get_length()), K(db_.get_length()));
|
|
} else if (OB_UNLIKELY(ib_.get_length() + db_.get_length() > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
LIB_LOG(WARN, "out of range, integer length, decimal length",
|
|
K(ret), K(ib_.get_length()), K(db_.get_length()));
|
|
} else {
|
|
if (!negative) {
|
|
number_.d_.sign_ = ObNumber::POSITIVE;
|
|
} else {
|
|
number_.d_.sign_ = ObNumber::NEGATIVE;
|
|
}
|
|
|
|
if (0 != ib_.get_length()) {
|
|
number_.d_.exp_ = 0x7f & (uint8_t)(ObNumber::EXP_ZERO + ib_.get_exp());
|
|
} else {
|
|
number_.d_.exp_ = 0x7f & (uint8_t)(ObNumber::EXP_ZERO + db_.get_exp());
|
|
}
|
|
|
|
number_.d_.len_ = (uint8_t)(ib_.get_length() + db_.get_length());
|
|
if (negative) {
|
|
number_.d_.exp_ = (0x7f & ~number_.d_.exp_);
|
|
++number_.d_.exp_;
|
|
}
|
|
|
|
if (NULL != number_.get_digits()
|
|
&& 0 < ib_.get_length()) {
|
|
MEMCPY(number_.get_digits(), ib_.get_digits(), ib_.get_length() * ITEM_SIZE(number_.get_digits()));
|
|
}
|
|
if (NULL != number_.get_digits()
|
|
&& 0 < db_.get_length()) {
|
|
MEMCPY(&number_.get_digits()[ib_.get_length()], db_.get_digits(), db_.get_length() * ITEM_SIZE(number_.get_digits()));
|
|
}
|
|
|
|
if (NULL != precision && NULL != scale) {
|
|
*scale = static_cast<int16_t>((length - decimal_start == -1) ? 0 : (length - decimal_start));
|
|
if (*scale > ObNumber::FLOATING_SCALE) {
|
|
*scale = ObNumber::FLOATING_SCALE;
|
|
}
|
|
*precision = static_cast<int16_t>(integer_end - integer_start + 1 - comma_cnt + *scale);
|
|
}
|
|
ret = number_.normalize_(number_.get_digits(), number_.d_.len_);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObNumberBuilder::build_v2(const char *str,
|
|
int64_t length,
|
|
int &warning,
|
|
ObNumberFmtModel *fmt,
|
|
int16_t *precision,
|
|
int16_t *scale)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
reset();
|
|
bool negative = false;
|
|
int64_t integer_start = -1;
|
|
int64_t integer_end = -1;
|
|
int64_t decimal_start = -1;
|
|
bool integer_zero = false;
|
|
bool decimal_zero = false;
|
|
char new_str[ObNumber::MAX_TOTAL_SCALE];
|
|
int64_t comma_cnt = 0;
|
|
if (OB_ISNULL(number_.get_digits())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(WARN, "digits_ should not be null when this func is invoked", K(ret));
|
|
} else {
|
|
if (OB_ISNULL(fmt)) {
|
|
if (OB_FAIL(lib::is_oracle_mode()
|
|
? find_point_v2_(str, length, integer_start, integer_end, decimal_start,
|
|
negative, integer_zero, decimal_zero, warning)
|
|
: find_point_(str, length, integer_start, integer_end, decimal_start,
|
|
negative, integer_zero, decimal_zero, warning))) {
|
|
LIB_LOG(WARN, "lookup fail", K(ret), K(length), "str", ObString(length, str), "is_oracle_mode", lib::is_oracle_mode());
|
|
}
|
|
} else {
|
|
//used for to_number(format)
|
|
if (OB_FAIL(find_point_(str, fmt, &new_str[0], length, integer_start, integer_end, decimal_start,
|
|
negative, integer_zero, decimal_zero, comma_cnt))) {
|
|
LIB_LOG(WARN, "lookup fail ", K(ret), "str", ObString(length, str), KCSTRING(fmt->fmt_str_), K(fmt->has_b_), K(fmt->has_currency_),
|
|
K(fmt->has_d_), K(fmt->has_sign_), K(fmt->has_x_));
|
|
} else {
|
|
LIB_LOG(DEBUG, "lookup success with fmt", K(ret), KCSTRING(fmt->fmt_str_), K(fmt->has_b_), K(fmt->has_currency_),
|
|
K(fmt->has_d_), K(fmt->has_sign_), K(fmt->has_x_), KCSTRING(new_str), K(length), K(integer_start), K(integer_end),
|
|
K(decimal_start), K(negative), K(integer_zero), K(decimal_zero));
|
|
str = &new_str[0];
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(build_integer_v2_(str, integer_start, integer_end, decimal_zero, integer_zero, warning, fmt))) {
|
|
LIB_LOG(WARN, "build integer fail", K(ret), K(length), "str", ObString(length, str), K(integer_start), K(integer_end), K(decimal_zero), K(integer_zero));
|
|
} else if (OB_FAIL(build_decimal_v2_(str, length, decimal_start, integer_zero, decimal_zero, warning))) {
|
|
LIB_LOG(WARN, "build decimal fail", K(ret), K(length), "str", ObString(length, str), K(decimal_start), K(integer_zero), K(decimal_zero));
|
|
} else if (OB_UNLIKELY(ib_.get_length() < 0) || OB_UNLIKELY(db_.get_length() < 0)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(ERROR, "unexpected integer length or decimal length. ",
|
|
K(ret), K(ib_.get_length()), K(db_.get_length()));
|
|
} else if (OB_UNLIKELY(ib_.get_length() + db_.get_length() > ObNumber::MAX_CALC_LEN)) {
|
|
ret = OB_ERROR_OUT_OF_RANGE;
|
|
LIB_LOG(WARN, "out of range, integer length, decimal length",
|
|
K(ret), K(ib_.get_length()), K(db_.get_length()));
|
|
} else {
|
|
number_.d_.sign_ = 0x01 & (negative ? ObNumber::NEGATIVE : ObNumber::POSITIVE);
|
|
number_.d_.len_ = (uint8_t)(ib_.get_length() + db_.get_length());
|
|
number_.d_.exp_ = 0x7f & (uint8_t)(ObNumber::EXP_ZERO + (ib_.get_length() != 0 ? ib_.get_exp() : db_.get_exp()));
|
|
if (negative) {
|
|
number_.d_.exp_ = (0x7f & ~number_.d_.exp_);
|
|
++number_.d_.exp_;
|
|
}
|
|
MEMCPY(number_.get_digits(), ib_.get_digits(), ib_.get_length() * ITEM_SIZE(number_.get_digits()));
|
|
MEMCPY(number_.get_digits() + ib_.get_length(), db_.get_digits(), db_.get_length() * ITEM_SIZE(number_.get_digits()));
|
|
|
|
if (NULL != precision && NULL != scale) {
|
|
*scale = static_cast<int16_t>((length - decimal_start == -1) ? 0 : (length - decimal_start));
|
|
if (*scale > ObNumber::FLOATING_SCALE) {
|
|
*scale = ObNumber::FLOATING_SCALE;
|
|
}
|
|
*precision = static_cast<int16_t>(integer_end - integer_start + 1 - comma_cnt + *scale);
|
|
}
|
|
ret = number_.normalize_v3_(false);
|
|
}
|
|
}
|
|
LIB_LOG(DEBUG, "succ to build_v2", K(number_), K(ret), K(warning));
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::build_integer_(const char *str, const int64_t integer_start,
|
|
const int64_t integer_end, const bool reduce_zero)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ib_.reset();
|
|
int64_t skiped_zero_counter = 0;
|
|
if (OB_UNLIKELY(integer_start <= integer_end && NULL == str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else if (integer_start >= 0 && integer_end >= 0) {
|
|
for (int64_t i = integer_end; i >= integer_start; --i) {
|
|
char c = str[i];
|
|
if ('0' == c) {
|
|
++skiped_zero_counter;
|
|
continue;
|
|
} else {
|
|
for (int64_t j = 0; j < skiped_zero_counter; ++j) {
|
|
if (OB_FAIL(ib_.push(0, reduce_zero))) {
|
|
LOG_WARN("push to integer builder fail", K(ret), K(j));
|
|
break;
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
break;
|
|
}
|
|
skiped_zero_counter = 0;
|
|
}
|
|
if (OB_FAIL(ib_.push((uint8_t)(c - '0'), reduce_zero))) {
|
|
LOG_WARN("push to integer builder fail", K(ret), K(c));
|
|
break;
|
|
}
|
|
}
|
|
} else { /* Do nothing */ }
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::build_integer_v2_(const char *str, const int64_t integer_start,
|
|
const int64_t integer_end, const bool reduce_zero, const bool integer_zero, int &warning)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
static const uint32_t POWS[ObNumber::DIGIT_LEN] = {1, 10, 100, 1000, 10000, 100000,
|
|
1000000, 10000000, 100000000};
|
|
|
|
if (integer_zero) {
|
|
ib_.reset();
|
|
} else if (OB_UNLIKELY(integer_start <= integer_end) && OB_ISNULL(str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null", K(integer_start), K(integer_end), K(ret));
|
|
} else if (OB_LIKELY(integer_start >= 0) && OB_LIKELY(integer_end >= 0)) {
|
|
uint32_t tmp = 0;
|
|
int64_t idx = 0;
|
|
int64_t non_zero_start = integer_start;
|
|
while (non_zero_start <= integer_end && str[non_zero_start] == '0') {
|
|
++non_zero_start;
|
|
}
|
|
for (int64_t i = integer_end; OB_SUCC(ret) && i >= non_zero_start; --i) {
|
|
int32_t tmp_value = str[i] - '0';
|
|
if (OB_UNLIKELY(tmp_value < 0) || OB_UNLIKELY(tmp_value > 9)) {
|
|
warning = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error: got ", K(ret), K(warning), K(str[i]), K(tmp_value));
|
|
} else {
|
|
tmp += static_cast<uint32_t>(POWS[idx++] * tmp_value);
|
|
if (idx >= ObNumber::DIGIT_LEN || i == non_zero_start) {
|
|
if (OB_FAIL(ib_.push_digit(tmp, reduce_zero))) {
|
|
LOG_WARN("push to integer builder fail", K(ret), K(i));
|
|
break;
|
|
}
|
|
tmp = 0;
|
|
idx = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::hex_to_num_(char c, int32_t &val) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
val = c - '0';
|
|
} else if (c >= 'a' && c <= 'z') {
|
|
val = c - 'a' + 10;
|
|
} else if (c >= 'A' && c <= 'Z') {
|
|
val = c - 'A' + 10;
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_ERROR("bad hexadecimal character", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::multiply_(int32_t multiplier, char *str, int32_t &len) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
if (OB_ISNULL(str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else if (len >= ObNumber::MAX_TOTAL_SCALE) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("str exceeds the max length");
|
|
} else {
|
|
int32_t carry = 0;
|
|
int32_t i = len;
|
|
int32_t tmp_str_idx = 0;
|
|
char tmp_str[ObNumber::MAX_TOTAL_SCALE] = {0};
|
|
while (OB_SUCC(ret) && --i >= 0) {
|
|
int val = 0;
|
|
if (OB_FAIL(hex_to_num_(str[i], val))) {
|
|
LOG_ERROR("failed to hex_to_num_", K(ret));
|
|
} else {
|
|
val = val * multiplier + carry;
|
|
carry = std::floor(val / 10);
|
|
tmp_str[tmp_str_idx++] = (val % 10) + '0';
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
while (carry > 0) {
|
|
tmp_str[tmp_str_idx++] = (carry % 10) + '0';
|
|
carry = std::floor(carry / 10);
|
|
}
|
|
// reverse tmp str
|
|
len = 0;
|
|
i = --tmp_str_idx;
|
|
for (; i >= 0; --i) {
|
|
str[len++] = tmp_str[i];
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::add_hex_str_(const char *str1, int32_t str1_len,
|
|
char *str2, int32_t &str2_len) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
if (OB_ISNULL(str1)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else if (str1_len >= ObNumber::MAX_TOTAL_SCALE) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("str exceeds the max length");
|
|
} else {
|
|
int32_t carry = 0;
|
|
int32_t i = 0;
|
|
int val = 0;
|
|
int32_t tmp_str_idx = 0;
|
|
char tmp_str[ObNumber::MAX_TOTAL_SCALE] = {0};
|
|
while (OB_SUCC(ret) && i < str1_len && i < str2_len) {
|
|
int32_t val1 = 0;
|
|
int32_t val2 = 0;
|
|
if (OB_FAIL(hex_to_num_(str1[str1_len - i - 1], val1))
|
|
|| OB_FAIL(hex_to_num_(str2[str2_len - i - 1], val2))) {
|
|
LOG_ERROR("failed to hex_to_num_", K(ret));
|
|
} else {
|
|
val = val1 + val2 + carry;
|
|
carry = std::floor(val / 10);
|
|
tmp_str[tmp_str_idx++] = (val % 10) + '0';
|
|
i++;
|
|
}
|
|
}
|
|
while(OB_SUCC(ret) && i < str1_len) {
|
|
int32_t val1 = 0;
|
|
if (OB_FAIL(hex_to_num_(str1[str1_len - i - 1], val1))) {
|
|
LOG_ERROR("failed to hex_to_num_", K(ret));
|
|
} else {
|
|
val = val1 + carry;
|
|
carry = std::floor(val / 10);
|
|
tmp_str[tmp_str_idx++] = (val % 10) + '0';
|
|
i++;
|
|
}
|
|
}
|
|
while(OB_SUCC(ret) && i < str2_len) {
|
|
int32_t val2 = 0;
|
|
if (OB_FAIL(hex_to_num_(str2[str2_len - i - 1], val2))) {
|
|
LOG_ERROR("failed to hex_to_num_", K(ret));
|
|
} else {
|
|
val = val2 + carry;
|
|
carry = std::floor(val / 10);
|
|
tmp_str[tmp_str_idx++] = (val % 10) + '0';
|
|
i++;
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
while (carry > 0) {
|
|
tmp_str[tmp_str_idx++] = (carry % 10) + '0';
|
|
carry = std::floor(carry / 10);
|
|
}
|
|
// reverse tmp str
|
|
str2_len = 0;
|
|
i = --tmp_str_idx;
|
|
for (; i >= 0; --i) {
|
|
str2[str2_len++] = tmp_str[i];
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::hex_to_dec_(const char *hex_str, int32_t hex_len,
|
|
char *dec_str, int32_t &dec_len) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int32_t i = 0;
|
|
char str[ObNumber::MAX_TOTAL_SCALE] = {0};
|
|
while (OB_SUCC(ret) && i < hex_len) {
|
|
int32_t j = i;
|
|
str[0] = hex_str[hex_len - 1 - i];
|
|
int32_t str_len = 1;
|
|
while (OB_SUCC(ret) && j--) {
|
|
if (OB_FAIL(multiply_(16, str, str_len))) {
|
|
LOG_WARN("failed to multiply_", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
ret = add_hex_str_(str, str_len, dec_str, dec_len);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::build_hex_integer_(const char *str, const int64_t integer_start,
|
|
const int64_t integer_end, const bool reduce_zero,
|
|
ObNumberFmtModel * fmt)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(fmt)) {
|
|
ret = build_integer_(str, integer_start, integer_end, reduce_zero);
|
|
} else {
|
|
// the maximum length of Oracle to_number(123, 'xxx') format is 63
|
|
const int32_t MAX_FORMAT_LEN = 64;
|
|
int64_t c_p = fmt->fmt_len_ - 1;
|
|
ib_.reset();
|
|
if (OB_UNLIKELY(c_p >= MAX_FORMAT_LEN)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the format exceeds the max length");
|
|
} else if (OB_UNLIKELY(integer_start <= integer_end && NULL == str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else if (integer_start >= 0 && integer_end >= 0) {
|
|
int32_t new_len = 0;
|
|
bool digit_appeared = false;
|
|
int64_t i = integer_start;
|
|
char hex_str[ObNumber::MAX_TOTAL_SCALE] = {0};
|
|
char dec_str[ObNumber::MAX_TOTAL_SCALE] = {0};
|
|
for (; i <= integer_end && c_p >= 0; ++i) {
|
|
char c = str[i];
|
|
switch(c) {
|
|
case ',':
|
|
/* do nothing */
|
|
break;
|
|
case '0':
|
|
if (!digit_appeared) {
|
|
/* do nothing */
|
|
} else {
|
|
hex_str[new_len++] = c;
|
|
--c_p;
|
|
}
|
|
break;
|
|
case 'a'...'f':
|
|
case 'A'...'F':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
digit_appeared = true;
|
|
hex_str[new_len++] = c;
|
|
--c_p;
|
|
break;
|
|
default:
|
|
ret = OB_INVALID_NUMERIC;
|
|
LOG_WARN("ObNumber got str error: got ", K(ret), K(c));
|
|
break;
|
|
}
|
|
}
|
|
if (i < integer_end) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LOG_WARN("integer part is longer than fmt str", K(ret), K(i), K(c_p));
|
|
}
|
|
int32_t dec_len = 0;
|
|
if (OB_FAIL(hex_to_dec_(hex_str, new_len, dec_str, dec_len))) {
|
|
LOG_WARN("failed to hex_to_dec", K(ret));
|
|
}
|
|
i = dec_len - 1;
|
|
for(; i >= 0; --i) {
|
|
char c = dec_str[i];
|
|
if (OB_FAIL(ib_.push((uint8_t)(c - '0'), reduce_zero))) {
|
|
LOG_WARN("push to integer builder fail", K(ret), K(c));
|
|
break;
|
|
}
|
|
}
|
|
} else { /* Do nothing */ }
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::build_integer_(const char *str, const int64_t integer_start,
|
|
const int64_t integer_end, const bool reduce_zero,
|
|
ObNumberFmtModel * fmt)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(fmt)) {
|
|
ret = build_integer_(str, integer_start, integer_end, reduce_zero);
|
|
} else {
|
|
ib_.reset();
|
|
int64_t skiped_zero_counter = 0;
|
|
if (OB_UNLIKELY(integer_start <= integer_end && NULL == str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else if (integer_start >= 0 && integer_end >= 0) {
|
|
int64_t c_p = fmt->fmt_len_ - 1;
|
|
char* fmt_str = fmt->fmt_str_;
|
|
bool got_comma_in_fmt = false;
|
|
bool pass_first_comma = false;
|
|
if (fmt->dc_position_ >= 0) {
|
|
c_p = fmt->dc_position_ - 1;
|
|
}
|
|
//comma: 4 32 1 4 32
|
|
//to_number('76,669,,83,5', '999,999,,999')=>76669835
|
|
//digit: 81 654 32 1 987 654 321
|
|
//check from right to left, before comma appears at fmt str for the first time, all commas in number_str can be ignored
|
|
//after the first appearance, commas in number_str must match with the fmt str
|
|
//so as the upper case, comma 1 can be ignored, comma 2\3\4 are match with fmt str
|
|
int64_t i = integer_end;
|
|
for (; i >= integer_start && c_p >= 0; --i) {
|
|
char c = str[i];
|
|
if ('0' == c) {
|
|
++skiped_zero_counter;
|
|
--c_p;
|
|
if (got_comma_in_fmt && !pass_first_comma) {
|
|
pass_first_comma = true;
|
|
}
|
|
continue;
|
|
} else if (',' == c) {
|
|
if (pass_first_comma && ',' != fmt_str[c_p]) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LOG_WARN("comma(s) not match", K(ret), K(c_p), K(i), K(fmt_str[c_p]));
|
|
break;
|
|
} else if (',' == fmt_str[c_p]) {
|
|
got_comma_in_fmt = true;
|
|
--c_p;
|
|
}
|
|
// LOG_DEBUG("ignore comma", K(ret), K(c_p), K(i), K(fmt_str[c_p]));
|
|
continue;
|
|
} else if (',' == fmt_str[c_p]) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LOG_WARN("got error during build integer", K(ret), K(i), K(fmt_str[c_p]), K(c_p));
|
|
break;
|
|
} else {
|
|
if (got_comma_in_fmt && !pass_first_comma) {
|
|
pass_first_comma = true;
|
|
}
|
|
for (int64_t j = 0; j < skiped_zero_counter; ++j) {
|
|
if (OB_FAIL(ib_.push(0, reduce_zero))) {
|
|
LOG_WARN("push to integer builder fail", K(ret), K(j));
|
|
break;
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
break;
|
|
}
|
|
skiped_zero_counter = 0;
|
|
--c_p;
|
|
}
|
|
if (OB_FAIL(ib_.push((uint8_t)(c - '0'), reduce_zero))) {
|
|
LOG_WARN("push to integer builder fail", K(ret), K(c));
|
|
break;
|
|
}
|
|
}
|
|
/* fmt 0: there must be a digit that matches with the 0 */
|
|
if (c_p >= 0) {
|
|
while(c_p >= 0) {
|
|
if (fmt_str[c_p] == '0') {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LOG_WARN("there has no digit that matches the 0(s) in the fmt str",
|
|
K(ret), K(i), K(c_p));
|
|
break;
|
|
}
|
|
c_p--;
|
|
}
|
|
}
|
|
if (i >= integer_start) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LOG_WARN("integer part is longer than fmt str", K(ret), K(i), K(c_p));
|
|
}
|
|
} else { /* Do nothing */ }
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::build_integer_v2_(const char *str, const int64_t integer_start,
|
|
const int64_t integer_end, const bool reduce_zero, const bool integer_zero,
|
|
int &warning, ObNumberFmtModel * fmt)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(fmt)) {
|
|
ret = build_integer_v2_(str, integer_start, integer_end, reduce_zero, integer_zero, warning);
|
|
} else if (fmt->has_x_) {
|
|
ret = build_hex_integer_(str, integer_start, integer_end, reduce_zero, fmt);
|
|
} else {
|
|
ret = build_integer_(str, integer_start, integer_end, reduce_zero, fmt);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::build_decimal_(const char *str, const int64_t length,
|
|
const int64_t decimal_start, const bool reduce_zero)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
db_.reset();
|
|
int64_t skiped_zero_counter = 0;
|
|
if (decimal_start < length && OB_ISNULL(str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else {
|
|
for (int64_t i = decimal_start; i < length; ++i) {
|
|
char c = str[i];
|
|
if ('0' == c) {
|
|
++skiped_zero_counter;
|
|
continue;
|
|
} else {
|
|
for (int64_t j = 0; j < skiped_zero_counter; ++j) {
|
|
if (OB_FAIL(db_.push(0, reduce_zero))) {
|
|
LOG_WARN("push to decimal builder fail", K(ret), K(j));
|
|
break;
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
break;
|
|
}
|
|
skiped_zero_counter = 0;
|
|
}
|
|
if (OB_FAIL(db_.push((uint8_t)(c - '0'), reduce_zero))) {
|
|
LOG_WARN("push to decimal builder fail", K(ret), K(c));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ObNumberBuilder::build_decimal_v2_(const char *str, const int64_t length,
|
|
const int64_t decimal_start, const bool reduce_zero, const bool decimal_zero, int &warning)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
static const uint32_t POWS[ObNumber::DIGIT_LEN] = {100000000, 10000000, 1000000,
|
|
100000, 10000, 1000, 100, 10, 1};
|
|
if (decimal_zero) {
|
|
db_.reset();
|
|
} else if (OB_UNLIKELY(decimal_start <= length) && OB_ISNULL(str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null", K(decimal_start), K(length), K(ret));
|
|
} else {
|
|
uint32_t tmp = 0;
|
|
int64_t idx = 0;
|
|
int64_t decimal_end = length - 1;
|
|
while (decimal_end >= decimal_start && str[decimal_end] == '0') {
|
|
--decimal_end;
|
|
}
|
|
for (int64_t i = decimal_start; OB_SUCC(ret) && i <= decimal_end; ++i) {
|
|
int32_t tmp_value = str[i] - '0';
|
|
if (OB_UNLIKELY(tmp_value < 0) || OB_UNLIKELY(tmp_value > 9)) {
|
|
warning = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error: got ", K(ret), K(warning), K(str[i]), K(tmp_value));
|
|
} else {
|
|
tmp += static_cast<uint32_t>(POWS[idx++] * tmp_value);
|
|
if (idx >= ObNumber::DIGIT_LEN || i == decimal_end) {
|
|
if (OB_FAIL(db_.push_digit(tmp, reduce_zero))) {
|
|
LOG_WARN("push to decimal builder fail", K(ret), K(i));
|
|
break;
|
|
}
|
|
tmp = 0;
|
|
idx = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::find_point_(
|
|
const char *str,
|
|
int64_t &length,
|
|
int64_t &integer_start,
|
|
int64_t &integer_end,
|
|
int64_t &decimal_start,
|
|
bool &negative,
|
|
bool &integer_zero,
|
|
bool &decimal_zero,
|
|
int &warning)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t i_integer_start = -2;
|
|
int64_t dot_idx = -1;
|
|
bool b_negative = false;
|
|
bool b_integer_zero = true;
|
|
bool b_decimal_zero = true;
|
|
bool sign_appeared = false;
|
|
bool digit_appeared = false;
|
|
bool dot_appeared = false;
|
|
int64_t i = 0;
|
|
if (OB_ISNULL(str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the str pointer is null", K(ret));
|
|
} else {
|
|
for (i = 0; i < length && isspace(str[i]); ++i);
|
|
for (; i + 1 < length && isspace(str[length - 1]); --length);
|
|
if (OB_UNLIKELY(i == length)) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
} else {
|
|
for (; OB_SUCCESS == warning && i < length; ++i) {
|
|
char c = str[i];
|
|
switch (c) {
|
|
case '-':
|
|
case '+':
|
|
if (sign_appeared || digit_appeared || dot_appeared) {
|
|
warning = OB_INVALID_NUMERIC;
|
|
// LOG_DEBUG("invalid numeric", K(sign_appeared), K(digit_appeared), K(dot_appeared));
|
|
} else {
|
|
if ('-' == c) {
|
|
b_negative = true;
|
|
}
|
|
sign_appeared = true;
|
|
}
|
|
break;
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
if (!dot_appeared) {
|
|
b_integer_zero = false;
|
|
} else {
|
|
b_decimal_zero = false;
|
|
}
|
|
/* no break. */
|
|
case '0':
|
|
if (-2 == i_integer_start) {
|
|
i_integer_start = i;
|
|
}
|
|
digit_appeared = true;
|
|
break;
|
|
case '.':
|
|
if (dot_appeared) {
|
|
warning = OB_INVALID_NUMERIC;
|
|
// LOG_DEBUG("invalid numeric", K(dot_appeared));
|
|
} else {
|
|
if (!digit_appeared) {
|
|
//".95" means "0.95"
|
|
// i_integer_start and i_integer_end both will be -1
|
|
i_integer_start = -1;
|
|
}
|
|
dot_appeared = true;
|
|
dot_idx = i;
|
|
}
|
|
break;
|
|
default:
|
|
warning = OB_INVALID_NUMERIC;
|
|
// _LOG_DEBUG("invalid numeric default, c=%x", c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_SUCCESS != warning) {
|
|
length = i - 1;
|
|
}
|
|
if (!dot_appeared) {
|
|
dot_idx = length;
|
|
}
|
|
negative = b_negative;
|
|
integer_zero = b_integer_zero;
|
|
decimal_zero = b_decimal_zero;
|
|
integer_start = i_integer_start;
|
|
integer_end = dot_idx - 1;
|
|
decimal_start = dot_idx + 1;
|
|
ObString tmp_str(length, str);
|
|
LOG_DEBUG("find v1", K(tmp_str), K(negative), K(integer_zero),
|
|
K(decimal_zero), K(integer_start), K(dot_idx), K(i), K(length));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::find_point_v2_(
|
|
const char *str,
|
|
int64_t &length,
|
|
int64_t &integer_start,
|
|
int64_t &integer_end,
|
|
int64_t &decimal_start,
|
|
bool &negative,
|
|
bool &integer_zero,
|
|
bool &decimal_zero,
|
|
int &warning)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t i_integer_start = -2;
|
|
int64_t dot_idx = -1;
|
|
bool b_negative = false;
|
|
bool b_integer_zero = true;
|
|
bool b_decimal_zero = true;
|
|
int64_t i = 0;
|
|
if (OB_ISNULL(str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the str pointer is null", K(ret));
|
|
} else {
|
|
for (i = 0; i < length && isspace(str[i]); ++i);
|
|
for (; i + 1 < length && isspace(str[length - 1]); --length);
|
|
if (OB_UNLIKELY(i == length)) {
|
|
if (0 == i) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
} else {
|
|
//empty string is enabled
|
|
warning = OB_INVALID_NUMERIC;
|
|
}
|
|
} else {
|
|
if ('-' == str[i]) {
|
|
b_negative = true;
|
|
++i;
|
|
} else if ('+' == str[i]) {
|
|
++i;
|
|
}
|
|
if (i == length) {
|
|
dot_idx = length;
|
|
} else {
|
|
const char *dot_pos = static_cast<const char *>(memchr(str + i, '.', length - i));
|
|
if (NULL == dot_pos) {
|
|
dot_idx = length;
|
|
i_integer_start = i;
|
|
b_integer_zero = false;
|
|
b_decimal_zero = true;
|
|
} else {
|
|
dot_idx = dot_pos - str;
|
|
i_integer_start = (i == dot_idx) ? -1 : i;
|
|
//todo can be used
|
|
b_integer_zero = ((dot_idx == i
|
|
|| ((dot_idx - i) == static_cast<int64_t>(strspn(str + i, "0"))))
|
|
? true
|
|
: false);
|
|
b_decimal_zero = (((dot_idx == length - 1)
|
|
|| ((length - 1 - dot_idx) == static_cast<int64_t>(strspn(str + dot_idx + 1, "0"))))
|
|
? true
|
|
: false);
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
negative = b_negative;
|
|
integer_zero = b_integer_zero;
|
|
decimal_zero = b_decimal_zero;
|
|
integer_start = i_integer_start;
|
|
integer_end = dot_idx - 1;
|
|
decimal_start = dot_idx + 1;
|
|
// ObString tmp_str(length, str);
|
|
// LOG_DEBUG("find v2", K(tmp_str), K(negative), K(integer_zero),
|
|
// K(decimal_zero), K(integer_start), K(dot_idx), K(i), K(length));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObNumberBuilder::find_point_(
|
|
const char *str,
|
|
ObNumberFmtModel *fmt,
|
|
char* new_str,
|
|
int64_t &length,
|
|
int64_t &integer_start,
|
|
int64_t &integer_end,
|
|
int64_t &decimal_start,
|
|
bool &negative,
|
|
bool &integer_zero,
|
|
bool &decimal_zero,
|
|
int64_t &comma_cnt)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t i_integer_start = -2;
|
|
int64_t dot_idx = -1;
|
|
bool b_negative = false;
|
|
bool b_integer_zero = true;
|
|
bool b_decimal_zero = true;
|
|
bool sign_appeared = false;
|
|
bool digit_appeared = false;
|
|
bool dot_appeared = false;
|
|
bool dollar_appeared = false;
|
|
int64_t heading_space = 0;
|
|
int64_t trailing_space = 0;
|
|
int64_t i = 0;
|
|
int64_t new_len = 0;
|
|
int64_t b_comma_cnt = 0;
|
|
if (OB_ISNULL(str)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "the str pointer is null", K(ret));
|
|
} else {
|
|
for (i = 0; i < length && isspace(str[i]); ++i);
|
|
heading_space = i;
|
|
for (; i + 1 < length && isspace(str[length - 1]); --length, ++trailing_space);
|
|
|
|
if (OB_UNLIKELY(i == length)) {
|
|
//to_number(' ', '9B9.9') => 0
|
|
if (!fmt->has_b_ || fmt->has_currency_ || fmt->has_sign_
|
|
|| heading_space + trailing_space != fmt->fmt_len_ + 1) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error", K(ret), K(fmt->has_b_), K(heading_space), K(trailing_space), K(fmt->fmt_len_));
|
|
} else {
|
|
new_str[0] = '0';
|
|
digit_appeared = true;
|
|
dot_idx = 0;
|
|
length = 1;
|
|
i = 1;
|
|
LIB_LOG(WARN, "ObNumber format:cast spaces to zero", K(ret));
|
|
}
|
|
} else {
|
|
for (; OB_SUCCESS == ret && i < length; ++i) {
|
|
char c = str[i];
|
|
switch (c) {
|
|
case '-':
|
|
case '+':
|
|
if (sign_appeared
|
|
|| ((!fmt->has_sign_ || fmt->sign_position_ == FmtFirst) && (digit_appeared || dot_appeared))
|
|
|| (fmt->sign_position_ == FmtLast && (i != (length - 1)))) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error", K(ret), K(fmt->sign_position_),
|
|
K(digit_appeared), K(dot_appeared), K(i));
|
|
} else {
|
|
if ('-' == c) {
|
|
b_negative = true;
|
|
} else if (!fmt->has_sign_) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error:no sign in fmt str, but got '+'", K(ret));
|
|
}
|
|
sign_appeared = true;
|
|
}
|
|
break;
|
|
case 'a'...'f':
|
|
case 'A'...'F':
|
|
if (!fmt->has_x_) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error: got ", K(ret), K(c));
|
|
}
|
|
/* no break. */
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
if (!dot_appeared) {
|
|
b_integer_zero = false;
|
|
} else {
|
|
b_decimal_zero = false;
|
|
}
|
|
/* no break. */
|
|
case '0':
|
|
if (-2 == i_integer_start) {
|
|
i_integer_start = new_len;
|
|
}
|
|
digit_appeared = true;
|
|
new_str[new_len++] = c;
|
|
break;
|
|
case '.':
|
|
if (dot_appeared) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error:dot appeared more than one time");
|
|
} else {
|
|
if (!digit_appeared) {
|
|
//".95" means "0.95"
|
|
// i_integer_start and i_integer_end both will be -1
|
|
i_integer_start = -1;
|
|
}
|
|
dot_appeared = true;
|
|
dot_idx = new_len;
|
|
new_str[new_len++] = c;
|
|
}
|
|
break;
|
|
case ',':
|
|
if (dot_appeared) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error:comma appears at decimal part");
|
|
} else if (fmt->has_comma_) {
|
|
new_str[new_len++] = ',';
|
|
++b_comma_cnt;
|
|
} else {
|
|
//ignore comma
|
|
}
|
|
break;
|
|
case '$':
|
|
if (dollar_appeared || dot_appeared || digit_appeared) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error:$ can only be the first valid character");
|
|
} else {
|
|
dollar_appeared = true;
|
|
}
|
|
break;
|
|
default:
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got format error: got ", K(ret), K(c));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (!digit_appeared && (dollar_appeared || sign_appeared)) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got error according to fmt ", K(ret), K(digit_appeared),
|
|
K(dollar_appeared));
|
|
} else if ((fmt->has_sign_ && !sign_appeared)
|
|
|| (fmt->has_currency_ && !dollar_appeared)) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got error according to fmt", K(ret), K(fmt->has_sign_),
|
|
K(sign_appeared), K(fmt->has_currency_), K(dollar_appeared));
|
|
} else if (fmt->has_x_ && (b_negative || dot_appeared)) {
|
|
ret = OB_INVALID_NUMERIC;
|
|
LIB_LOG(WARN, "ObNumber got error according to fmt ", K(ret), K(dot_appeared),
|
|
K(fmt->has_x_));
|
|
} else {
|
|
if (!dot_appeared) {
|
|
dot_idx = new_len;
|
|
}
|
|
negative = b_negative;
|
|
integer_zero = b_integer_zero;
|
|
decimal_zero = b_decimal_zero;
|
|
integer_start = i_integer_start;
|
|
integer_end = dot_idx - 1;
|
|
decimal_start = dot_idx + 1;
|
|
comma_cnt = b_comma_cnt;
|
|
//to_number('12.34', '9.99') => error
|
|
//to_number('1.32', '9999.9') => error
|
|
//to_number('1.32', '9999.999') => 1.32
|
|
if (((fmt->dc_position_ > 0) && ((integer_end - integer_start + 1 - comma_cnt) > fmt->dc_position_))
|
|
|| (fmt->has_x_ && new_len > fmt->fmt_len_)
|
|
|| (dot_appeared && ((new_len - decimal_start) >
|
|
(fmt->dc_position_ > 0 ? (fmt->fmt_len_ - fmt->dc_position_ - 1) : 0)))) {
|
|
LIB_LOG(WARN, "ObNumber got error according to fmt", K(integer_end), K(integer_start),
|
|
K(fmt->dc_position_), K(dot_appeared), K(new_len), K(decimal_start),
|
|
K(fmt->fmt_len_), K(fmt->dc_position_), K(comma_cnt));
|
|
ret = OB_INVALID_NUMERIC;
|
|
}
|
|
LIB_LOG(DEBUG, "ObNumber process fmt success", KCSTRING(new_str), K(length), K(new_len),
|
|
K(dot_appeared));
|
|
length = new_len;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int64_t ObNumberBuilder::get_exp() const
|
|
{
|
|
return number_.d_.exp_;
|
|
}
|
|
|
|
int64_t ObNumberBuilder::get_length() const
|
|
{
|
|
return number_.d_.len_;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int ObDigitIterator::get_next_digit(uint32_t &digit, bool &from_integer, bool &last_decimal)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (0 <= iter_exp_) {
|
|
from_integer = true;
|
|
if (iter_idx_ < iter_len_) {
|
|
ret = get_digit(iter_idx_++, digit);
|
|
} else {
|
|
digit = 0;
|
|
}
|
|
--iter_exp_;
|
|
} else {
|
|
from_integer = false;
|
|
if (-1 == iter_exp_) {
|
|
if (iter_idx_ >= iter_len_) {
|
|
ret = OB_ITER_END;
|
|
} else {
|
|
ret = get_digit(iter_idx_++, digit);
|
|
last_decimal = (iter_idx_ == iter_len_);
|
|
}
|
|
} else {
|
|
digit = 0;
|
|
++iter_exp_;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ObDigitIterator::NextDigitEnum ObDigitIterator::get_next_digit(uint32_t &digit)
|
|
{
|
|
NextDigitEnum nd_enum = ND_END;
|
|
if (0 <= iter_exp_) {
|
|
nd_enum = (0 == iter_idx_ ? ND_HEAD_INTEGER : ND_BODY_INTEGER);
|
|
if (iter_idx_ < iter_len_) {
|
|
digit = number_.get_digits()[iter_idx_++];
|
|
} else {
|
|
digit = 0;
|
|
}
|
|
--iter_exp_;
|
|
} else {
|
|
if (-1 == iter_exp_) {
|
|
if (iter_idx_ < iter_len_) {
|
|
digit = number_.get_digits()[iter_idx_++];
|
|
nd_enum = (iter_idx_ == iter_len_ ? ND_TAIL_DECIMAL : ND_BODY_DECIMAL);
|
|
} else {
|
|
nd_enum = ND_END;
|
|
}
|
|
} else {
|
|
digit = 0;
|
|
++iter_exp_;
|
|
nd_enum = ND_BODY_DECIMAL;
|
|
}
|
|
}
|
|
return nd_enum;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ObCalcVector::ObCalcVector() : base_(ObNumber::BASE),
|
|
length_(0),
|
|
digits_(buffer_)
|
|
{
|
|
}
|
|
|
|
ObCalcVector::~ObCalcVector()
|
|
{
|
|
}
|
|
|
|
ObCalcVector::ObCalcVector(const ObCalcVector &other)
|
|
{
|
|
LIB_LOG(DEBUG, "copy assignment invoked");
|
|
*this = other;
|
|
}
|
|
|
|
ObCalcVector &ObCalcVector::operator =(const ObCalcVector &other)
|
|
{
|
|
LIB_LOG(DEBUG, "operator = invoked");
|
|
if (this != &other) {
|
|
base_ = other.base_;
|
|
length_ = other.length_;
|
|
digits_ = other.digits_;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
int ObCalcVector::init(const uint32_t desc, uint32_t *digits)
|
|
{
|
|
ObDigitIterator di;
|
|
int ret = OB_SUCCESS;
|
|
di.assign(desc, digits);
|
|
bool head_zero = true;
|
|
uint32_t digit = 0;
|
|
bool from_integer = false;
|
|
bool last_decimal = false;
|
|
length_ = 0;
|
|
digits_ = buffer_;
|
|
while (OB_SUCCESS == (ret = di.get_next_digit(digit, from_integer, last_decimal))) {
|
|
if (head_zero) {
|
|
if (0 == digit) {
|
|
continue;
|
|
} else {
|
|
head_zero = false;
|
|
}
|
|
}
|
|
digits_[length_ ++] = digit;
|
|
}
|
|
if (OB_ITER_END == ret) {
|
|
ret = OB_SUCCESS;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
uint64_t ObCalcVector::at(const int64_t idx) const
|
|
{
|
|
uint64_t ret_digit = 0;
|
|
if (OB_ISNULL(digits_)) {
|
|
LOG_ERROR_RET(OB_ERROR, "the pointer is null");
|
|
} else if (OB_UNLIKELY(idx <0 || idx > length_)) {
|
|
LOG_ERROR_RET(OB_INVALID_ARGUMENT, "the param is invalid");
|
|
} else {
|
|
ret_digit = digits_[idx];
|
|
}
|
|
return ret_digit;
|
|
}
|
|
|
|
uint64_t ObCalcVector::base() const
|
|
{
|
|
return base_;
|
|
}
|
|
|
|
void ObCalcVector::set_base(const uint64_t base)
|
|
{
|
|
base_ = base;
|
|
}
|
|
|
|
int64_t ObCalcVector::size() const
|
|
{
|
|
return length_;
|
|
}
|
|
|
|
uint32_t *ObCalcVector::get_digits()
|
|
{
|
|
return digits_;
|
|
}
|
|
|
|
int ObCalcVector::normalize()
|
|
{
|
|
int64_t i = 0;
|
|
int ret = OB_SUCCESS;
|
|
if (length_ > 0 && OB_ISNULL(digits_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else {
|
|
for (; i < length_; ++i) {
|
|
if (0 != digits_[i]) {
|
|
break;
|
|
}
|
|
}
|
|
length_ = length_ - i;
|
|
if (0 == length_) {
|
|
digits_ = NULL;
|
|
} else {
|
|
digits_ = &digits_[i];
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObCalcVector::set(const int64_t idx, const uint64_t digit)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_UNLIKELY(0 > idx
|
|
|| idx >= length_
|
|
|| base_ <= digit
|
|
|| NULL == digits_)) {
|
|
LOG_ERROR("invalid param ",
|
|
K(idx), K(length_), K(digit), K(base_));
|
|
ret = OB_INVALID_ARGUMENT;
|
|
} else {
|
|
digits_[idx] = (uint32_t)digit;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObCalcVector::ensure(const int64_t size)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(buffer_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else if (OB_UNLIKELY(ObNumber::MAX_CALC_LEN < size)) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
} else if (OB_UNLIKELY(digits_ < &buffer_[0]
|
|
|| digits_ > &buffer_[ObNumber::MAX_CALC_LEN - 1])) {
|
|
LOG_ERROR("digits is read only ", K(digits_), K(buffer_));
|
|
ret = OB_ERR_READ_ONLY;
|
|
} else {
|
|
length_ = size;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObCalcVector::resize(const int64_t size)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(buffer_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR("the pointer is null");
|
|
} else if (OB_UNLIKELY(ObNumber::MAX_CALC_LEN < size)) {
|
|
ret = OB_NUMERIC_OVERFLOW;
|
|
} else if (OB_UNLIKELY(digits_ < &buffer_[0]
|
|
|| digits_ > &buffer_[ObNumber::MAX_CALC_LEN - 1])) {
|
|
LOG_ERROR("digits is read only", K(digits_), K(buffer_));
|
|
ret = OB_ERR_READ_ONLY;
|
|
} else {
|
|
MEMSET(digits_, 0, size * ITEM_SIZE(digits_));
|
|
length_ = size;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ObCalcVector ObCalcVector::ref(const int64_t start, const int64_t end) const
|
|
{
|
|
ObCalcVector ret_calc_vec;
|
|
if (OB_ISNULL(digits_)) {
|
|
LOG_ERROR_RET(OB_ERROR, "the pinter is null");
|
|
} else {
|
|
ret_calc_vec.length_ = end - start + 1;
|
|
ret_calc_vec.digits_ = &digits_[start];
|
|
ret_calc_vec.set_base(this->base());
|
|
}
|
|
|
|
return ret_calc_vec;
|
|
}
|
|
|
|
int ObCalcVector::assign(const ObCalcVector &other, const int64_t start, const int64_t end)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (0 < (end - start + 1)) {
|
|
if (OB_ISNULL(digits_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_ERROR( "the pointer is null");
|
|
} else {
|
|
MEMCPY(&digits_[start], other.digits_, (end - start + 1) * ITEM_SIZE(digits_));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int64_t ObCalcVector::to_string(char *buffer, const int64_t length) const
|
|
{
|
|
int64_t pos = 0;
|
|
if ((length_ > 0 && OB_ISNULL(digits_)) || length_ < 0) {
|
|
databuff_printf(buffer, length, pos, "the value is invalid");
|
|
} else {
|
|
databuff_printf(buffer, length, pos, "\"{length=%ld digits_ptr=%p buffer_ptr=%p digits=[",
|
|
length_, digits_, buffer_);
|
|
for (int64_t i = 0; i < length_; ++i) {
|
|
databuff_printf(buffer, length, pos, "%u,", digits_[i]);
|
|
}
|
|
databuff_printf(buffer, length, pos, "]}\"");
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
}
|
|
}
|
|
}
|