// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #include "runtime/decimal_value.h" #include #include #include namespace doris { const char* DecimalValue::_s_llvm_class_name = "class.doris::DecimalValue"; // set the 1st param if the second param is smaller. template inline void set_if_smaller(T* num1_ptr, const T num2) { if (*num1_ptr > num2) { *num1_ptr = num2; } } // set the 1st param if the second param is smaller. template inline void set_if_bigger(T* num1_ptr, const T num2) { if (*num1_ptr < num2) { *num1_ptr = num2; } } // util function: check if there is error and fix it. inline void fix_intg_frac_error( const int32_t len, int32_t* int_len, int32_t* frac_len, int32_t* error) { if (*int_len + *frac_len > len) { if (*int_len > len) { *int_len = len; *frac_len = 0; *error = E_DEC_OVERFLOW; } else { *frac_len = len - *int_len; *error = E_DEC_TRUNCATED; } } else { *error = E_DEC_OK; } } // Note: the carry <= 1, so for the sum of three number(value1, value2, *carry), // the maximum value of carry is 1. inline void add(const int32_t value1, const int32_t value2, int32_t* to, int32_t* carry) { int32_t sum = value1 + value2 + *carry; *carry = (sum >= DIG_BASE) ? 1 : 0; if (*carry) { sum -= DIG_BASE; } *to = sum; } // to = value1 - value2 inline void sub(const int32_t value1, const int32_t value2, int32_t* to, int32_t* carry) { int32_t a = value1 - value2 - *carry ; *carry = (a < 0) ? 1 : 0; if (*carry) { a += DIG_BASE; } *to = a; } // Note: the input carry may > 1, after the summation process of three number (value1, value2, *carry), // the maximum value of carry may be 2, when sum() >= 2 * DIG_BASE. inline void add2(const int32_t value1, const int32_t value2, int32_t* to, int32_t* carry) { // NOTE: When three int32_t integers (the maximum value of each number is 10 ^ 9 - 1) are added, // because the maximum value of int32_t is 2147483647, the result may overflow, so it is // necessary to convert int32_t to int64_t. int64_t sum = (int64_t) value1 + value2 + *carry; *carry = (sum >= DIG_BASE) ? 1 : 0; if (*carry) { sum -= DIG_BASE; } if (sum >= DIG_BASE) { sum -= DIG_BASE; ++(*carry); } // the value of sum must small than DIG_BASE here *to = (int32_t) sum; } // to = value1 - value2 ƒ inline void sub2(const int32_t value1, const int32_t value2, int32_t* to, int32_t* carry) { int32_t a = value1 - value2 - *carry ; *carry = (a < 0) ? 1 : 0; if (*carry) { a += DIG_BASE; } if (a < 0) { a += DIG_BASE; ++(*carry); } *to = a; } int32_t do_add( const DecimalValue& value1, const DecimalValue& value2, DecimalValue* to) { int32_t intg1 = round_up(value1._int_length); int32_t intg2 = round_up(value2._int_length); int32_t frac1 = round_up(value1._frac_length); int32_t frac2 = round_up(value2._frac_length); int32_t frac0 = std::max(frac1, frac2); int32_t intg0 = std::max(intg1, intg2); // Is there a need for extra word because of carry? int32_t first_big_digit_sum = intg1 > intg2 ? value1._buffer[0] : intg2 > intg1 ? value2._buffer[0] : value1._buffer[0] + value2._buffer[0]; if (first_big_digit_sum > DIG_MAX - 1) { // yes, there is ++intg0; to->_buffer[0] = 0; // for safety } to->_sign = value1._sign; int32_t error = E_DEC_OK; fix_intg_frac_error(to->_buffer_length, &intg0, &frac0, &error); if (error == E_DEC_OVERFLOW) { to->to_max_decimal(to->_buffer_length * DIG_PER_DEC1, 0); return error; } int32_t *buf0 = to->_buffer + intg0 + frac0; to->_int_length = intg0 * DIG_PER_DEC1; to->_frac_length = std::max(value1._frac_length, value2._frac_length); if (error) { // E_DEC_TRUNCATED int32_t to_frac_length = to->_frac_length; //ATTN: _int_lenggh is bit-field struct member, can not take address directly. set_if_smaller(&to_frac_length, frac0 * DIG_PER_DEC1); to->_frac_length = to_frac_length; set_if_smaller(&frac1, frac0); set_if_smaller(&frac2, frac0); set_if_smaller(&intg1, intg0); set_if_smaller(&intg2, intg0); } // part 1 - max(frac) ... min (frac) const int32_t *buf1 = nullptr; const int32_t *buf2 = nullptr; const int32_t *stop = nullptr; const int32_t *stop2 = nullptr; if (frac1 > frac2) { buf1 = value1._buffer + intg1 + frac1; stop = value1._buffer + intg1 + frac2; buf2 = value2._buffer + intg2 + frac2; stop2 = value1._buffer + ((intg1 > intg2) ? (intg1 - intg2) : 0); } else { buf1 = value2._buffer + intg2 + frac2; stop = value2._buffer + intg2 + frac1; buf2 = value1._buffer + intg1 + frac1; stop2 = value2._buffer + ((intg2 > intg1) ? (intg2 - intg1) : 0); } while (buf1 > stop) { *--buf0 = *--buf1; } // part 2 - min(frac) ... min(intg) int32_t carry = 0; while (buf1 > stop2) { add(*--buf1, *--buf2, --buf0, &carry); } // part 3 - min(intg) ... max(intg) if (intg1 > intg2) { stop = value1._buffer; buf1 = stop + intg1 - intg2; } else { stop = value2._buffer; buf1 = stop + intg2 - intg1; } while (buf1 > stop) { add(*--buf1, 0, --buf0, &carry); } if (carry) { *--buf0 = 1; } return error; } // to=value1-value2. // if to==0, return -1/0/+1 - the result of the comparison int do_sub(const DecimalValue& value1, const DecimalValue& value2, DecimalValue *to) { int32_t intg1 = round_up(value1._int_length); int32_t intg2 = round_up(value2._int_length); int32_t frac1 = round_up(value1._frac_length); int32_t frac2 = round_up(value2._frac_length); int32_t frac0 = std::max(frac1, frac2); int32_t error = E_DEC_OK; int32_t carry = 0; // let carry:=1 if value2 > value1 // TODO(lingbin): add another variable 'is_bigger' to replace 'carry' to make carry only // has one meaning const int32_t* buf1 = value1._buffer; const int32_t* start1 = buf1; const int32_t* stop1 = buf1 + intg1; const int32_t* buf2 = value2._buffer; const int32_t* start2 = buf2; const int32_t* stop2 = buf2 + intg2; // ignore leading zeroes if (*buf1 == 0) { while (buf1 < stop1 && *buf1 == 0) { buf1++; } start1 = buf1; intg1 = (int32_t) (stop1 - buf1); } if (*buf2 == 0) { while (buf2 < stop2 && *buf2 == 0) { buf2++; } start2 = buf2; intg2 = (int32_t) (stop2 - buf2); } if (intg2 > intg1) { carry = 1; } else if (intg2 == intg1) { const int32_t *end1 = stop1 + (frac1 - 1); const int32_t *end2 = stop2 + (frac2 - 1); // ignore trailing zeroes while ((buf1 <= end1) && (*end1 == 0)) { end1--; } while ((buf2 <= end2) && (*end2 == 0)) { end2--; } frac1 = (int32_t) (end1 - stop1) + 1; frac2 = (int32_t) (end2 - stop2) + 1; while (buf1 <= end1 && buf2 <= end2 && *buf1 == *buf2) { buf1++; buf2++; } if (buf1 <= end1) { if (buf2 <= end2) { // not equal carry = (*buf2 > *buf1); } else { // value1 is longer, so value1 > value2 carry = 0; } } else { if (buf2 <= end2){ // value2 is longer carry = 1; } else { // short-circuit everything: value1 == value2 if (to == nullptr) {// for decimal_cmp() return 0; } to->set_to_zero(); return E_DEC_OK; } } } if (to == nullptr) { // decimal_cmp(), not equal return (carry == value1._sign) ? 1 : -1; } to->_sign = value1._sign; DecimalValue value_big = value1; DecimalValue value_small = value2; // ensure that always value1 > value2 (and intg1 >= intg2) if (carry) { std::swap(value_big, value_small); std::swap(start1, start2); std::swap(intg1, intg2); std::swap(frac1, frac2); to->_sign = 1 - to->_sign; } fix_intg_frac_error(to->_buffer_length, &intg1, &frac0, &error); int32_t* buf0 = to->_buffer + intg1 + frac0; to->_frac_length = std::max(value_big._frac_length, value_small._frac_length); to->_int_length = intg1 * DIG_PER_DEC1; if (error) { // must be E_DEC_TRUNCATE. int32_t temp_to_frac_length = to->_frac_length; set_if_smaller(&temp_to_frac_length, frac0 * DIG_PER_DEC1); to->_frac_length = temp_to_frac_length; set_if_smaller(&frac1, frac0); set_if_smaller(&frac2, frac0); set_if_smaller(&intg2, intg1); } carry = 0; // part 1 - max(frac) ... min (frac) if (frac1 > frac2) { buf1 = start1 + intg1 + frac1; stop1 = start1 + intg1 + frac2; buf2 = start2 + intg2 + frac2; while (frac0-- > frac1) { //occur when there are trailing zeroes *--buf0 = 0; } while (buf1 > stop1) { *--buf0 = *--buf1; } } else { buf1 = start1 + intg1 + frac1; buf2 = start2 + intg2 + frac2; stop2 = start2 + intg2 + frac1; while (frac0-- > frac2) { *--buf0 = 0; } while (buf2 > stop2) { sub(0, *--buf2, --buf0, &carry); } } // part 2 - min(frac) ... intg2 while (buf2 > start2) { sub(*--buf1, *--buf2, --buf0, &carry); } // part 3 - intg2 ... intg1 */ while (carry && buf1 > start1) { sub(*--buf1, 0, --buf0, &carry); } while (buf1 > start1) { *--buf0 = *--buf1; } while (buf0 > to->_buffer) { // TODO(lingbin): will not happen? *--buf0 = 0; } return error; } // multiply two decimals // @return E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW; // // NOTE: // in this implementation, with sizeof(big_digit_type)=4 we have DIG_PER_DEC1=9, // and 63-digit number will take only 7 big_digit_type words (basically a 7-digit // "base 999999999" number). Thus there's no need in fast multiplication // algorithms, 7-digit numbers can be multiplied with a naive O(n*n) // method. // XXX if this library is to be used with huge numbers of thousands of // digits, fast multiplication must be implemented. int do_mul(const DecimalValue& value1, const DecimalValue& value2, DecimalValue* to) { int32_t intg1 = round_up(value1._int_length); int32_t intg2 = round_up(value2._int_length); int32_t frac1 = round_up(value1._frac_length); int32_t frac2 = round_up(value2._frac_length); int32_t intg0 = round_up(value1._int_length + value2._int_length); int32_t frac0 = frac1 + frac2; int32_t error = E_DEC_OK; const int32_t* buf1 = value1._buffer + intg1; const int32_t* buf2 = value2._buffer + intg2; // If E_DEC_OVERFLOW, save 'ideal' values. Note that no need to calculate fraction now. int32_t temp_intg = intg0; // if E_DEC_TRUNCATE, use to int32_t temp_frac = frac0; fix_intg_frac_error(to->_buffer_length, &intg0, &frac0, &error); // bound size to->_sign = (value1._sign != value2._sign) ? true : false; to->_int_length = intg0 * DIG_PER_DEC1; to->_frac_length = value1._frac_length + value2._frac_length; // store size in digits int32_t temp_to_frac_length = to->_frac_length; //ATTN: _int_lenggh is bit-field struct member, can not take address directly. set_if_smaller(&temp_to_frac_length, NOT_FIXED_DEC); to->_frac_length = temp_to_frac_length; if (error) { int32_t temp_to_int_length = to->_int_length; set_if_smaller(&temp_to_int_length, intg0 * DIG_PER_DEC1); to->_int_length = temp_to_int_length; int32_t temp_to_frac_length = to->_frac_length; set_if_smaller(&temp_to_frac_length, frac0 * DIG_PER_DEC1); to->_frac_length = temp_to_frac_length; if (temp_intg > intg0) { // bounded integer-part, E_DEC_OVERFLOW temp_intg -= intg0; temp_frac = temp_intg >> 1; intg1 -= temp_frac; intg2 -= temp_intg - temp_frac; frac1 = frac2 = 0; // frac0 is already 0 here } else { // bounded fract part, E_DEC_TRUNCATE temp_frac -= frac0; temp_intg = temp_frac >> 1; if (frac1 <= frac2) { frac1 -= temp_intg; frac2 -= temp_frac - temp_intg; } else { frac2 -= temp_intg; frac1 -= temp_frac - temp_intg; } } } int32_t* start0 = to->_buffer + intg0 + frac0 - 1; const int32_t* start2 = buf2 + frac2 - 1; const int32_t* stop1 = buf1 - intg1; const int32_t* stop2 = buf2 - intg2; int32_t* buf0 = nullptr; int32_t carry = 0; memset(to->_buffer, 0, (intg0 + frac0) * sizeof(int32_t)); for (buf1 += frac1 - 1; buf1 >= stop1; buf1--, start0--) { carry = 0; for (buf0 = start0, buf2 = start2; buf2 >= stop2; buf2--, buf0--) { int64_t mul_result = ((int64_t) *buf1) * ((int64_t) *buf2); int32_t high = (int32_t) (mul_result / DIG_BASE); int32_t low = (int32_t) (mul_result - ((int64_t)high) * DIG_BASE); add2(*buf0, low, buf0, &carry); carry += high; } if (carry) { if (buf0 < to->_buffer) { return E_DEC_OVERFLOW; } add2(*buf0, 0, buf0, &carry); } // may carry again. for (buf0--; carry; buf0--) { if (buf0 < to->_buffer) { return E_DEC_OVERFLOW; } add2(*buf0, 0, buf0, &carry); } } // Now we have to check for '-0.000' case if (to->_sign) { int32_t *buf = to->_buffer; int32_t *end = to->_buffer + intg0 + frac0; for (;buf < end; ++buf) { if (*buf) { break; } } if (buf == end) { // We got decimal zero to->set_to_zero(); } } // remove leading zeros. buf1 = to->_buffer; int32_t d_to_move = intg0 + round_up(to->_frac_length); while ((*buf1 == 0) && (to->_int_length > DIG_PER_DEC1)) { ++buf1; to->_int_length -= DIG_PER_DEC1; d_to_move--; } if (to->_buffer < buf1) { int32_t *cur_d = to->_buffer; for (; d_to_move--; cur_d++, buf1++) { *cur_d = *buf1; } } return error; } // if N1/N2 mod==NULL; if N1%N2 to==NULL; int do_div_mod( const DecimalValue& value1, const DecimalValue& value2, DecimalValue* to, DecimalValue* mod) { int32_t frac1 = round_up(value1._frac_length) * DIG_PER_DEC1; int32_t frac2 = round_up(value2._frac_length) * DIG_PER_DEC1; int32_t prec1 = value1._int_length + frac1; int32_t prec2 = value2._int_length + frac2; int32_t error = E_DEC_OK; if (mod) { to = mod; } const int32_t* buff1 = value1._buffer; const int32_t* buff2 = value2._buffer; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" // removing all the leading zeros // process value2 int32_t first_big_digit_length = (prec2 - 1) % DIG_PER_DEC1 + 1; while (prec2 > 0 && *buff2 == 0) { prec2 -= first_big_digit_length; first_big_digit_length = DIG_PER_DEC1; ++buff2; } if (prec2 <= 0) { // short-circuit everything, value2 == 0 return E_DEC_DIV_ZERO; } first_big_digit_length = (prec2 - 1) % DIG_PER_DEC1 + 1; for (; *buff2 < powers10[--first_big_digit_length];) { --prec2; } // process value1 first_big_digit_length = (prec1 - 1) % DIG_PER_DEC1 + 1; while (prec1 > 0 && *buff1 == 0) { prec1 -= first_big_digit_length; first_big_digit_length = DIG_PER_DEC1; ++buff1; } if (prec1 <= 0) { // short-circuit everything, value1 == 0 to->set_to_zero(); return E_DEC_OK; } first_big_digit_length = (prec1 - 1) % DIG_PER_DEC1 + 1; for (; *buff1 < powers10[--first_big_digit_length];) { --prec1; } #pragma GCC diagnostic pop // 比较两个数的整形部分,得到结果的intg。 如果被除数较小,intg=0. int32_t dintg = (prec1 - frac1) - (prec2 - frac2) + (*buff1 >= *buff2); int32_t intg0 = 0; // big digit length int32_t frac0 = 0; if (dintg < 0) { dintg /= DIG_PER_DEC1; intg0 = 0; } else { intg0 = round_up(dintg); } if (mod) { // we are calculating N1 % N2. The result will have // 1) frac = max(frac1, frac2), as for subtraction // 2) intg = intg2. to->_sign = value1._sign; to->_frac_length = std::max(value1._frac_length, value2._frac_length); frac0 = 0; } else { // we are calculating N1/N2. // N1 is in the buff1, has prec1 digits; N2 is in the buff2, has prec2 digits. // Scales are frac1 and frac2 accordingly. // Thus, the result will have // 1) frac = round_up(frac1 + frac2) // 2) intg = (prec1 - frac1) - (prec2 - frac2) + 1 // 3) prec = intg + frac frac0 = round_up(frac1 + frac2); if (frac0 == 0) { frac0 = 1; } fix_intg_frac_error(to->_buffer_length, &intg0, &frac0, &error); to->_sign = (value1._sign != value2._sign); to->_int_length = intg0 * DIG_PER_DEC1; to->_frac_length = frac0 * DIG_PER_DEC1; } int32_t* buff0 = to->_buffer; int32_t* stop0 = buff0 + intg0 + frac0; int32_t div_mod = !(mod); // true when do div, false when do mod. if (div_mod) { // do div while (dintg++ < 0 && buff0 < &(to->_buffer[to->_buffer_length])) { *buff0++ = 0; } } int32_t i = round_up(prec1); int32_t len1 = i + round_up(2 * frac2 + 1) + 1; set_if_bigger(&len1, 3); int32_t* tmp1 = new (std::nothrow) int32_t[len1 * sizeof(int32_t)]; if (tmp1 == nullptr) { return E_DEC_OOM; } memcpy(tmp1, buff1, i * sizeof(int32_t)); memset(tmp1 + i, 0, (len1 - i) * sizeof(int32_t)); int32_t* start1 = tmp1; int32_t* stop1 = start1 + len1; const int32_t* start2 = buff2; const int32_t* stop2 = buff2 + round_up(prec2) - 1; // removing end zeroes while (*stop2 == 0 && stop2 >= start2) { --stop2; } int32_t len2 = (int32_t)((stop2++) - start2); // calculating norm2 (normalized *start2) - we need *start2 to be large // (at least > DIG_BASE/2), but unlike Knuth's Alg. D we don't want to // normalize input numbers (as we don't make a copy of the divisor). // Thus we normalize first big_digit_type of buf2 only, and we'll normalize *start1 // on the fly for the purpose of guesstimation only. // It's also faster, as we're saving on normalization of buf2 int64_t norm_factor = DIG_BASE / (*start2 + 1); int32_t norm2 = (int32_t)(norm_factor * start2[0]); if (len2 > 0) { norm2 += (int32_t)(norm_factor * start2[1] / DIG_BASE); } int32_t dcarry; if (*start1 < *start2) { dcarry = *start1++; } else { dcarry = 0; } int64_t guess = 0; // main loop for (; buff0 < stop0; buff0++) { // short-circuit, if possible if (dcarry == 0 && *start1 < *start2) { guess = 0; } else { // D3: make a guess int64_t x = start1[0] + ((int64_t)dcarry) * DIG_BASE; int64_t y = start1[1]; guess = (norm_factor * x + norm_factor * y / DIG_BASE) / norm2; if (guess >= DIG_BASE) { guess = DIG_BASE - 1; } if (len2 > 0) { // hmm, this is a suspicious trick - I removed normalization here if (start2[1] * guess > (x - guess * start2[0]) * DIG_BASE + y) { guess--; } if (start2[1] * guess > (x - guess * start2[0]) * DIG_BASE + y){ guess--; } } // D4: multiply and subtract buff2 = stop2; int32_t* temp_prt = start1 + len2; int32_t carry = 0; for (; buff2 > start2; --temp_prt) { int32_t high; int32_t low; x = guess * (*--buff2); high = (int32_t)(x / DIG_BASE); low = (int32_t)(x - ((int64_t)high) * DIG_BASE); sub2(*temp_prt, low, temp_prt, &carry); carry += high; } carry = dcarry < carry; // D5: check the remainder if (carry) { // D6: correct the guess guess--; buff2 = stop2; temp_prt = start1 + len2; for (carry = 0; buff2 > start2; temp_prt--) { add(*temp_prt, *--buff2, temp_prt, &carry); } } } if (div_mod) { *buff0 = (int32_t)guess; } dcarry = *start1; ++start1; } do{ if (mod) { // now the result is in tmp1, it has // intg=prec1-frac1 // frac=max(frac1, frac2)=to->frac if (dcarry) { *--start1 = dcarry; } buff0 = to->_buffer; intg0 = (int) (round_up(prec1 - frac1) - (start1 - tmp1)); frac0 = round_up(to->_frac_length); error = E_DEC_OK; if (frac0 == 0 && intg0 == 0) { to->set_to_zero(); break; } if (intg0 <= 0) { if (-intg0 >= to->_buffer_length) { error = E_DEC_TRUNCATED; to->set_to_zero(); break; } stop1 = start1 + frac0 + intg0; frac0 += intg0; to->_int_length = 0; while (intg0++ < 0) { *buff0++ = 0; } } else { if (intg0 > to->_buffer_length) { frac0 = 0; intg0 = to->_buffer_length; error = E_DEC_OVERFLOW; break; } stop1 = start1 + frac0 + intg0; to->_int_length = std::min(intg0 * DIG_PER_DEC1, (int) value2._int_length); } if (intg0 + frac0 > to->_buffer_length) { stop1 -= frac0 + intg0 - to->_buffer_length; frac0 = to->_buffer_length - intg0; to->_frac_length = frac0 * DIG_PER_DEC1; error = E_DEC_TRUNCATED; } while (start1 < stop1) { *buff0++ = *start1++; } } }while(0); delete[] tmp1; int32_t to_int_length = 0; const int32_t* first_no_zero = to->get_first_no_zero_index(&to_int_length); to->_int_length = to_int_length; if (to->_buffer != first_no_zero) { memmove(to->_buffer, first_no_zero, (round_up(to->_int_length) + round_up(to->_frac_length)) * sizeof(int32_t)); } return error; } // TODO(lingbin): if ignore do_add's error code DecimalValue operator+(const DecimalValue& v1, const DecimalValue& v2) { DecimalValue result; if (v1._sign == v2._sign) { do_add(v1, v2, &result); } else { do_sub(v1, v2, &result); } return result; } DecimalValue operator-(const DecimalValue& v1, const DecimalValue& v2) { DecimalValue result; if (v1._sign == v2._sign) { do_sub(v1, v2, &result); } else { do_add(v1, v2, &result); } return result; } DecimalValue operator*(const DecimalValue& v1, const DecimalValue& v2){ DecimalValue result; do_mul(v1, v2, &result); return result; } DecimalValue operator/(const DecimalValue& v1, const DecimalValue& v2){ DecimalValue result; do_div_mod(v1, v2, &result, nullptr); return result; } DecimalValue operator%(const DecimalValue& v1, const DecimalValue& v2){ DecimalValue result; do_div_mod(v1, v2, nullptr, &result); return result; } std::ostream& operator<<(std::ostream& os, DecimalValue const& decimal_value) { return os << decimal_value.to_string(); } std::istream& operator>>(std::istream& ism, DecimalValue& decimal_value) { std::string str_buff; ism >> str_buff; decimal_value.parse_from_str(str_buff.c_str(), str_buff.size()); return ism; } DecimalValue operator-(const DecimalValue& v) { DecimalValue result = v; result._sign = !result._sign; return result; } DecimalValue& DecimalValue::operator+=(const DecimalValue& other) { *this = *this + other; return *this; } int DecimalValue::parse_from_str(const char* decimal_str, int32_t length) { set_to_zero(); const char* begin = decimal_str; const char* end = decimal_str + length; int32_t error = E_DEC_OK; // ignore leading spaces while (begin < end && std::isspace(*begin)) { ++begin; } if (begin == end) { set_to_zero(); return E_DEC_BAD_NUM; } // positive or negative if (*begin == '-') { _sign = true; ++begin; } else if (*begin == '+') { _sign = false; ++begin; } else { _sign = false; } // count int_length and frac_length const char* temp_ptr = begin; const char* frac_ptr = nullptr; // after this loop, 'begin' point to the first non digital position while (begin < end && std::isdigit(*begin)) { ++begin; } int32_t int_len = (int32_t) (begin - temp_ptr); int32_t frac_len = 0; if (begin < end && *begin == '.') { frac_ptr = begin + 1; while (frac_ptr < end && std::isdigit(*frac_ptr)) { ++frac_ptr; } frac_len = frac_ptr - begin - 1; // -1 for char '.' } else { frac_len = 0; frac_ptr = begin; } // bad num like " a" if ((int_len + frac_len) == 0) { set_to_zero(); return E_DEC_BAD_NUM; } int32_t int_big_digit_len = round_up(int_len); int32_t frac_big_digit_len = round_up(frac_len); fix_intg_frac_error(_buffer_length, &int_big_digit_len, &frac_big_digit_len, &error); if (error) { frac_len = frac_big_digit_len * DIG_PER_DEC1; if (error == E_DEC_OVERFLOW) { int_len = int_big_digit_len * DIG_PER_DEC1; } } _int_length = int_len; _frac_length = frac_len; temp_ptr = begin; // fill int value int32_t* buff = _buffer + int_big_digit_len; int32_t value = 0; int32_t index_in_powers10 = 0; for (; int_len > 0; --int_len) { value += (*--temp_ptr - '0') * powers10[index_in_powers10]; ++index_in_powers10; if (index_in_powers10 == DIG_PER_DEC1) { *--buff = value; value = 0; index_in_powers10 = 0; } } if (index_in_powers10) { *--buff = value; } // fill fraction value buff = _buffer + int_big_digit_len; temp_ptr = begin; for (value = 0, index_in_powers10 = 0; frac_len > 0; --frac_len) { value = (*++temp_ptr - '0') + value * 10; ++index_in_powers10; if (index_in_powers10 == DIG_PER_DEC1) { *buff++ = value; value = 0; index_in_powers10 = 0; } } if (index_in_powers10) { *buff = value * powers10[DIG_PER_DEC1 - index_in_powers10]; } // TODO: we do not support decimal in scientific notation if ((frac_ptr + 1) < end && (*frac_ptr == 'e' || *frac_ptr == 'E')) { // return E_DEC_BAD_NUM; int64_t exponent = strtoll(frac_ptr + 1, (char**) &end, 10); if (end != frac_ptr + 1) { // If at least one digit if (errno) { // system error number, it is thread local set_to_zero(); return E_DEC_BAD_NUM; } if (exponent > (INT_MAX / 2) || (errno == 0 && exponent < 0)) { set_to_zero(); return E_DEC_OVERFLOW; } if (exponent < INT_MAX / 2 && error != E_DEC_OVERFLOW) { set_to_zero(); return E_DEC_TRUNCATED; } if (error != E_DEC_OVERFLOW) { // error = shift((int32_t) exponent); } } } if (_sign && is_zero()) { _sign = false; } return error; } // TODO(lingbin): should be refactored with to_string(int scale) std::string DecimalValue::to_string() const { // Ignore trailing zeroes int32_t intg = round_up(_int_length); int32_t frac = round_up(_frac_length); const int32_t* frac_begin = _buffer + intg; const int32_t* frac_end = frac_begin + (frac - 1); const int32_t* buff = frac_end; while ((buff < frac_end) && (*buff == 0)) { --buff; } int32_t actual_frac = (int32_t) (buff - frac_begin) + 1; int32_t actual_frac_len = actual_frac * DIG_PER_DEC1; // Count the number of zeroes at the end of last "big digit" number if (actual_frac_len > 0) { int32_t last_big_digit = *buff; while ((last_big_digit > 0) && (last_big_digit % 10) == 0) { --actual_frac_len; last_big_digit /= 10; } } return to_string(actual_frac_len); } std::string DecimalValue::to_string(int scale) const { int32_t temp_intg = _int_length; int32_t temp_frac = _frac_length; if (temp_frac > scale) { temp_frac = scale; } const int32_t* buff_no_zero = get_first_no_zero_index(&temp_intg); int32_t temp = 0; if ((temp_intg + temp_frac) == 0) { temp_intg = 1; buff_no_zero = &temp; } int32_t int_str_length = temp_intg; if (temp_intg == 0) { int_str_length = 1; } int32_t length = (_sign ? 1 : 0) + int_str_length + (temp_frac ? 1 : 0) + temp_frac; char result_str[DECIMAL_MAX_STR_LENGTH + 1]; char* result_ptr = result_str; result_ptr[length] = '\0'; if (_sign) { *result_ptr++ = '-'; } if (temp_frac) { char* char_point = result_ptr + int_str_length; int32_t fill_length = scale - temp_frac; const int32_t* buff = buff_no_zero + round_up(temp_intg); *char_point++ = '.'; for (; temp_frac > 0; temp_frac -= DIG_PER_DEC1) { int32_t m = *buff++; for (int32_t i = std::min(temp_frac, DIG_PER_DEC1); i; --i) { int32_t n = m / DIG_MASK; *char_point++ = '0' + n; m -= n * DIG_MASK; m *= 10; } } while (fill_length-- > 0) { *result_ptr = '0'; // use char '0' to fill } } if (temp_intg) { char* char_point = result_ptr + int_str_length; const int32_t* buff = buff_no_zero + round_up(temp_intg); for (; temp_intg > 0; temp_intg -= DIG_PER_DEC1) { int32_t m = *--buff; for (int32_t i = std::min(temp_intg, DIG_PER_DEC1); i; --i) { int32_t n = m / 10; *--char_point = '0' + (m - n * 10); m = n; } } } else { *result_ptr = '0'; } return std::string(result_str, length); } // NOTE: only change abstract value, do not change sign void DecimalValue::to_max_decimal(int32_t precision, int32_t frac) { int32_t *buf = _buffer; _int_length = precision - frac; int32_t intpart = precision - frac; if (intpart) { int32_t firstdigits = intpart % DIG_PER_DEC1; if (firstdigits) { *buf++ = powers10[firstdigits] - 1; // get 9 99 999 ... } for (intpart /= DIG_PER_DEC1; intpart; intpart--) { *buf++ = DIG_MAX; } } _frac_length = frac; if (frac) { int32_t lastdigits = frac % DIG_PER_DEC1; for (frac /= DIG_PER_DEC1; frac; frac--) { *buf++ = DIG_MAX; } if (lastdigits) { *buf = frac_max[lastdigits - 1]; } } } std::size_t hash_value(DecimalValue const& value) { return value.hash(0); } int DecimalValue::round(DecimalValue *to, int scale, DecimalRoundMode mode) { int frac0 = scale > 0 ? round_up(scale) : (scale + 1) / DIG_PER_DEC1; int frac1 = round_up(_frac_length); int intg0 = round_up(_int_length); int error = E_DEC_OK; int len = _buffer_length; int32_t* buf0 = _buffer; int32_t* buf1 = to->_buffer; int32_t x = 0; int32_t y = 0; int32_t carry = 0; int first_dig = 0; int round_digit = 0; switch (mode) { case HALF_UP: case HALF_EVEN: round_digit = 5; break; case CEILING: round_digit = _sign ? 10 : 0; break; case FLOOR: round_digit = _sign ? 0 : 10; break; case TRUNCATE: round_digit = 10; break; default: return E_DEC_ERROR; } // input is too large, make it meaningful if (frac0 + intg0 > len) { frac0 = len - intg0; scale = frac0 * DIG_PER_DEC1; error = E_DEC_TRUNCATED; } // zero if (scale + _int_length < 0) { to->set_to_zero(); return E_DEC_OK; } // normal case copy if (to != this) { int32_t *p0 = buf0 + intg0 + std::max(frac1, frac0); int32_t *p1 = buf1 + intg0 + std::max(frac1, frac0); while (buf0 < p0) { *(--p1) = *(--p0); } buf0 = to->_buffer; buf1 = to->_buffer; to->_sign = _sign; to->_int_length = std::min(intg0, len) * DIG_PER_DEC1; } // no need to trunk if (frac0 > frac1) { buf1 += intg0 + frac1; while (frac0-- > frac1) { *buf1++ = 0; } to->_frac_length = scale; return error; } if (scale >= _frac_length) { /* nothing to do */ to->_frac_length = scale; return error; } buf0 += intg0 + frac0 - 1; buf1 += intg0 + frac0 - 1; if (scale == frac0 * DIG_PER_DEC1) { bool do_inc = false; switch (round_digit) { case 0: { int32_t* p0 = buf0 + (frac1 - frac0); for (; p0 > buf0; p0--) { if (*p0) { do_inc = true; break; } } break; } case 5: { x = buf0[1] / DIG_MASK; do_inc = (x > 5) || ((x == 5) && (mode == HALF_UP || (frac0 + intg0 > 0 && *buf0 & 1))); break; } default: break; } if (do_inc) { if (frac0 + intg0 > 0) { (*buf1)++; } else { *(++buf1) = DIG_BASE; } } else if (frac0 + intg0 == 0) { to->set_to_zero(); return E_DEC_OK; } } else { /* TODO - fix this code as it won't work for CEILING mode */ int pos = frac0 * DIG_PER_DEC1 - scale - 1; x = *buf1 / powers10[pos]; y = x % 10; if (y > round_digit || (round_digit == 5 && y == 5 && (mode == HALF_UP || (x / 10) & 1))) { x += 10; } *buf1 = powers10[pos] * (x - y); } /* In case we're rounding e.g. 1.5e9 to 2.0e9, the decimal_digit_t's inside the buffer are as follows. Before <1, 5e8> After <2, 5e8> Hence we need to set the 2nd field to 0. The same holds if we round 1.5e-9 to 2e-9. */ if (frac0 < frac1) { int32_t *buf = to->_buffer + ((scale == 0 && intg0 == 0) ? 1 : intg0 + frac0); int32_t *end = to->_buffer + len; while (buf < end) { *buf++ = 0; } } if (*buf1 >= DIG_BASE) { carry = 1; *buf1 -= DIG_BASE; while (carry && --buf1 >= to->_buffer) { add(0, *buf1, buf1, &carry); } if (carry) { /* shifting the number to create space for new digit */ if (frac0 + intg0 >= len) { frac0--; scale = frac0 * DIG_PER_DEC1; error = E_DEC_TRUNCATED; /* XXX */ } for (buf1 = to->_buffer + intg0 + std::max(frac0, 0); buf1 > to->_buffer; buf1--) { /* Avoid out-of-bounds write. */ if (buf1 < to->_buffer + len) { buf1[0] = buf1[-1]; } else { error = E_DEC_OVERFLOW; } } *buf1 = 1; /* We cannot have more than 9 * 9 = 81 digits. */ if (to->_int_length < len * DIG_PER_DEC1) { to->_int_length++; } else { error = E_DEC_OVERFLOW; } } } else { for (;;) { if (*buf1) { break; } if (buf1-- == to->_buffer) { /* making 'zero' with the proper scale */ int32_t *p0 = to->_buffer + frac0 + 1; to->_int_length = 1; to->_frac_length = std::max(scale, 0); to->_sign = 0; for (buf1 = to->_buffer; buf1 < p0; buf1++) { *buf1 = 0; } return E_DEC_OK; } } } /* Here we check 999.9 -> 1000 case when we need to increase intg */ first_dig = to->_int_length % DIG_PER_DEC1; if (first_dig && (*buf1 >= powers10[first_dig])) { to->_int_length++; } if (scale < 0) { scale = 0; } to->_frac_length = scale; return error; } } // end namespace doris