From afd6598800dc69a5478602214e7e3a9518e32973 Mon Sep 17 00:00:00 2001 From: rui-mo Date: Sat, 27 Jan 2024 16:38:17 +0800 Subject: [PATCH] Add CAST(real as decimal) (8575) --- velox/docs/functions/presto/conversion.rst | 18 +- velox/expression/CastExpr-inl.h | 10 +- velox/expression/CastExpr.cpp | 6 +- velox/expression/CastExpr.h | 4 +- velox/expression/tests/CastExprTest.cpp | 156 ++++++++++++++- velox/type/DecimalUtil.h | 61 ++++-- velox/type/tests/DecimalTest.cpp | 220 ++++++++++++++++----- 7 files changed, 381 insertions(+), 94 deletions(-) diff --git a/velox/docs/functions/presto/conversion.rst b/velox/docs/functions/presto/conversion.rst index f640b4626510..d27fb1890a0e 100644 --- a/velox/docs/functions/presto/conversion.rst +++ b/velox/docs/functions/presto/conversion.rst @@ -123,7 +123,7 @@ supported conversions to/from JSON are listed in :doc:`json`. - - - - - + - Y * - double - Y - Y @@ -724,14 +724,15 @@ Invalid examples SELECT cast(123 as decimal(6, 4)); -- Out of range SELECT cast(123 as decimal(4, 2)); -- Out of range -From double type -^^^^^^^^^^^^^^^^ +From floating-point types +^^^^^^^^^^^^^^^^^^^^^^^^^ -Casting a double number to a decimal of given precision and scale is allowed -if the input value can be represented by the precision and scale. When the -given scale is less than the number of decimal places, the double value is -rounded. The conversion precision is up to 15 as double provides 16(±1) -significant decimal digits precision. Casting from invalid input values throws. +Casting a floating-point number to a decimal of given precision and scale is allowed +if the input value can be represented by the precision and scale. When the given +scale is less than the number of decimal places, the floating-point value is rounded. +The conversion precision is up to 15 for double and 6 for real according to the +significant decimal digits precision they provide. Casting from invalid input values +throws. Valid example @@ -741,6 +742,7 @@ Valid example SELECT cast(0.12 as decimal(4, 1)); -- decimal '0.1' SELECT cast(0.19 as decimal(4, 1)); -- decimal '0.2' SELECT cast(0.123456789123123 as decimal(38, 18)); -- decimal '0.123456789123123000' + SELECT cast(cast(0.123456 as real) as decimal(38, 18)); -- decimal '0.123456000000000000' Invalid example diff --git a/velox/expression/CastExpr-inl.h b/velox/expression/CastExpr-inl.h index 8d9e732d33a9..1bc54880bf06 100644 --- a/velox/expression/CastExpr-inl.h +++ b/velox/expression/CastExpr-inl.h @@ -453,22 +453,22 @@ void CastExpr::applyIntToDecimalCastKernel( }); } -template -void CastExpr::applyDoubleToDecimalCastKernel( +template +void CastExpr::applyFloatingPointToDecimalCastKernel( const SelectivityVector& rows, const BaseVector& input, exec::EvalCtx& context, const TypePtr& toType, VectorPtr& result) { - const auto doubleInput = input.as>(); + const auto floatingInput = input.as>(); auto rawResults = result->asUnchecked>()->mutableRawValues(); const auto toPrecisionScale = getDecimalPrecisionScale(*toType); applyToSelectedNoThrowLocal(context, rows, result, [&](vector_size_t row) { TOutput output; - const auto status = DecimalUtil::rescaleDouble( - doubleInput->valueAt(row), + const auto status = DecimalUtil::rescaleFloatingPoint( + floatingInput->valueAt(row), toPrecisionScale.first, toPrecisionScale.second, output); diff --git a/velox/expression/CastExpr.cpp b/velox/expression/CastExpr.cpp index b0e375c279ae..00c02ca4e8ac 100644 --- a/velox/expression/CastExpr.cpp +++ b/velox/expression/CastExpr.cpp @@ -576,8 +576,12 @@ VectorPtr CastExpr::applyDecimal( applyIntToDecimalCastKernel( rows, input, context, toType, castResult); break; + case TypeKind::REAL: + applyFloatingPointToDecimalCastKernel( + rows, input, context, toType, castResult); + break; case TypeKind::DOUBLE: - applyDoubleToDecimalCastKernel( + applyFloatingPointToDecimalCastKernel( rows, input, context, toType, castResult); break; case TypeKind::BIGINT: { diff --git a/velox/expression/CastExpr.h b/velox/expression/CastExpr.h index 6a7e57428b11..8cb8447489f2 100644 --- a/velox/expression/CastExpr.h +++ b/velox/expression/CastExpr.h @@ -206,8 +206,8 @@ class CastExpr : public SpecialForm { const TypePtr& toType, VectorPtr& castResult); - template - void applyDoubleToDecimalCastKernel( + template + void applyFloatingPointToDecimalCastKernel( const SelectivityVector& rows, const BaseVector& input, exec::EvalCtx& context, diff --git a/velox/expression/tests/CastExprTest.cpp b/velox/expression/tests/CastExprTest.cpp index 62a7de0ffd6b..0e293942d29c 100644 --- a/velox/expression/tests/CastExprTest.cpp +++ b/velox/expression/tests/CastExprTest.cpp @@ -1969,29 +1969,52 @@ TEST_F(CastExprTest, castInTry) { TEST_F(CastExprTest, doubleToDecimal) { // Double to short decimal. - const auto input = - makeFlatVector({-3333.03, -2222.02, -1.0, 0.00, 100, 99999.99}); + const auto input = makeFlatVector( + {-3333.03, + -2222.02, + -1.0, + 0.00, + 100, + 99999.99, + 10.03, + 10.05, + 9.95, + -2.123456789}); testCast( input, makeFlatVector( - {-33'330'300, -22'220'200, -10'000, 0, 1'000'000, 999'999'900}, + {-33'330'300, + -22'220'200, + -10'000, + 0, + 1'000'000, + 999'999'900, + 100'300, + 100'500, + 99'500, + -21'235}, DECIMAL(10, 4))); // Double to long decimal. testCast( input, makeFlatVector( - {-33'330'300'000'000, - -22'220'200'000'000, - -10'000'000'000, + {HugeInt::build(0xFFFFFFFFFFFFFF4B, 0x50EABA2657C90000), + HugeInt::build(0xFFFFFFFFFFFFFF87, 0x8B4726C43A860000), + -1'000'000'000'000'000'000, 0, - 1'000'000'000'000, - 999'999'900'000'000}, - DECIMAL(20, 10))); + HugeInt::build(0x5, 0x6BC75E2D63100000), + HugeInt::build(0x152D, 0x02A45A5886BF0000), + HugeInt::build(0, 0x8B31B7DBD92B0000), + HugeInt::build(0, 0x8B78C5C0B8AD0000), + HugeInt::build(0, 0x8A1580485B230000), + -2'123'456'789'000'000'000}, + DECIMAL(38, 18))); testCast( input, makeFlatVector( - {-33'330, -22'220, -10, 0, 1'000, 1'000'000}, DECIMAL(20, 1))); + {-33'330, -22'220, -10, 0, 1'000, 1'000'000, 100, 101, 100, -21}, + DECIMAL(20, 1))); testCast( makeNullableFlatVector( {0.13456789, @@ -2062,6 +2085,119 @@ TEST_F(CastExprTest, doubleToDecimal) { "Cannot cast DOUBLE 'NaN' to DECIMAL(38, 2). The input value should be finite."); } +TEST_F(CastExprTest, realToDecimal) { + // Real to short decimal. + const auto input = makeFlatVector( + {-3333.03, + -2222.02, + -1.0, + 0.00, + 100, + 99999.9, + 10.03, + 10.05, + 9.95, + -2.12345}); + testCast( + input, + makeFlatVector( + {-33'330'300, + -22'220'200, + -10'000, + 0, + 1'000'000, + 999'999'000, + 100'300, + 100'500, + 99'500, + -212'35}, + DECIMAL(10, 4))); + + // Real to long decimal. + testCast( + input, + makeFlatVector( + {HugeInt::build(0xFFFFFFFFFFFFFF4B, 0x50EABA2657C90000), + HugeInt::build(0xFFFFFFFFFFFFFF87, 0x8B4726C43A860000), + -1'000'000'000'000'000'000, + 0, + HugeInt::build(0x5, 0x6BC75E2D63100000), + HugeInt::build(0x152D, 0x01649BD298F60000), + HugeInt::build(0, 0x8B31B7DBD92B0000), + HugeInt::build(0, 0x8B78C5C0B8AD0000), + HugeInt::build(0, 0x8A1580485B230000), + -2'123'450'000'000'000'000}, + DECIMAL(38, 18))); + testCast( + input, + makeFlatVector( + {-33'330, -22'220, -10, 0, 1'000, 999'999, 100, 101, 100, -21}, + DECIMAL(20, 1))); + testCast( + makeNullableFlatVector( + {0.134567, 0.000015, 0.000001, 0.999999, 0.123456, std::nullopt}), + makeNullableFlatVector( + {134'567'000'000'000'000, + 15'000'000'000'000, + 1'000'000'000'000, + 999'999'000'000'000'000, + 123'456'000'000'000'000, + std::nullopt}, + DECIMAL(38, 18))); + + testThrow( + REAL(), + DECIMAL(10, 2), + {9999999999999999999999.99}, + "Cannot cast REAL '9.999999778196308E21' to DECIMAL(10, 2). Result overflows."); + testThrow( + REAL(), + DECIMAL(10, 2), + {static_cast( + static_cast(std::numeric_limits::max()) + 1)}, + "Cannot cast REAL '9223372036854776000' to DECIMAL(10, 2). Result overflows."); + testThrow( + REAL(), + DECIMAL(10, 2), + {static_cast( + static_cast(std::numeric_limits::min()) - 1)}, + "Cannot cast REAL '-9223372036854776000' to DECIMAL(10, 2). Result overflows."); + testThrow( + REAL(), + DECIMAL(20, 2), + {static_cast(DecimalUtil::kLongDecimalMax)}, + "Cannot cast REAL '9.999999680285692E37' to DECIMAL(20, 2). Result overflows."); + testThrow( + REAL(), + DECIMAL(20, 2), + {static_cast(DecimalUtil::kLongDecimalMin)}, + "Cannot cast REAL '-9.999999680285692E37' to DECIMAL(20, 2). Result overflows."); + testThrow( + REAL(), + DECIMAL(38, 2), + {std::numeric_limits::max()}, + "Cannot cast REAL '3.4028234663852886E38' to DECIMAL(38, 2). Result overflows."); + testThrow( + REAL(), + DECIMAL(38, 2), + {std::numeric_limits::lowest()}, + "Cannot cast REAL '-3.4028234663852886E38' to DECIMAL(38, 2). Result overflows."); + testCast( + makeConstant(std::numeric_limits::min(), 1), + makeConstant(0, 1, DECIMAL(38, 2))); + + testThrow( + REAL(), + DECIMAL(38, 2), + {INFINITY}, + "Cannot cast REAL 'Infinity' to DECIMAL(38, 2). The input value should be finite."); + testThrow( + REAL(), + DECIMAL(38, 2), + {NAN}, + "Cannot cast REAL 'NaN' to DECIMAL(38, 2). The input value should be finite."); +} + TEST_F(CastExprTest, primitiveNullConstant) { // Evaluate cast(NULL::double as bigint). auto cast = diff --git a/velox/type/DecimalUtil.h b/velox/type/DecimalUtil.h index deb47cb81f2d..4721fc86925d 100644 --- a/velox/type/DecimalUtil.h +++ b/velox/type/DecimalUtil.h @@ -18,6 +18,7 @@ #include #include "velox/common/base/CheckedArithmetic.h" +#include "velox/common/base/CountBits.h" #include "velox/common/base/Exceptions.h" #include "velox/common/base/Nulls.h" #include "velox/common/base/Status.h" @@ -203,29 +204,55 @@ class DecimalUtil { return static_cast(rescaledValue); } - /// Rescales a double value to decimal value of given precision and scale. The - /// output is rescaled value of int128_t or int64_t type. Returns error status - /// if fails. - template - inline static Status - rescaleDouble(double value, int precision, int scale, TOutput& output) { + /// Rescales a floating point value to decimal value of given precision and + /// scale. The output is rescaled value of int128_t or int64_t type. Returns + /// error status if fails. + template + inline static Status rescaleFloatingPoint( + TIntput value, + int precision, + int scale, + TOutput& output) { if (!std::isfinite(value)) { return Status::UserError("The input value should be finite."); } + uint8_t digits; + if constexpr (std::is_same_v) { + // A float provides between 6 and 7 decimal digits, so at least 6 digits + // are precise. + digits = 6; + } else { + // A double provides from 15 to 17 decimal digits, so at least 15 digits + // are precise. + digits = 15; + if (value <= std::numeric_limits::min() || + value >= std::numeric_limits::max()) { + return Status::UserError("Result overflows."); + } + } + + // Calculate the precise fractional digits. + const auto integralValue = + static_cast(value > 0 ? value : -value); + const auto integralDigits = + integralValue == 0 ? 0 : countDigits(integralValue); + const auto fractionDigits = digits - integralDigits; + /// Scales up the input value to keep all the precise fractional digits + /// before rounding. Convert value to long double type, as double * int128_t + /// returns int128_t and fractional digits are lost. No need to consider + /// 'toValue' becoming infinite as DOUBLE_MAX * 10^38 < LONG_DOUBLE_MAX. + const auto scaledValue = + (long double)value * DecimalUtil::kPowersOfTen[fractionDigits]; + long double rounded; - // A double provides 16(±1) decimal digits, so at least 15 digits are - // precise. - if (scale > 15) { - // Convert value to long double type, as double * int128_t returns - // int128_t and fractional digits are lost. No need to consider 'toValue' - // becoming infinite as DOUBLE_MAX * 10^38 < LONG_DOUBLE_MAX. - const auto toValue = (long double)value * DecimalUtil::kPowersOfTen[15]; - rounded = std::round(toValue) * DecimalUtil::kPowersOfTen[scale - 15]; + if (scale > fractionDigits) { + rounded = std::round(scaledValue) * + DecimalUtil::kPowersOfTen[scale - fractionDigits]; } else { - const auto toValue = - (long double)value * DecimalUtil::kPowersOfTen[scale]; - rounded = std::round(toValue); + rounded = std::round( + std::round(scaledValue) / + DecimalUtil::kPowersOfTen[fractionDigits - scale]); } const auto result = folly::tryTo(rounded); diff --git a/velox/type/tests/DecimalTest.cpp b/velox/type/tests/DecimalTest.cpp index f0fdd570fd2e..588970113a94 100644 --- a/velox/type/tests/DecimalTest.cpp +++ b/velox/type/tests/DecimalTest.cpp @@ -23,25 +23,29 @@ namespace facebook::velox { namespace { -template -void assertRescaleDouble(double value, const TypePtr& type, T expectedValue) { +template +void assertRescaleFloatingPoint( + TInput value, + const TypePtr& type, + TOutput expectedValue) { const auto [precision, scale] = getDecimalPrecisionScale(*type); - T actualValue; - const auto status = - DecimalUtil::rescaleDouble(value, precision, scale, actualValue); + TOutput actualValue; + const auto status = DecimalUtil::rescaleFloatingPoint( + value, precision, scale, actualValue); ASSERT_TRUE(status.ok()); ASSERT_EQ(actualValue, expectedValue); } -template -void assertRescaleDoubleFail( - double value, +template +void assertRescaleFloatingPointFail( + TInput value, const TypePtr& type, const std::string& expectedErrorMessage) { const auto [precision, scale] = getDecimalPrecisionScale(*type); - T actualValue; + TOutput actualValue; VELOX_ASSERT_ERROR_STATUS( - DecimalUtil::rescaleDouble(value, precision, scale, actualValue), + (DecimalUtil::rescaleFloatingPoint( + value, precision, scale, actualValue)), StatusCode::kUserError, expectedErrorMessage); } @@ -287,84 +291,198 @@ TEST(DecimalAggregateTest, adjustSumForOverflow) { } TEST(DecimalTest, rescaleDouble) { - assertRescaleDouble(-3333.03, DECIMAL(10, 4), -33'330'300); - assertRescaleDouble(-3333.03, DECIMAL(20, 1), -33'330); - assertRescaleDouble(-3333.03, DECIMAL(20, 10), -33'330'300'000'000); - - assertRescaleDouble(-2222.02, DECIMAL(10, 4), -22'220'200); - assertRescaleDouble(-2222.02, DECIMAL(20, 1), -22'220); - assertRescaleDouble(-2222.02, DECIMAL(20, 10), -22'220'200'000'000); - - assertRescaleDouble(-1.0, DECIMAL(10, 4), -10'000); - assertRescaleDouble(-1.0, DECIMAL(20, 1), -10); - assertRescaleDouble(-1.0, DECIMAL(20, 10), -10'000'000'000); - - assertRescaleDouble(0.00, DECIMAL(10, 4), 0); - assertRescaleDouble(0.00, DECIMAL(20, 1), 0); - assertRescaleDouble(0.00, DECIMAL(20, 10), 0); - - assertRescaleDouble(100, DECIMAL(10, 4), 1'000'000); - assertRescaleDouble(100, DECIMAL(20, 1), 1'000); - assertRescaleDouble(100, DECIMAL(20, 10), 1'000'000'000'000); - - assertRescaleDouble(99999.99, DECIMAL(10, 4), 999'999'900); - assertRescaleDouble(99999.99, DECIMAL(20, 1), 1'000'000); - assertRescaleDouble(99999.99, DECIMAL(20, 10), 999'999'900'000'000); - - assertRescaleDouble( + assertRescaleFloatingPoint( + -3333.03, DECIMAL(10, 4), -33'330'300); + assertRescaleFloatingPoint( + -3333.03, DECIMAL(20, 1), -33'330); + assertRescaleFloatingPoint( + -3333.03, + DECIMAL(38, 18), + HugeInt::build(0xFFFFFFFFFFFFFF4B, 0x50EABA2657C90000)); + + assertRescaleFloatingPoint( + -2222.02, DECIMAL(10, 4), -22'220'200); + assertRescaleFloatingPoint( + -2222.02, DECIMAL(20, 1), -22'220); + assertRescaleFloatingPoint( + -2222.02, + DECIMAL(38, 18), + HugeInt::build(0xFFFFFFFFFFFFFF87, 0x8B4726C43A860000)); + + assertRescaleFloatingPoint(-1.0, DECIMAL(10, 4), -10'000); + assertRescaleFloatingPoint(-1.0, DECIMAL(20, 1), -10); + assertRescaleFloatingPoint( + -1.0, DECIMAL(38, 18), -1'000'000'000'000'000'000); + + assertRescaleFloatingPoint(0.00, DECIMAL(10, 4), 0); + assertRescaleFloatingPoint(0.00, DECIMAL(20, 1), 0); + assertRescaleFloatingPoint(0.00, DECIMAL(38, 18), 0); + + assertRescaleFloatingPoint(100, DECIMAL(10, 4), 1'000'000); + assertRescaleFloatingPoint(100, DECIMAL(20, 1), 1'000); + assertRescaleFloatingPoint( + 100, DECIMAL(38, 18), HugeInt::build(0x5, 0x6BC75E2D63100000)); + + assertRescaleFloatingPoint( + 99999.99, DECIMAL(10, 4), 999'999'900); + assertRescaleFloatingPoint( + 99999.99, DECIMAL(20, 1), 1'000'000); + assertRescaleFloatingPoint( + 99999.99, DECIMAL(38, 18), HugeInt::build(0x152D, 0x02A45A5886BF0000)); + + assertRescaleFloatingPoint(0.95, DECIMAL(3, 1), 10); + assertRescaleFloatingPoint( + 10.03, DECIMAL(38, 18), HugeInt::build(0, 0x8B31B7DBD92B0000)); + assertRescaleFloatingPoint( 0.034567890, DECIMAL(38, 18), 34'567'890'000'000'000); - assertRescaleDouble( + assertRescaleFloatingPoint( 0.999999999999999, DECIMAL(38, 18), 999'999'999'999'999'000); - assertRescaleDouble( + assertRescaleFloatingPoint( 0.123456789123123, DECIMAL(38, 18), 123'456'789'123'123'000); - assertRescaleDouble(21.54551, DECIMAL(12, 3), 21546); + assertRescaleFloatingPoint(21.54551, DECIMAL(12, 3), 21546); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( std::numeric_limits::max(), DECIMAL(38, 38), "Result overflows."); - assertRescaleDouble( + assertRescaleFloatingPoint( std::numeric_limits::min(), DECIMAL(38, 2), 0); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( std::numeric_limits::lowest(), DECIMAL(38, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( NAN, DECIMAL(10, 2), "The input value should be finite."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( INFINITY, DECIMAL(10, 2), "The input value should be finite."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( 9999999999999999999999.99, DECIMAL(10, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( static_cast( static_cast(std::numeric_limits::max()) + 1), DECIMAL(10, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( static_cast( static_cast(std::numeric_limits::min()) - 1), DECIMAL(10, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( static_cast(DecimalUtil::kShortDecimalMax), DECIMAL(10, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( static_cast(DecimalUtil::kShortDecimalMin), DECIMAL(10, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( static_cast(DecimalUtil::kLongDecimalMax), DECIMAL(20, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( static_cast(DecimalUtil::kLongDecimalMin), DECIMAL(20, 2), "Result overflows."); - assertRescaleDoubleFail( + assertRescaleFloatingPointFail( 99999.99, DECIMAL(6, 4), "Result cannot fit in the given precision 6."); } +TEST(DecimalTest, rescaleReal) { + assertRescaleFloatingPoint( + -3333.03, DECIMAL(10, 4), -33'330'300); + assertRescaleFloatingPoint( + -3333.03, DECIMAL(20, 1), -33'330); + assertRescaleFloatingPoint( + -3333.03, + DECIMAL(38, 18), + HugeInt::build(0xFFFFFFFFFFFFFF4B, 0x50EABA2657C90000)); + + assertRescaleFloatingPoint( + -2222.02, DECIMAL(10, 4), -22'220'200); + assertRescaleFloatingPoint( + -2222.02, DECIMAL(20, 1), -22'220); + assertRescaleFloatingPoint( + -2222.02, + DECIMAL(38, 18), + HugeInt::build(0xFFFFFFFFFFFFFF87, 0x8B4726C43A860000)); + + assertRescaleFloatingPoint(-1.0, DECIMAL(10, 4), -10'000); + assertRescaleFloatingPoint(-1.0, DECIMAL(20, 1), -10); + assertRescaleFloatingPoint( + -1.0, DECIMAL(38, 18), -1'000'000'000'000'000'000); + + assertRescaleFloatingPoint(0.00, DECIMAL(10, 4), 0); + assertRescaleFloatingPoint(0.00, DECIMAL(20, 1), 0); + assertRescaleFloatingPoint(0.00, DECIMAL(38, 18), 0); + + assertRescaleFloatingPoint(100, DECIMAL(10, 4), 1'000'000); + assertRescaleFloatingPoint(100, DECIMAL(20, 1), 1'000); + assertRescaleFloatingPoint( + 100, DECIMAL(38, 18), HugeInt::build(0x5, 0x6BC75E2D63100000)); + + assertRescaleFloatingPoint( + 9999.99, DECIMAL(10, 4), 99'999'900); + assertRescaleFloatingPoint(9999.99, DECIMAL(20, 1), 100'000); + assertRescaleFloatingPoint( + 9999.99, DECIMAL(38, 18), HugeInt::build(0x21E, 0x19BD42C8427F0000)); + + assertRescaleFloatingPoint(0.95, DECIMAL(3, 1), 10); + assertRescaleFloatingPoint( + 10.03, DECIMAL(38, 18), HugeInt::build(0, 0x8B31B7DBD92B0000)); + assertRescaleFloatingPoint( + 0.034567, DECIMAL(38, 18), 34'567'000'000'000'000); + assertRescaleFloatingPoint( + 0.999999999999999, DECIMAL(38, 18), 1'000'000'000'000'000'000); + assertRescaleFloatingPoint( + 0.123456, DECIMAL(38, 18), 123'456'000'000'000'000); + assertRescaleFloatingPoint(21.5455, DECIMAL(12, 3), 21546); + + assertRescaleFloatingPointFail( + std::numeric_limits::max(), DECIMAL(38, 38), "Result overflows."); + assertRescaleFloatingPoint( + std::numeric_limits::min(), DECIMAL(38, 2), 0); + assertRescaleFloatingPointFail( + std::numeric_limits::lowest(), + DECIMAL(38, 2), + "Result overflows."); + + assertRescaleFloatingPointFail( + NAN, DECIMAL(10, 2), "The input value should be finite."); + assertRescaleFloatingPointFail( + INFINITY, DECIMAL(10, 2), "The input value should be finite."); + + assertRescaleFloatingPointFail( + 9999999999999999999999.99, DECIMAL(10, 2), "Result overflows."); + assertRescaleFloatingPointFail( + static_cast( + static_cast(std::numeric_limits::max()) + 1), + DECIMAL(10, 2), + "Result overflows."); + assertRescaleFloatingPointFail( + static_cast( + static_cast(std::numeric_limits::min()) - 1), + DECIMAL(10, 2), + "Result overflows."); + assertRescaleFloatingPointFail( + static_cast(DecimalUtil::kShortDecimalMax), + DECIMAL(10, 2), + "Result overflows."); + assertRescaleFloatingPointFail( + static_cast(DecimalUtil::kShortDecimalMin), + DECIMAL(10, 2), + "Result overflows."); + assertRescaleFloatingPointFail( + static_cast(DecimalUtil::kLongDecimalMax), + DECIMAL(20, 2), + "Result overflows."); + assertRescaleFloatingPointFail( + static_cast(DecimalUtil::kLongDecimalMin), + DECIMAL(20, 2), + "Result overflows."); + + assertRescaleFloatingPointFail( + 99999.99, DECIMAL(6, 4), "Result cannot fit in the given precision 6."); +} } // namespace } // namespace facebook::velox