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]