diff --git a/bolt/expression/CastExpr-tpl.cpp b/bolt/expression/CastExpr-tpl.cpp index b027dbcf7..351d82c86 100644 --- a/bolt/expression/CastExpr-tpl.cpp +++ b/bolt/expression/CastExpr-tpl.cpp @@ -368,7 +368,7 @@ class Converter { constexpr TypeKind originKind = ToKind::storage; using LimitType = typename util:: Converter::LimitType; - if (from > LimitType::maxLimit()) { + if (LimitType::isPositiveOverflow(from)) { to = LimitType::max(); return ConvertStatus::INTEGER_OVERFLOW; } diff --git a/bolt/functions/sparksql/tests/SparkCastExprTest.cpp b/bolt/functions/sparksql/tests/SparkCastExprTest.cpp index 3cddfe5d3..49eaa8b9b 100644 --- a/bolt/functions/sparksql/tests/SparkCastExprTest.cpp +++ b/bolt/functions/sparksql/tests/SparkCastExprTest.cpp @@ -655,6 +655,18 @@ TEST_F(SparkCastExprTest, overflow) { testCast("tinyint", {127.8}, {127}); testCast("tinyint", {129.9}, {-127}); testCast("smallint", {1234567.89}, {-10617}); + testCast( + "integer", + {0x1.fffffep30f, 0x1p31f, -0x1p31f}, + {2'147'483'520, + std::numeric_limits::max(), + std::numeric_limits::min()}); + testCast( + "bigint", + {0x1.fffffffffffffp62, 0x1p63, -0x1p63}, + {9'223'372'036'854'774'784LL, + std::numeric_limits::max(), + std::numeric_limits::min()}); testCast( "bigint", {std::numeric_limits::max()}, {9223372036854775807}); testCast( diff --git a/bolt/type/Conversions.h b/bolt/type/Conversions.h index 8605ccae3..6aba61179 100644 --- a/bolt/type/Conversions.h +++ b/bolt/type/Conversions.h @@ -323,6 +323,17 @@ struct Converter< struct LimitType { static constexpr bool kByteOrSmallInt = std::is_same_v || std::is_same_v; + using FirstStageType = std::conditional_t; + + template + static bool isPositiveOverflow(const FP& value) { + // Integer max may round up to the first out-of-range value when it is + // converted to FP. The negative minimum is a power of two, so its + // negation gives an exact exclusive upper bound. + return value >= + -static_cast(std::numeric_limits::min()); + } + static int64_t minLimit() { if (kByteOrSmallInt) { return std::numeric_limits::min(); @@ -363,7 +374,7 @@ struct Converter< } if constexpr (std::is_same_v) { return std::numeric_limits::max(); - } else if (v > LimitType::maxLimit()) { + } else if (LimitType::isPositiveOverflow(v)) { return LimitType::max(); } if constexpr (std::is_same_v) { @@ -392,7 +403,7 @@ struct Converter< } if constexpr (std::is_same_v) { return std::numeric_limits::max(); - } else if (v > LimitType::maxLimit()) { + } else if (LimitType::isPositiveOverflow(v)) { return LimitType::max(); } if constexpr (std::is_same_v) { diff --git a/bolt/type/tests/ConversionsTest.cpp b/bolt/type/tests/ConversionsTest.cpp index 1a040d935..242c74722 100644 --- a/bolt/type/tests/ConversionsTest.cpp +++ b/bolt/type/tests/ConversionsTest.cpp @@ -369,6 +369,12 @@ TEST_F(ConversionsTest, toIntegeralTypes) { 12345, }, /*truncate*/ true); + testConversion( + {0x1.fffffffffffffp62, 0x1p63, -0x1p63}, + {9'223'372'036'854'774'784LL, + std::numeric_limits::max(), + std::numeric_limits::min()}, + /*truncate*/ true); testConversion( { 1.888, @@ -385,6 +391,16 @@ TEST_F(ConversionsTest, toIntegeralTypes) { -100, }, /*truncate*/ true); + testConversion( + {0x1.fffffffffffffp30, 0x1p31, -0x1p31}, + {std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min()}, + /*truncate*/ true); + testConversion( + {0x1.fffffffffffffp30, 0x1p31, -0x1p31}, + {-1, -1, 0}, + /*truncate*/ true); testConversion( { 12345.67, @@ -397,6 +413,10 @@ TEST_F(ConversionsTest, toIntegeralTypes) { 127, }, /*truncate*/ true); + testConversion( + {0x1.fffffffffffffp30, 0x1p31, -0x1p31}, + {-1, -1, 0}, + /*truncate*/ true); testConversion( { 1, @@ -440,9 +460,27 @@ TEST_F(ConversionsTest, toIntegeralTypes) { // When TRUNCATE = true. testConversion( {kInf, kNan}, {9223372036854775807, 0}, /*truncate*/ true); - testConversion({kNan}, {0}, /*truncate*/ true); - testConversion({kNan}, {0}, /*truncate*/ true); - testConversion({kNan}, {0}, /*truncate*/ true); + testConversion( + {0x1.fffffep62f, 0x1p63f, -0x1p63f}, + {9'223'371'487'098'961'920LL, + std::numeric_limits::max(), + std::numeric_limits::min()}, + /*truncate*/ true); + testConversion( + {kNan, 0x1.fffffep30f, 0x1p31f, -0x1p31f}, + {0, + 2'147'483'520, + std::numeric_limits::max(), + std::numeric_limits::min()}, + /*truncate*/ true); + testConversion( + {kNan, 0x1.fffffep30f, 0x1p31f, -0x1p31f}, + {0, -128, -1, 0}, + /*truncate*/ true); + testConversion( + {kNan, 0x1.fffffep30f, 0x1p31f, -0x1p31f}, + {0, -128, -1, 0}, + /*truncate*/ true); } // From string.