imay commented on a change in pull request #697: Optimize decimal by introducing decimal_v2 (#695) URL: https://github.com/apache/incubator-doris/pull/697#discussion_r263642354
########## File path: be/src/runtime/decimalv2_value.cpp ########## @@ -0,0 +1,415 @@ +// 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/decimalv2_value.h" +#include "util/string_parser.hpp" + +#include <algorithm> +#include <iostream> +#include <utility> + +namespace doris { + +const char* DecimalV2Value::_s_llvm_class_name = "class.doris::DecimalV2Value"; + +static inline int128_t abs(const int128_t& x) { return (x < 0) ? -x : x; } + +// x>=0 && y>=0 +static int do_add(int128_t x, int128_t y, int128_t* result) { + int error = E_DEC_OK; + if (DecimalV2Value::MAX_DECIMAL_VALUE - x >= y) { + *result = x + y; + } else { + *result = DecimalV2Value::MAX_DECIMAL_VALUE; + error = E_DEC_OVERFLOW; + LOG(INFO) << "overflow (x=" << x << ", y=" << y << ")"; + } + return error; +} + +// x>=0 && y>=0 +static int do_sub(int128_t x, int128_t y, int128_t* result) { + int error = E_DEC_OK; + *result = x - y; + return error; +} + +// clear leading zero for __int128 +static int clz128(unsigned __int128 v) { + if (v == 0) return 128; + unsigned __int128 shifted = v >> 64; + if (shifted != 0) { + return __builtin_clzll(shifted); + } else { + return __builtin_clzll(v) + 64; + } +} + +// x>0 && y>0 +static int do_mul(int128_t x, int128_t y, int128_t* result) { + int error = E_DEC_OK; + + // The bits of result as following is 120 + // clz128((MAX_INT_VALUE * ONE_BILLION + MAX_FRAC_VALUE) * ONE_BILLION) = 8 + // The bits range of m * n is in (m+n-1 --> m+n) + int bits = 128 + 128 - clz128(x) - clz128(y); + if (bits > (120 + 1)) { + *result = DecimalV2Value::MAX_DECIMAL_VALUE; + LOG(INFO) << "overflow (x=" << x << ", y=" << y << ")"; + error = E_DEC_OVERFLOW; + return error; + } + + int128_t product = x * y; + *result = product / DecimalV2Value::ONE_BILLION; + + // overflow + if (*result > DecimalV2Value::MAX_DECIMAL_VALUE) { + *result = DecimalV2Value::MAX_DECIMAL_VALUE; + LOG(INFO) << "overflow (x=" << x << ", y=" << y << ")"; + error = E_DEC_OVERFLOW; + return error; + } + + // truncate with round + int128_t remainder = product % DecimalV2Value::ONE_BILLION; + if (remainder != 0) { + error = E_DEC_TRUNCATED; + if (remainder >= (DecimalV2Value::ONE_BILLION >> 1)) { + *result += 1; + } + LOG(INFO) << "truncate (x=" << x << ", y=" << y << ")" << ", result=" << *result; + } + + return error; +} + +// x>0 && y>0 +static int do_div(int128_t x, int128_t y, int128_t* result) { + int error = E_DEC_OK; + int128_t dividend = x * DecimalV2Value::ONE_BILLION; + *result = dividend / y; + + // overflow + int128_t remainder = dividend % y; + if (remainder != 0) { + error = E_DEC_TRUNCATED; + if (remainder >= (y >> 1)) { + *result += 1; + } + LOG(INFO) << "truncate (x=" << x << ", y=" << y << ")" << ", result=" << *result; + } + + return error; +} + +// x>0 && y>0 +static int do_mod(int128_t x, int128_t y, int128_t* result) { + int error = E_DEC_OK; + *result = x % y; + return error; +} + +DecimalV2Value operator+(const DecimalV2Value& v1, const DecimalV2Value& v2) { + int128_t result; + int128_t x = v1.value(); + int128_t y = v2.value(); + if (x == 0) { + result = y; + } else if (y == 0) { + result = x; + } else if (x > 0) { + if (y > 0) { + do_add(x, y, &result); + } else { + do_sub(x, -y, &result); + } + } else { // x < 0 + if (y > 0) { + do_sub(y, -x, &result); + } else { + do_add(-x, -y, &result); + result = -result; + } + } + + return DecimalV2Value(result); +} + +DecimalV2Value operator-(const DecimalV2Value& v1, const DecimalV2Value& v2) { + int128_t result; + int128_t x = v1.value(); + int128_t y = v2.value(); + if (x == 0) { + result = -y; + } else if (y == 0) { + result = x; + } else if (x > 0) { + if (y > 0) { + do_sub(x, y, &result); + } else { + do_add(x, -y, &result); + } + } else { // x < 0 + if (y > 0) { + do_add(-x, y, &result); + result = -result; + } else { + do_sub(-x, -y, &result); + result = -result; + } + } + + return DecimalV2Value(result); +} + +DecimalV2Value operator*(const DecimalV2Value& v1, const DecimalV2Value& v2){ + int128_t result; + int128_t x = v1.value(); + int128_t y = v2.value(); + + if (x == 0 || y == 0) return DecimalV2Value(0); + + bool is_positive = (x > 0 && y > 0) || (x < 0 && y < 0); + + do_mul(abs(x), abs(y), &result); + + if (!is_positive) result = -result; + + return DecimalV2Value(result); +} + +DecimalV2Value operator/(const DecimalV2Value& v1, const DecimalV2Value& v2){ + int128_t result; + int128_t x = v1.value(); + int128_t y = v2.value(); + + //todo: return 0 for divide zero + if (x == 0 || y == 0) return DecimalV2Value(0); + bool is_positive = (x > 0 && y > 0) || (x < 0 && y < 0); + do_div(abs(x), abs(y), &result); + + if (!is_positive) result = -result; + + return DecimalV2Value(result); +} + +DecimalV2Value operator%(const DecimalV2Value& v1, const DecimalV2Value& v2){ + int128_t result; + int128_t x = v1.value(); + int128_t y = v2.value(); + + //todo: return 0 for divide zero + if (x == 0 || y == 0) return DecimalV2Value(0); + + bool is_positive = (x > 0 && y > 0) || (x < 0 && y < 0); + do_mod(abs(x), abs(y), &result); + + if (!is_positive) result = -result; + + return DecimalV2Value(result); +} + +std::ostream& operator<<(std::ostream& os, DecimalV2Value const& decimal_value) { + return os << decimal_value.to_string(); +} + +std::istream& operator>>(std::istream& ism, DecimalV2Value& decimal_value) { + std::string str_buff; + ism >> str_buff; + decimal_value.parse_from_str(str_buff.c_str(), str_buff.size()); + return ism; +} + +DecimalV2Value operator-(const DecimalV2Value& v) { + return DecimalV2Value(-v.value()); +} + +DecimalV2Value& DecimalV2Value::operator+=(const DecimalV2Value& other) { + *this = *this + other; + return *this; +} + +int DecimalV2Value::parse_from_str(const char* decimal_str, int32_t length) { + int32_t error = E_DEC_OK; + StringParser::ParseResult result = StringParser::PARSE_SUCCESS; + + _value = StringParser::string_to_decimal(decimal_str, length, + PRECISION, SCALE, &result); + + if (result != StringParser::PARSE_SUCCESS) { + error = E_DEC_BAD_NUM; + } + return error; +} + +std::string DecimalV2Value::to_string(int round_scale) const { + if (_value == 0) return std::string(1, '0'); Review comment: indent ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
