This is an automated email from the ASF dual-hosted git repository.

Mryange pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 8dbdde6a263 [fix](function) Fix decimal rounding overflow at scale 
boundaries (#68251)
8dbdde6a263 is described below

commit 8dbdde6a263fdb72057f95e831858e4b1e830aaa
Author: Mryange <[email protected]>
AuthorDate: Mon Sep 21 16:08:23 2026 +0800

    [fix](function) Fix decimal rounding overflow at scale boundaries (#68251)
    
    Decimal rounding produced incorrect results when the input or result
    scale exceeded the range supported by `uint64_t`, and `ceil`/`floor`
    could overflow while adjusting values near the native Decimal limits.
    Root cause: Decimal rescaling used the `uint64_t`-based `int_exp10`
    helper for Decimal128 and Decimal256 values, while the rounding formulas
    added or subtracted the divisor before division. This change uses scale
    factors with the Decimal type's native width, performs rounding from
    quotient and remainder without overflow-prone pre-adjustment, and
    handles divisors beyond the physical Decimal range for directed
    rounding. The result scale is now applied consistently across vector and
    constant Decimal inputs.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [ ] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [ ] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 be/src/exprs/function/round.h                  | 248 +++++++------------------
 be/test/exprs/function/function_round_test.cpp |  88 +++++++++
 2 files changed, 150 insertions(+), 186 deletions(-)

diff --git a/be/src/exprs/function/round.h b/be/src/exprs/function/round.h
index ebc3d356c7e..d8d480c30d8 100644
--- a/be/src/exprs/function/round.h
+++ b/be/src/exprs/function/round.h
@@ -108,41 +108,42 @@ struct IntegerRoundingComputation {
 
     static size_t prepare(size_t scale) { return scale; }
 
-    /// Integer overflow is Ok.
     static ALWAYS_INLINE T compute_impl(T x, T scale, T target_scale) {
+        T quotient = x / scale;
+        const T remainder = x - quotient * scale;
+
         if constexpr (rounding_mode == RoundingMode::Trunc) {
-            return target_scale > 1 ? x / scale * target_scale : x / scale;
+            return target_scale > 1 ? quotient * target_scale : quotient;
         }
         if constexpr (rounding_mode == RoundingMode::Floor) {
-            if (x < 0) {
-                x -= scale - 1;
+            if (remainder < 0) {
+                --quotient;
             }
-            return target_scale > 1 ? x / scale * target_scale : x / scale;
+            return target_scale > 1 ? quotient * target_scale : quotient;
         }
         if constexpr (rounding_mode == RoundingMode::Ceil) {
-            if (x >= 0) {
-                x += scale - 1;
+            if (remainder > 0) {
+                ++quotient;
             }
-            return target_scale > 1 ? x / scale * target_scale : x / scale;
+            return target_scale > 1 ? quotient * target_scale : quotient;
         }
         if constexpr (rounding_mode == RoundingMode::Round) {
-            if (x < 0) {
-                x -= scale;
-            }
+            const T abs_remainder = remainder < 0 ? -remainder : remainder;
+            const T remainder_complement = scale - abs_remainder;
+            const T carry = remainder < 0 ? -1 : 1;
             if constexpr (tie_breaking_mode == TieBreakingMode::Auto) {
-                x = (x + scale / 2) / scale;
+                if (remainder != 0 && abs_remainder >= remainder_complement) {
+                    quotient += carry;
+                }
             }
             if constexpr (tie_breaking_mode == TieBreakingMode::Bankers) {
-                T quotient = (x + scale / 2) / scale;
-                if (quotient * scale == x + scale / 2) {
-                    // round half to even
-                    x = (quotient + (x < 0)) & ~1;
-                } else {
-                    // round the others as usual
-                    x = quotient;
+                if (remainder != 0 &&
+                    (abs_remainder > remainder_complement ||
+                     (abs_remainder == remainder_complement && (quotient & 1) 
!= 0))) {
+                    quotient += carry;
                 }
             }
-            return target_scale > 1 ? x * target_scale : x;
+            return target_scale > 1 ? quotient * target_scale : quotient;
         }
     }
 
@@ -157,7 +158,21 @@ struct IntegerRoundingComputation {
                                       U target_scale) {
         if constexpr (sizeof(T) <= sizeof(scale) && scale_mode == 
ScaleMode::Negative) {
             if (scale >= std::numeric_limits<T>::max()) {
-                *out = 0;
+                if constexpr (!is_decimal(Type)) {
+                    *out = 0;
+                } else {
+                    // A saturated divisor is larger than every valid value of 
this Decimal type.
+                    // Only directed rounding can produce one representable 
target unit.
+                    if (target_scale >= std::numeric_limits<T>::max()) {
+                        *out = T(0);
+                    } else if constexpr (rounding_mode == RoundingMode::Floor) 
{
+                        *out = *in < 0 ? -target_scale : T(0);
+                    } else if constexpr (rounding_mode == RoundingMode::Ceil) {
+                        *out = *in > 0 ? target_scale : T(0);
+                    } else {
+                        *out = T(0);
+                    }
+                }
                 return;
             }
         }
@@ -176,28 +191,21 @@ private:
 
 public:
     static NO_INLINE void apply(const Container& in, UInt32 in_scale, 
Container& out,
-                                Int16 out_scale) {
-        Int16 scale_arg = in_scale - out_scale;
+                                Int16 out_scale, Int16 result_scale) {
+        Int32 scale_arg = static_cast<Int32>(in_scale) - out_scale;
         if (scale_arg > 0) {
             auto scale = DecimalScaleParams::get_scale_factor<Type>(scale_arg);
+            auto target_scale =
+                    DecimalScaleParams::get_scale_factor<Type>(result_scale - 
out_scale);
 
-            const NativeType* __restrict p_in = reinterpret_cast<const 
NativeType*>(in.data());
+            const auto* __restrict p_in = reinterpret_cast<const 
NativeType*>(in.data());
             const NativeType* end_in = reinterpret_cast<const 
NativeType*>(in.data()) + in.size();
-            NativeType* __restrict p_out = 
reinterpret_cast<NativeType*>(out.data());
-
-            if (out_scale < 0) {
-                auto negative_scale = 
DecimalScaleParams::get_scale_factor<Type>(-out_scale);
-                while (p_in < end_in) {
-                    Op::compute(p_in, scale, p_out, negative_scale);
-                    ++p_in;
-                    ++p_out;
-                }
-            } else {
-                while (p_in < end_in) {
-                    Op::compute(p_in, scale, p_out, 1);
-                    ++p_in;
-                    ++p_out;
-                }
+            auto* __restrict p_out = reinterpret_cast<NativeType*>(out.data());
+
+            while (p_in < end_in) {
+                Op::compute(p_in, scale, p_out, target_scale);
+                ++p_in;
+                ++p_out;
             }
         } else {
             memcpy(out.data(), in.data(), in.size() * sizeof(T));
@@ -205,16 +213,13 @@ public:
     }
 
     static NO_INLINE void apply(const NativeType& in, UInt32 in_scale, 
NativeType& out,
-                                Int16 out_scale) {
-        Int16 scale_arg = in_scale - out_scale;
+                                Int16 out_scale, Int16 result_scale) {
+        Int32 scale_arg = static_cast<Int32>(in_scale) - out_scale;
         if (scale_arg > 0) {
             auto scale = DecimalScaleParams::get_scale_factor<Type>(scale_arg);
-            if (out_scale < 0) {
-                auto negative_scale = 
DecimalScaleParams::get_scale_factor<Type>(-out_scale);
-                Op::compute(&in, scale, &out, negative_scale);
-            } else {
-                Op::compute(&in, scale, &out, 1);
-            }
+            auto target_scale =
+                    DecimalScaleParams::get_scale_factor<Type>(result_scale - 
out_scale);
+            Op::compute(&in, scale, &out, target_scale);
         } else {
             memcpy(&out, &in, sizeof(NativeType));
         }
@@ -486,72 +491,27 @@ struct Dispatcher {
             const auto* const decimal_col =
                     assert_cast<const typename 
PrimitiveTypeTraits<T>::ColumnType*>(col_general);
             const auto& vec_src = decimal_col->get_data();
-            const size_t input_rows_count = vec_src.size();
             auto col_res = 
PrimitiveTypeTraits<T>::ColumnType::create(vec_src.size(), result_scale);
             auto& vec_res = col_res->get_data();
 
             if (!vec_res.empty()) {
-                FunctionRoundingImpl<ScaleMode::Negative>::apply(
-                        decimal_col->get_data(), decimal_col->get_scale(), 
vec_res, scale_arg);
-            }
-            // We need to always make sure result decimal's scale is as 
expected as its in plan
-            // So we need to append enough zero to result.
-
-            // Case 0: scale_arg <= -(integer part digits count)
-            //      do nothing, because result is 0
-            // Case 1: scale_arg <= 0 && scale_arg > -(integer part digits 
count)
-            //      decimal parts has been erased, so add them back by 
multiply 10^(result_scale)
-            // Case 2: scale_arg > 0 && scale_arg < result_scale
-            //      decimal part now has scale_arg digits, so multiply 
10^(result_scale - scal_arg)
-            // Case 3: scale_arg >= input_scale
-            //      do nothing
-
-            if (scale_arg <= 0) {
-                for (size_t i = 0; i < input_rows_count; ++i) {
-                    vec_res[i] = DecimalV2Value(vec_res[i].value() * 
int_exp10(result_scale));
-                }
-            } else if (scale_arg > 0 && scale_arg < result_scale) {
-                for (size_t i = 0; i < input_rows_count; ++i) {
-                    vec_res[i] = DecimalV2Value(vec_res[i].value() *
-                                                int_exp10(result_scale - 
scale_arg));
-                }
+                
FunctionRoundingImpl<ScaleMode::Negative>::apply(decimal_col->get_data(),
+                                                                 
decimal_col->get_scale(), vec_res,
+                                                                 scale_arg, 
result_scale);
             }
-
             return col_res;
         } else if constexpr (is_decimal(T)) {
             const auto* const decimal_col =
                     assert_cast<const typename 
PrimitiveTypeTraits<T>::ColumnType*>(col_general);
             const auto& vec_src = decimal_col->get_data();
-            const size_t input_rows_count = vec_src.size();
             auto col_res = 
PrimitiveTypeTraits<T>::ColumnType::create(vec_src.size(), result_scale);
             auto& vec_res = col_res->get_data();
 
             if (!vec_res.empty()) {
-                FunctionRoundingImpl<ScaleMode::Negative>::apply(
-                        decimal_col->get_data(), decimal_col->get_scale(), 
vec_res, scale_arg);
+                
FunctionRoundingImpl<ScaleMode::Negative>::apply(decimal_col->get_data(),
+                                                                 
decimal_col->get_scale(), vec_res,
+                                                                 scale_arg, 
result_scale);
             }
-            // We need to always make sure result decimal's scale is as 
expected as its in plan
-            // So we need to append enough zero to result.
-
-            // Case 0: scale_arg <= -(integer part digits count)
-            //      do nothing, because result is 0
-            // Case 1: scale_arg <= 0 && scale_arg > -(integer part digits 
count)
-            //      decimal parts has been erased, so add them back by 
multiply 10^(result_scale)
-            // Case 2: scale_arg > 0 && scale_arg < result_scale
-            //      decimal part now has scale_arg digits, so multiply 
10^(result_scale - scal_arg)
-            // Case 3: scale_arg >= input_scale
-            //      do nothing
-
-            if (scale_arg <= 0) {
-                for (size_t i = 0; i < input_rows_count; ++i) {
-                    vec_res[i].value *= int_exp10(result_scale);
-                }
-            } else if (scale_arg > 0 && scale_arg < result_scale) {
-                for (size_t i = 0; i < input_rows_count; ++i) {
-                    vec_res[i].value *= int_exp10(result_scale - scale_arg);
-                }
-            }
-
             return col_res;
         } else {
             static_assert(false);
@@ -607,29 +567,7 @@ struct Dispatcher {
             for (size_t i = 0; i < input_row_count; ++i) {
                 DecimalRoundingImpl<T, rounding_mode, 
tie_breaking_mode>::apply(
                         decimal_col->get_element(i).value(), input_scale,
-                        col_res->get_element(i).value(), 
col_scale_i32.get_data()[i]);
-            }
-
-            for (size_t i = 0; i < input_row_count; ++i) {
-                // For func(ColumnDecimal, ColumnInt32), we should always have 
same scale with source Decimal column
-                // So we need this check to make sure the result have correct 
digits count
-                //
-                // Case 0: scale_arg <= -(integer part digits count)
-                //      do nothing, because result is 0
-                // Case 1: scale_arg <= 0 && scale_arg > -(integer part digits 
count)
-                //      decimal parts has been erased, so add them back by 
multiply 10^(scale_arg)
-                // Case 2: scale_arg > 0 && scale_arg < result_scale
-                //      decimal part now has scale_arg digits, so multiply 
10^(result_scale - scal_arg)
-                // Case 3: scale_arg >= input_scale
-                //      do nothing
-                const Int32 scale_arg = col_scale_i32.get_data()[i];
-                if (scale_arg <= 0) {
-                    col_res->get_element(i) = 
DecimalV2Value(col_res->get_element(i).value() *
-                                                             
int_exp10(result_scale));
-                } else if (scale_arg > 0 && scale_arg < result_scale) {
-                    col_res->get_element(i) = 
DecimalV2Value(col_res->get_element(i).value() *
-                                                             
int_exp10(result_scale - scale_arg));
-                }
+                        col_res->get_element(i).value(), 
col_scale_i32.get_data()[i], result_scale);
             }
 
             return col_res;
@@ -643,27 +581,7 @@ struct Dispatcher {
             for (size_t i = 0; i < input_row_count; ++i) {
                 DecimalRoundingImpl<T, rounding_mode, 
tie_breaking_mode>::apply(
                         decimal_col->get_element(i).value, input_scale,
-                        col_res->get_element(i).value, 
col_scale_i32.get_data()[i]);
-            }
-
-            for (size_t i = 0; i < input_row_count; ++i) {
-                // For func(ColumnDecimal, ColumnInt32), we should always have 
same scale with source Decimal column
-                // So we need this check to make sure the result have correct 
digits count
-                //
-                // Case 0: scale_arg <= -(integer part digits count)
-                //      do nothing, because result is 0
-                // Case 1: scale_arg <= 0 && scale_arg > -(integer part digits 
count)
-                //      decimal parts has been erased, so add them back by 
multiply 10^(scale_arg)
-                // Case 2: scale_arg > 0 && scale_arg < result_scale
-                //      decimal part now has scale_arg digits, so multiply 
10^(result_scale - scal_arg)
-                // Case 3: scale_arg >= input_scale
-                //      do nothing
-                const Int32 scale_arg = col_scale_i32.get_data()[i];
-                if (scale_arg <= 0) {
-                    col_res->get_element(i).value *= int_exp10(result_scale);
-                } else if (scale_arg > 0 && scale_arg < result_scale) {
-                    col_res->get_element(i).value *= int_exp10(result_scale - 
scale_arg);
-                }
+                        col_res->get_element(i).value, 
col_scale_i32.get_data()[i], result_scale);
             }
 
             return col_res;
@@ -701,29 +619,7 @@ struct Dispatcher {
             for (size_t i = 0; i < input_rows_count; ++i) {
                 DecimalRoundingImpl<T, rounding_mode, 
tie_breaking_mode>::apply(
                         general_val, input_scale, 
col_res->get_element(i).value(),
-                        col_scale_i32.get_data()[i]);
-            }
-
-            for (size_t i = 0; i < input_rows_count; ++i) {
-                // For func(ColumnDecimal, ColumnInt32), we should always have 
same scale with source Decimal column
-                // So we need this check to make sure the result have correct 
digits count
-                //
-                // Case 0: scale_arg <= -(integer part digits count)
-                //      do nothing, because result is 0
-                // Case 1: scale_arg <= 0 && scale_arg > -(integer part digits 
count)
-                //      decimal parts has been erased, so add them back by 
multiply 10^(scale_arg)
-                // Case 2: scale_arg > 0 && scale_arg < result_scale
-                //      decimal part now has scale_arg digits, so multiply 
10^(result_scale - scal_arg)
-                // Case 3: scale_arg >= input_scale
-                //      do nothing
-                const Int32 scale_arg = col_scale_i32.get_data()[i];
-                if (scale_arg <= 0) {
-                    col_res->get_element(i) = 
DecimalV2Value(col_res->get_element(i).value() *
-                                                             
int_exp10(result_scale));
-                } else if (scale_arg > 0 && scale_arg < result_scale) {
-                    col_res->get_element(i) = 
DecimalV2Value(col_res->get_element(i).value() *
-                                                             
int_exp10(result_scale - scale_arg));
-                }
+                        col_scale_i32.get_data()[i], result_scale);
             }
 
             return col_res;
@@ -739,27 +635,7 @@ struct Dispatcher {
             for (size_t i = 0; i < input_rows_count; ++i) {
                 DecimalRoundingImpl<T, rounding_mode, 
tie_breaking_mode>::apply(
                         general_val, input_scale, 
col_res->get_element(i).value,
-                        col_scale_i32.get_data()[i]);
-            }
-
-            for (size_t i = 0; i < input_rows_count; ++i) {
-                // For func(ColumnDecimal, ColumnInt32), we should always have 
same scale with source Decimal column
-                // So we need this check to make sure the result have correct 
digits count
-                //
-                // Case 0: scale_arg <= -(integer part digits count)
-                //      do nothing, because result is 0
-                // Case 1: scale_arg <= 0 && scale_arg > -(integer part digits 
count)
-                //      decimal parts has been erased, so add them back by 
multiply 10^(scale_arg)
-                // Case 2: scale_arg > 0 && scale_arg < result_scale
-                //      decimal part now has scale_arg digits, so multiply 
10^(result_scale - scal_arg)
-                // Case 3: scale_arg >= input_scale
-                //      do nothing
-                const Int32 scale_arg = col_scale_i32.get_data()[i];
-                if (scale_arg <= 0) {
-                    col_res->get_element(i).value *= int_exp10(result_scale);
-                } else if (scale_arg > 0 && scale_arg < result_scale) {
-                    col_res->get_element(i).value *= int_exp10(result_scale - 
scale_arg);
-                }
+                        col_scale_i32.get_data()[i], result_scale);
             }
 
             return col_res;
diff --git a/be/test/exprs/function/function_round_test.cpp 
b/be/test/exprs/function/function_round_test.cpp
index 1c6b8ab8385..deb2482fb9e 100644
--- a/be/test/exprs/function/function_round_test.cpp
+++ b/be/test/exprs/function/function_round_test.cpp
@@ -59,20 +59,33 @@ using Decimal32FloorFunction = 
FunctionRounding<DecimalRoundTwoImpl<FloorName, T
                                                 RoundingMode::Floor, 
TieBreakingMode::Auto>;
 using Decimal64FloorFunction = FunctionRounding<DecimalRoundTwoImpl<FloorName, 
TYPE_DECIMAL64>,
                                                 RoundingMode::Floor, 
TieBreakingMode::Auto>;
+using Decimal128FloorFunction = 
FunctionRounding<DecimalRoundTwoImpl<FloorName, TYPE_DECIMAL128I>,
+                                                 RoundingMode::Floor, 
TieBreakingMode::Auto>;
+using Decimal256FloorFunction = 
FunctionRounding<DecimalRoundTwoImpl<FloorName, TYPE_DECIMAL256>,
+                                                 RoundingMode::Floor, 
TieBreakingMode::Auto>;
 using Decimal32CeilFunction = FunctionRounding<DecimalRoundTwoImpl<CeilName, 
TYPE_DECIMAL32>,
                                                RoundingMode::Ceil, 
TieBreakingMode::Auto>;
 using Decimal64CeilFunction = FunctionRounding<DecimalRoundTwoImpl<CeilName, 
TYPE_DECIMAL64>,
                                                RoundingMode::Ceil, 
TieBreakingMode::Auto>;
+using Decimal128CeilFunction = FunctionRounding<DecimalRoundTwoImpl<CeilName, 
TYPE_DECIMAL128I>,
+                                                RoundingMode::Ceil, 
TieBreakingMode::Auto>;
+using Decimal256CeilFunction = FunctionRounding<DecimalRoundTwoImpl<CeilName, 
TYPE_DECIMAL256>,
+                                                RoundingMode::Ceil, 
TieBreakingMode::Auto>;
 using Decimal32RoundFunction = FunctionRounding<DecimalRoundTwoImpl<RoundName, 
TYPE_DECIMAL32>,
                                                 RoundingMode::Round, 
TieBreakingMode::Auto>;
 using Decimal64RoundFunction = FunctionRounding<DecimalRoundTwoImpl<RoundName, 
TYPE_DECIMAL64>,
                                                 RoundingMode::Round, 
TieBreakingMode::Auto>;
+using Decimal128RoundFunction = 
FunctionRounding<DecimalRoundTwoImpl<RoundName, TYPE_DECIMAL128I>,
+                                                 RoundingMode::Round, 
TieBreakingMode::Auto>;
 using Decimal32RoundBankersFunction =
         FunctionRounding<DecimalRoundTwoImpl<RoundBankersName, 
TYPE_DECIMAL32>, RoundingMode::Round,
                          TieBreakingMode::Bankers>;
 using Decimal64RoundBankersFunction =
         FunctionRounding<DecimalRoundTwoImpl<RoundBankersName, 
TYPE_DECIMAL64>, RoundingMode::Round,
                          TieBreakingMode::Bankers>;
+using Decimal128RoundBankersFunction =
+        FunctionRounding<DecimalRoundTwoImpl<RoundBankersName, 
TYPE_DECIMAL128I>,
+                         RoundingMode::Round, TieBreakingMode::Bankers>;
 
 using FloatTruncateFunction = 
FunctionRounding<DoubleRoundTwoImpl<TruncateName>,
                                                RoundingMode::Trunc, 
TieBreakingMode::Auto>;
@@ -1022,6 +1035,44 @@ static void decimal_checker(const DecimalTestDataSet& 
round_test_cases, bool dec
     }
 }
 
+template <typename FuncType, typename DecimalType>
+static void check_decimal_rounding(typename DecimalType::NativeType input, int 
input_precision,
+                                   int input_scale, int scale_arg, int 
result_precision,
+                                   int result_scale, typename 
DecimalType::NativeType expected,
+                                   bool decimal_col_is_const, bool 
scale_col_is_const) {
+    auto func = std::dynamic_pointer_cast<FuncType>(FuncType::create());
+    auto col_general = ColumnDecimal<DecimalType::PType>::create(1, 
input_scale);
+    col_general->get_element(0) = DecimalType(input);
+    auto col_scale = ColumnInt32::create();
+    col_scale->insert(Field::create_field<TYPE_INT>(scale_arg));
+
+    Block block;
+    auto input_type =
+            
std::make_shared<DataTypeDecimal<DecimalType::PType>>(input_precision, 
input_scale);
+    if (decimal_col_is_const) {
+        block.insert({ColumnConst::create(std::move(col_general), 1), 
std::move(input_type),
+                      "col_general"});
+    } else {
+        block.insert({std::move(col_general), std::move(input_type), 
"col_general"});
+    }
+    if (scale_col_is_const) {
+        block.insert({ColumnConst::create(std::move(col_scale), 1),
+                      std::make_shared<DataTypeInt32>(), "col_scale"});
+    } else {
+        block.insert({std::move(col_scale), std::make_shared<DataTypeInt32>(), 
"col_scale"});
+    }
+    block.insert(
+            {nullptr,
+             
std::make_shared<DataTypeDecimal<DecimalType::PType>>(result_precision, 
result_scale),
+             "col_res"});
+
+    const ColumnNumbers arguments = {0, 1};
+    ASSERT_TRUE(func->execute_impl(nullptr, block, arguments, 2, 1).ok());
+    const auto& result =
+            assert_cast<const 
ColumnDecimal<DecimalType::PType>&>(*block.get_by_position(2).column);
+    EXPECT_EQ(result.get_element(0).value, expected);
+}
+
 template <typename FuncType, PrimitiveType FloatPType>
 static void float_checker(const FloatTestDataSet& round_test_cases, bool 
float_col_is_const) {
     using FloatType = typename PrimitiveTypeTraits<FloatPType>::CppType;
@@ -1109,6 +1160,43 @@ TEST(RoundFunctionTest, normal_decimal_const) {
     decimal_checker<Decimal64RoundBankersFunction, 
Decimal64>(round_decimal64_cases, true);
 }
 
+TEST(RoundFunctionTest, decimal_scale_boundaries) {
+    check_decimal_rounding<Decimal32CeilFunction, Decimal32>(1, 9, 9, -1, 9, 
0, 10, false, true);
+    check_decimal_rounding<Decimal32FloorFunction, Decimal32>(-1, 9, 9, -1, 9, 
0, -10, false, true);
+
+    const Int128 scale20 = common::exp10_i128(20);
+    check_decimal_rounding<Decimal128CeilFunction, Decimal128V3>(scale20 + 
scale20 / 2, 38, 20, 0,
+                                                                 38, 20, 
scale20 * 2, false, false);
+    check_decimal_rounding<Decimal128FloorFunction, Decimal128V3>(scale20 + 
scale20 / 2, 38, 20, 0,
+                                                                  38, 20, 
scale20, false, false);
+    check_decimal_rounding<Decimal128CeilFunction, Decimal128V3>(scale20 + 
scale20 / 2, 38, 20, 0,
+                                                                 38, 20, 
scale20 * 2, true, false);
+    check_decimal_rounding<Decimal128FloorFunction, Decimal128V3>(scale20 + 
scale20 / 2, 38, 20, 0,
+                                                                  38, 20, 
scale20, true, false);
+
+    const Int128 decimal128_fraction = common::exp10_i128(37) * 9;
+    check_decimal_rounding<Decimal128CeilFunction, 
Decimal128V3>(decimal128_fraction, 38, 38, 0, 38,
+                                                                 0, 1, false, 
true);
+    check_decimal_rounding<Decimal128FloorFunction, 
Decimal128V3>(-decimal128_fraction, 38, 38, 0,
+                                                                  38, 0, -1, 
false, true);
+
+    const Int128 decimal128_half = common::exp10_i128(37) * 5;
+    check_decimal_rounding<Decimal128RoundFunction, 
Decimal128V3>(decimal128_half, 38, 38, 0, 38, 0,
+                                                                  1, false, 
true);
+    check_decimal_rounding<Decimal128RoundFunction, 
Decimal128V3>(-decimal128_half, 38, 38, 0, 38,
+                                                                  0, -1, 
false, true);
+    check_decimal_rounding<Decimal128RoundBankersFunction, 
Decimal128V3>(decimal128_half, 38, 38, 0,
+                                                                         38, 
0, 0, false, true);
+    check_decimal_rounding<Decimal128RoundBankersFunction, 
Decimal128V3>(-decimal128_half, 38, 38,
+                                                                         0, 
38, 0, 0, false, true);
+
+    const wide::Int256 decimal256_fraction = common::exp10_i256(75) * 9;
+    check_decimal_rounding<Decimal256CeilFunction, 
Decimal256>(decimal256_fraction, 76, 76, 0, 76,
+                                                               0, 1, false, 
true);
+    check_decimal_rounding<Decimal256FloorFunction, 
Decimal256>(-decimal256_fraction, 76, 76, 0, 76,
+                                                                0, -1, false, 
true);
+}
+
 /// tests for func(Column, Column) with float input
 TEST(RoundFunctionTest, normal_float) {
     float_checker<FloatTruncateFunction, TYPE_FLOAT>(trunc_float32_cases, 
false);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to