diff --git a/dbms/src/Functions/FunctionsRound.h b/dbms/src/Functions/FunctionsRound.h index cf63b0d5f01..1ee628b507f 100644 --- a/dbms/src/Functions/FunctionsRound.h +++ b/dbms/src/Functions/FunctionsRound.h @@ -206,75 +206,6 @@ enum class RoundingMode #endif }; -/** Rounding functions for decimal values - */ - -template -struct DecimalRoundingComputation -{ - static_assert(IsDecimal); - static const size_t data_count = 1; - static size_t prepare(size_t scale) - { - return scale; - } - // compute need decimal_scale to interpret decimals - static inline void compute(const T * __restrict in, size_t scale, OutputType * __restrict out, ScaleType decimal_scale) - { - static_assert(std::is_same_v || std::is_same_v); - Float64 val = in->template toFloat(decimal_scale); - - if constexpr (scale_mode == ScaleMode::Positive) - { - val = val * scale; - } - else if constexpr (scale_mode == ScaleMode::Negative) - { - val = val / scale; - } - - if constexpr (rounding_mode == RoundingMode::Round) - { - val = round(val); - } - else if constexpr (rounding_mode == RoundingMode::Floor) - { - val = floor(val); - } - else if constexpr (rounding_mode == RoundingMode::Ceil) - { - val = ceil(val); - } - else if constexpr (rounding_mode == RoundingMode::Trunc) - { - val = trunc(val); - } - - - if constexpr (scale_mode == ScaleMode::Positive) - { - val = val / scale; - } - else if constexpr (scale_mode == ScaleMode::Negative) - { - val = val * scale; - } - - if constexpr (std::is_same_v) - { - *out = ToDecimal(val, decimal_scale); - } - else if constexpr (std::is_same_v) - { - *out = static_cast(val); - } - else - { - ; // never arrived here - } - } -}; - /** Rounding functions for integer values. */ @@ -336,12 +267,74 @@ struct IntegerRoundingComputation } } - static ALWAYS_INLINE void compute(const T * __restrict in, size_t scale, T * __restrict out) + static ALWAYS_INLINE void compute(const T * __restrict in, T scale, T * __restrict out) { *out = compute(*in, scale); } }; +/** Rounding functions for decimal values + */ + +template +struct DecimalRoundingComputation +{ + static_assert(IsDecimal); + using NativeType = typename T::NativeType; + static const size_t data_count = 1; + static size_t prepare(size_t scale) { return scale; } + // compute need decimal_scale to interpret decimals + static inline void compute( + const T * __restrict in, + size_t scale, + OutputType * __restrict out, + NativeType decimal_scale) + { + static_assert(std::is_same_v || std::is_same_v); + // Currently, we only use DecimalRoundingComputation for floor/ceil. + // As for round/truncate, we always use tidbRoundWithFrac/tidbTruncateWithFrac. + // So, we only handle ScaleMode::Zero here. + if constexpr (scale_mode == ScaleMode::Zero) + { + try + { + if constexpr (rounding_mode == RoundingMode::Floor) + { + auto x = in->value; + if (x < 0) + x -= decimal_scale - 1; + *out = static_cast(x / decimal_scale); + } + else if constexpr (rounding_mode == RoundingMode::Ceil) + { + auto x = in->value; + if (x >= 0) + x += decimal_scale - 1; + *out = static_cast(x / decimal_scale); + } + else + { + throw Exception( + "Logical error: unexpected 'rounding_mode' of DecimalRoundingComputation", + ErrorCodes::LOGICAL_ERROR); + } + } + catch (const std::overflow_error & e) + { + throw Exception( + "Logical error: unexpected overflow in DecimalRoundingComputation", + ErrorCodes::LOGICAL_ERROR); + } + } + else + { + throw Exception( + "Logical error: unexpected 'scale_mode' of DecimalRoundingComputation and unexpected scale: " + + toString(scale), + ErrorCodes::LOGICAL_ERROR); + } + } +}; #if __SSE4_1__ @@ -554,7 +547,7 @@ struct IntegerRoundingImpl while (p_in < end_in) { - Op::compute(p_in, scale, p_out); + Op::compute(p_in, static_cast(scale), p_out); ++p_in; ++p_out; } @@ -620,6 +613,9 @@ struct DecimalRoundingImpl; template struct DecimalRoundingImpl { + static_assert(IsDecimal); + using NativeType = typename T::NativeType; + private: using Op = DecimalRoundingComputation; using Data = T; @@ -627,7 +623,8 @@ struct DecimalRoundingImpl public: static NO_INLINE void apply(const DecimalPaddedPODArray & in, size_t scale, typename ColumnVector::Container & out) { - ScaleType decimal_scale = in.getScale(); + ScaleType in_scale = in.getScale(); + auto decimal_scale = intExp10OfSize(in_scale); const T * end_in = in.data() + in.size(); const T * __restrict p_in = in.data(); @@ -645,6 +642,9 @@ struct DecimalRoundingImpl template struct DecimalRoundingImpl { + static_assert(IsDecimal); + using NativeType = typename T::NativeType; + private: using Op = DecimalRoundingComputation; using Data = T; @@ -652,7 +652,8 @@ struct DecimalRoundingImpl public: static NO_INLINE void apply(const DecimalPaddedPODArray & in, size_t scale, typename ColumnDecimal::Container & out) { - ScaleType decimal_scale = in.getScale(); + ScaleType in_scale = in.getScale(); + auto decimal_scale = intExp10OfSize(in_scale); const T * end_in = in.data() + in.size(); const T * __restrict p_in = in.data(); @@ -705,7 +706,12 @@ struct Dispatcher if constexpr (IsDecimal) { - auto col_res = ColumnDecimal::create(col->getData().size(), col->getData().getScale()); + UInt32 res_scale = 0; + if constexpr (rounding_mode == RoundingMode::Round || rounding_mode == RoundingMode::Trunc) + { + res_scale = col->getData().getScale(); + } + auto col_res = ColumnDecimal::create(col->getData().size(), res_scale); typename ColumnDecimal::Container & vec_res = col_res->getData(); applyInternal(col, vec_res, col_res, block, scale_arg, result); } @@ -808,6 +814,20 @@ class FunctionRounding : public IFunction fmt::format("Illegal type {} of argument of function {}", arguments[0]->getName(), getName()), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + if constexpr (rounding_mode == RoundingMode::Ceil || rounding_mode == RoundingMode::Floor) + { + if (arguments[0]->isDecimal()) + { + if (const auto * decimal_type32 = checkAndGetDataType(arguments[0].get())) + return std::make_shared(decimal_type32->getPrec(), 0); + else if (const auto * decimal_type64 = checkAndGetDataType(arguments[0].get())) + return std::make_shared(decimal_type64->getPrec(), 0); + else if (const auto * decimal_type128 = checkAndGetDataType(arguments[0].get())) + return std::make_shared(decimal_type128->getPrec(), 0); + else if (const auto * decimal_type256 = checkAndGetDataType(arguments[0].get())) + return std::make_shared(decimal_type256->getPrec(), 0); + } + } return arguments[0]; } diff --git a/dbms/src/Functions/tests/gtest_functions_floor_ceil.cpp b/dbms/src/Functions/tests/gtest_functions_floor_ceil.cpp new file mode 100644 index 00000000000..3c696c1b1cd --- /dev/null +++ b/dbms/src/Functions/tests/gtest_functions_floor_ceil.cpp @@ -0,0 +1,379 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DB +{ +namespace tests +{ +namespace +{ +template +using Limits = std::numeric_limits; + +using DecimalField32 = DecimalField; +using DecimalField64 = DecimalField; +using DecimalField128 = DecimalField; +using DecimalField256 = DecimalField; + +template +struct ToDecimalType; + +template +struct ToDecimalType> +{ + using type = T; +}; + +auto parseIntArray(const std::vector> & literals) +{ + std::vector> result; + + result.reserve(literals.size()); + for (const auto & literal : literals) + { + if (literal.has_value()) + result.push_back(literal.value()); + else + result.push_back(std::nullopt); + } + + return result; +} + +// parse an array of strings into array of decimals. +template +auto parseDecimalArray(PrecType prec, ScaleType scale, const std::vector> & literals) +{ + std::vector> result; + + result.reserve(literals.size()); + for (const auto & literal : literals) + { + if (literal.has_value()) + result.push_back(parseDecimal::type>(literal.value(), prec, scale)); + else + result.push_back(std::nullopt); + } + + return result; +} + +template +struct TestData +{ + using InputType = Input; + using OutputType = Output; + + PrecType input_prec; + ScaleType input_scale; + + std::vector> input; + std::vector> floor_output; + std::vector> ceil_output; +}; + +// `id` is used to distinguish between different test cases with same input and output types. +template +struct TestCase +{ + using InputType = Input; + using OutputType = Output; + static constexpr size_t id = id_; +}; + +using TestCases = ::testing::Types< + TestCase, + TestCase, + TestCase, + TestCase, + TestCase>; + +template +auto getTestData(); + +template <> +auto getTestData() +{ + return TestData{ + 9, + 2, + parseDecimalArray(9, 2, {"2.5", "999.5", "2", "999", std::nullopt}), + parseIntArray({2, 999, 2, 999, std::nullopt}), + parseIntArray({3, 1000, 2, 999, std::nullopt})}; +} + +template <> +auto getTestData() +{ + return TestData{ + 9, + 2, + parseDecimalArray(9, 2, {"2.5", "999.5", "2", "999", std::nullopt}), + parseDecimalArray(9, 0, {"2", "999", "2", "999", std::nullopt}), + parseDecimalArray(9, 0, {"3", "1000", "2", "999", std::nullopt})}; +} + +template <> +auto getTestData() +{ + return TestData{ + 18, + 10, + parseDecimalArray(18, 10, {"2.5", "999.5", "2", "999", std::nullopt}), + parseDecimalArray(18, 0, {"2", "999", "2", "999", std::nullopt}), + parseDecimalArray(18, 0, {"3", "1000", "2", "999", std::nullopt})}; +} + +template <> +auto getTestData() +{ + return TestData{ + 38, + 30, + parseDecimalArray(38, 30, {"2.5", "999.5", "2", "999", std::nullopt}), + parseDecimalArray(38, 0, {"2", "999", "2", "999", std::nullopt}), + parseDecimalArray(38, 0, {"3", "1000", "2", "999", std::nullopt})}; +} + +template <> +auto getTestData() +{ + return TestData{ + 65, + 30, + parseDecimalArray(65, 30, {"2.5", "999.5", "2", "999", std::nullopt}), + parseDecimalArray(65, 0, {"2", "999", "2", "999", std::nullopt}), + parseDecimalArray(65, 0, {"3", "1000", "2", "999", std::nullopt})}; +} + +} // namespace + +template +class TestFunctionsFloorAndCeil : public DB::tests::FunctionTest +{ +}; + +TYPED_TEST_CASE(TestFunctionsFloorAndCeil, TestCases); + +TYPED_TEST(TestFunctionsFloorAndCeil, Floor) +try +{ + // prepare test data. + + using InputType = typename TypeParam::InputType; + using OutputType = typename TypeParam::OutputType; + + auto data = getTestData(); + size_t size = data.input.size(); + + // determine data type. + + DataTypePtr data_type; + if constexpr (isDecimalField()) + data_type = std::make_shared::type>>( + data.input_prec, + data.input_scale); + else + data_type = std::make_shared>(); + data_type = makeNullable(data_type); + + auto input_column = data_type->createColumn(); + + for (const auto & value : data.input) + { + if (value.has_value()) + input_column->insert(Field(value.value())); + else + input_column->insert(Null()); + } + + auto input = ColumnWithTypeAndName{std::move(input_column), data_type, "input"}; + + // build function. + + const auto context = TiFlashTestEnv::getContext(); + auto & factory = FunctionFactory::instance(); + + FunctionBuilderPtr builder; + if constexpr (isDecimalField()) + builder = factory.tryGet("floor", *context); + else + builder = factory.tryGet("floorDecimalToInt", *context); + ASSERT_NE(builder, nullptr); + + auto function = builder->build({input}); + ASSERT_NE(function, nullptr); + + // prepare block. + + Block block; + block.insert(input); + block.insert({nullptr, function->getReturnType(), "result"}); + + // execute function. + + function->execute(block, {block.getPositionByName("input")}, block.getPositionByName("result")); + + // check result. + + auto result = block.getByName("result"); + ASSERT_NE(result.column, nullptr); + + if constexpr (isDecimalField()) + { + auto result_type = result.type; + if (auto actual = checkAndGetDataType(result_type.get())) + result_type = actual->getNestedType(); + } + + ASSERT_EQ(block.rows(), size); + + Field result_field; + for (size_t i = 0; i < size; ++i) + { + result.column->get(i, result_field); + + if (data.floor_output[i].has_value()) + { + ASSERT_FALSE(result_field.isNull()) << "index = " << i; + + auto got = result_field.safeGet(); + auto expected = data.floor_output[i].value(); + + if constexpr (isDecimalField()) + { + ASSERT_EQ(got.getScale(), expected.getScale()) << "index = " << i; + ASSERT_TRUE(got.getValue() == expected.getValue()) << "index = " << i; + } + else + ASSERT_EQ(got, expected) << "index = " << i; + } + else + ASSERT_TRUE(result_field.isNull()) << "index = " << i; + } +} +CATCH + +TYPED_TEST(TestFunctionsFloorAndCeil, Ceil) +try +{ + // prepare test data. + + using InputType = typename TypeParam::InputType; + using OutputType = typename TypeParam::OutputType; + + auto data = getTestData(); + size_t size = data.input.size(); + + // determine data type. + + DataTypePtr data_type; + if constexpr (isDecimalField()) + data_type = std::make_shared::type>>( + data.input_prec, + data.input_scale); + else + data_type = std::make_shared>(); + data_type = makeNullable(data_type); + + auto input_column = data_type->createColumn(); + + for (const auto & value : data.input) + { + if (value.has_value()) + input_column->insert(Field(value.value())); + else + input_column->insert(Null()); + } + + auto input = ColumnWithTypeAndName{std::move(input_column), data_type, "input"}; + + // build function. + + const auto context = TiFlashTestEnv::getContext(); + auto & factory = FunctionFactory::instance(); + + FunctionBuilderPtr builder; + if constexpr (isDecimalField()) + builder = factory.tryGet("ceil", *context); + else + builder = factory.tryGet("ceilDecimalToInt", *context); + ASSERT_NE(builder, nullptr); + + auto function = builder->build({input}); + ASSERT_NE(function, nullptr); + + // prepare block. + + Block block; + block.insert(input); + block.insert({nullptr, function->getReturnType(), "result"}); + + // execute function. + + function->execute(block, {block.getPositionByName("input")}, block.getPositionByName("result")); + + // check result. + + auto result = block.getByName("result"); + ASSERT_NE(result.column, nullptr); + + if constexpr (isDecimalField()) + { + auto result_type = result.type; + if (auto actual = checkAndGetDataType(result_type.get())) + result_type = actual->getNestedType(); + } + + ASSERT_EQ(block.rows(), size); + + Field result_field; + for (size_t i = 0; i < size; ++i) + { + result.column->get(i, result_field); + + if (data.ceil_output[i].has_value()) + { + ASSERT_FALSE(result_field.isNull()) << "index = " << i; + + auto got = result_field.safeGet(); + auto expected = data.ceil_output[i].value(); + + if constexpr (isDecimalField()) + { + ASSERT_EQ(got.getScale(), expected.getScale()) << "index = " << i; + ASSERT_TRUE(got.getValue() == expected.getValue()) << "index = " << i; + } + else + ASSERT_EQ(got, expected) << "index = " << i; + } + else + ASSERT_TRUE(result_field.isNull()) << "index = " << i; + } +} +CATCH + +} // namespace tests + +} // namespace DB diff --git a/libs/libcommon/include/common/intExp.h b/libs/libcommon/include/common/intExp.h index 8a0188d6287..dc49779a3dc 100644 --- a/libs/libcommon/include/common/intExp.h +++ b/libs/libcommon/include/common/intExp.h @@ -14,6 +14,8 @@ #pragma once +#include + #include #include @@ -32,16 +34,226 @@ inline uint64_t intExp10(int x) if (x > 19) return std::numeric_limits::max(); - static const uint64_t table[20] = - { - 1ULL, 10ULL, 100ULL, - 1000ULL, 10000ULL, 100000ULL, - 1000000ULL, 10000000ULL, 100000000ULL, - 1000000000ULL, 10000000000ULL, 100000000000ULL, - 1000000000000ULL, 10000000000000ULL, 100000000000000ULL, - 1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL, - 1000000000000000000ULL, 10000000000000000000ULL - }; + static const uint64_t table[20] = { + 1ULL, + 10ULL, + 100ULL, + 1000ULL, + 10000ULL, + 100000ULL, + 1000000ULL, + 10000000ULL, + 100000000ULL, + 1000000000ULL, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL}; return table[x]; } + +constexpr int64_t exp10_i64_table[] + = {1LL, + 10LL, + 100LL, + 1000LL, + 10000LL, + 100000LL, + 1000000LL, + 10000000LL, + 100000000LL, + 1000000000LL, + 10000000000LL, + 100000000000LL, + 1000000000000LL, + 10000000000000LL, + 100000000000000LL, + 1000000000000000LL, + 10000000000000000LL, + 100000000000000000LL, + 1000000000000000000LL}; + +constexpr Int128 exp10_i128_table[] + = {static_cast(1LL), + static_cast(10LL), + static_cast(100LL), + static_cast(1000LL), + static_cast(10000LL), + static_cast(100000LL), + static_cast(1000000LL), + static_cast(10000000LL), + static_cast(100000000LL), + static_cast(1000000000LL), + static_cast(10000000000LL), + static_cast(100000000000LL), + static_cast(1000000000000LL), + static_cast(10000000000000LL), + static_cast(100000000000000LL), + static_cast(1000000000000000LL), + static_cast(10000000000000000LL), + static_cast(100000000000000000LL), + static_cast(1000000000000000000LL), + static_cast(1000000000000000000LL) * 10LL, + static_cast(1000000000000000000LL) * 100LL, + static_cast(1000000000000000000LL) * 1000LL, + static_cast(1000000000000000000LL) * 10000LL, + static_cast(1000000000000000000LL) * 100000LL, + static_cast(1000000000000000000LL) * 1000000LL, + static_cast(1000000000000000000LL) * 10000000LL, + static_cast(1000000000000000000LL) * 100000000LL, + static_cast(1000000000000000000LL) * 1000000000LL, + static_cast(1000000000000000000LL) * 10000000000LL, + static_cast(1000000000000000000LL) * 100000000000LL, + static_cast(1000000000000000000LL) * 1000000000000LL, + static_cast(1000000000000000000LL) * 10000000000000LL, + static_cast(1000000000000000000LL) * 100000000000000LL, + static_cast(1000000000000000000LL) * 1000000000000000LL, + static_cast(1000000000000000000LL) * 10000000000000000LL, + static_cast(1000000000000000000LL) * 100000000000000000LL, + static_cast(1000000000000000000LL) * 100000000000000000LL * 10LL, + static_cast(1000000000000000000LL) * 100000000000000000LL * 100LL, + static_cast(1000000000000000000LL) * 100000000000000000LL * 1000LL}; + +constexpr Int256 i10e18{1000000000000000000ll}; +constexpr Int256 exp10_i256_table[] = { + static_cast(1ll), + static_cast(10ll), + static_cast(100ll), + static_cast(1000ll), + static_cast(10000ll), + static_cast(100000ll), + static_cast(1000000ll), + static_cast(10000000ll), + static_cast(100000000ll), + static_cast(1000000000ll), + static_cast(10000000000ll), + static_cast(100000000000ll), + static_cast(1000000000000ll), + static_cast(10000000000000ll), + static_cast(100000000000000ll), + static_cast(1000000000000000ll), + static_cast(10000000000000000ll), + static_cast(100000000000000000ll), + i10e18, + i10e18 * 10ll, + i10e18 * 100ll, + i10e18 * 1000ll, + i10e18 * 10000ll, + i10e18 * 100000ll, + i10e18 * 1000000ll, + i10e18 * 10000000ll, + i10e18 * 100000000ll, + i10e18 * 1000000000ll, + i10e18 * 10000000000ll, + i10e18 * 100000000000ll, + i10e18 * 1000000000000ll, + i10e18 * 10000000000000ll, + i10e18 * 100000000000000ll, + i10e18 * 1000000000000000ll, + i10e18 * 10000000000000000ll, + i10e18 * 100000000000000000ll, + i10e18 * 100000000000000000ll * 10ll, + i10e18 * 100000000000000000ll * 100ll, + i10e18 * 100000000000000000ll * 1000ll, + i10e18 * 100000000000000000ll * 10000ll, + i10e18 * 100000000000000000ll * 100000ll, + i10e18 * 100000000000000000ll * 1000000ll, + i10e18 * 100000000000000000ll * 10000000ll, + i10e18 * 100000000000000000ll * 100000000ll, + i10e18 * 100000000000000000ll * 1000000000ll, + i10e18 * 100000000000000000ll * 10000000000ll, + i10e18 * 100000000000000000ll * 100000000000ll, + i10e18 * 100000000000000000ll * 1000000000000ll, + i10e18 * 100000000000000000ll * 10000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000ll, + i10e18 * 100000000000000000ll * 1000000000000000ll, + i10e18 * 100000000000000000ll * 10000000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 10ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 1000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 10000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 1000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 10000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 1000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 10000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 1000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 10000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 1000000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 10000000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll * 10ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll * 100ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll * 1000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll * 10000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll * 100000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll * 1000000ll, + i10e18 * 100000000000000000ll * 100000000000000000ll * 100000000000000000ll * 10000000ll, +}; + +constexpr int exp10_i32(int x) +{ + if (x < 0) + return 0; + if (x > 9) + return std::numeric_limits::max(); + + constexpr int exp10_i32_table[10] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + return exp10_i32_table[x]; +} + +constexpr int64_t exp10_i64(int x) +{ + if (x < 0) + return 0; + if (x > 18) + return std::numeric_limits::max(); + + return exp10_i64_table[x]; +} + +constexpr Int128 exp10_i128(int x) +{ + if (x < 0) + return 0; + if (x > 38) + return std::numeric_limits::max(); + + return exp10_i128_table[x]; +} + +constexpr Int256 exp10_i256(int x) +{ + if (x < 0) + return 0; + if (x > 76) + return std::numeric_limits::max(); + + return exp10_i256_table[x]; +} + + +/// intExp10 returning the type T. +template +T intExp10OfSize(int x) +{ + if constexpr (sizeof(T) <= 4) + return static_cast(exp10_i32(x)); + else if constexpr (sizeof(T) <= 8) + return exp10_i64(x); + else if constexpr (sizeof(T) <= 16) + return exp10_i128(x); + else + return exp10_i256(x); +} diff --git a/tests/fullstack-test/expr/ceil_floor.test b/tests/fullstack-test/expr/ceil_floor.test index 93212c34c7c..14be5776b7b 100644 --- a/tests/fullstack-test/expr/ceil_floor.test +++ b/tests/fullstack-test/expr/ceil_floor.test @@ -34,4 +34,29 @@ mysql> select /*+ AGG_TO_COP(), READ_FROM_STORAGE(TIFLASH[t]) */ sum(floor(ld)), | 1 | 2 | 3 | 4 | +----------------+----------------+---------------+--------------+ +mysql> drop table if exists test.t + +mysql> drop table if exists test.t +mysql> create table test.t(a decimal(8,2), b decimal(15,0), c decimal(19,1), d decimal(38,2), e decimal(38,30), f decimal(40,30), g decimal(52, 30), h decimal(65,30), ff float, ii integer) +mysql> alter table test.t set tiflash replica 1 +mysql> insert into test.t values(2.5, 2, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2) +mysql> insert into test.t values(2, 2, 2, 2, 2, 2, 2, 2, 2, 2) +mysql> insert into test.t values(-2, -2, -2, -2, -2, -2, -2, -2, -2, -2) +mysql> insert into test.t values(999, 999, 999, 999, 999, 999, 999, 999, 999, 999) +mysql> insert into test.t values(-999, -999, -999, -999, -999, -999, -999, -999, -999, -999) + +func> wait_table test t + + +mysql> select floor(a),ceil(a),floor(b),ceil(b),floor(c),ceil(c),floor(d),ceil(d),floor(e),ceil(e),floor(f),ceil(f),floor(g),ceil(g),floor(h),ceil(h),floor(ii),ceil(ii) from test.t; ++----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+-----------+----------+ +| floor(a) | ceil(a) | floor(b) | ceil(b) | floor(c) | ceil(c) | floor(d) | ceil(d) | floor(e) | ceil(e) | floor(f) | ceil(f) | floor(g) | ceil(g) | floor(h) | ceil(h) | floor(ii) | ceil(ii) | ++----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+-----------+----------+ +| 2 | 3 | 2 | 2 | 2 | 3 | 2 | 3 | 2 | 3 | 2 | 3 | 2 | 3 | 2 | 3 | 2 | 2 | +| 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | +| -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | +| 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | 999 | +| -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | -999 | ++----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+----------+---------+-----------+----------+ + mysql> drop table if exists test.t \ No newline at end of file