@@ -88,6 +88,25 @@ class DecimalArithmeticTest : public SparkFunctionBaseTest {
8888 std::optional<U> u) {
8989 return evaluateOnce<int64_t >(" checked_div(c0, c1)" , {tType, uType}, t, u);
9090 }
91+
92+ template <typename T, typename U>
93+ std::optional<int128_t > checked_add (
94+ const TypePtr& tType,
95+ const TypePtr& uType,
96+ std::optional<T> t,
97+ std::optional<U> u) {
98+ return evaluateOnce<int128_t >(" checked_add(c0, c1)" , {tType, uType}, t, u);
99+ }
100+
101+ template <typename T, typename U>
102+ std::optional<int128_t > checked_subtract (
103+ const TypePtr& tType,
104+ const TypePtr& uType,
105+ std::optional<T> t,
106+ std::optional<U> u) {
107+ return evaluateOnce<int128_t >(
108+ " checked_subtract(c0, c1)" , {tType, uType}, t, u);
109+ }
91110};
92111
93112TEST_F (DecimalArithmeticTest, add) {
@@ -833,5 +852,170 @@ TEST_F(DecimalArithmeticTest, checkedDiv) {
833852 1 )),
834853 " Overflow in integral divide" );
835854}
855+
856+ TEST_F (DecimalArithmeticTest, checkedAdd) {
857+ // Normal cases should work.
858+ // Use DECIMAL(18, 2) so result precision (19) exceeds 18 (long decimal).
859+ EXPECT_EQ (
860+ (checked_add<int64_t , int64_t >(DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 100 , 200 )),
861+ 300 );
862+ EXPECT_EQ (
863+ (checked_add<int64_t , int128_t >(
864+ DECIMAL (18 , 2 ), DECIMAL (20 , 2 ), 100 , 200 )),
865+ 300 );
866+ EXPECT_EQ (
867+ (checked_add<int128_t , int64_t >(
868+ DECIMAL (20 , 2 ), DECIMAL (18 , 2 ), 100 , 200 )),
869+ 300 );
870+ EXPECT_EQ (
871+ (checked_add<int128_t , int128_t >(
872+ DECIMAL (20 , 2 ), DECIMAL (20 , 2 ), 100 , 200 )),
873+ 300 );
874+
875+ // Adding with zero.
876+ EXPECT_EQ (
877+ (checked_add<int64_t , int64_t >(DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 0 , 100 )),
878+ 100 );
879+
880+ // Adding negative numbers.
881+ EXPECT_EQ (
882+ (checked_add<int64_t , int64_t >(DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), -100 , 50 )),
883+ -50 );
884+
885+ // Result precision capped at 38, no overflow.
886+ EXPECT_EQ (
887+ (checked_add<int128_t , int128_t >(
888+ DECIMAL (38 , 0 ), DECIMAL (38 , 0 ), 100 , 200 )),
889+ 300 );
890+
891+ // Near-boundary success: large values through addLarge path, but fits.
892+ EXPECT_EQ (
893+ (checked_add<int128_t , int128_t >(
894+ DECIMAL (38 , 0 ),
895+ DECIMAL (38 , 0 ),
896+ HugeInt::parse (" 49999999999999999999999999999999999999" ),
897+ HugeInt::parse (" 49999999999999999999999999999999999999" ))),
898+ HugeInt::parse (" 99999999999999999999999999999999999998" ));
899+
900+ // Positive overflow should throw.
901+ VELOX_ASSERT_USER_THROW (
902+ (checked_add<int128_t , int128_t >(
903+ DECIMAL (38 , 0 ),
904+ DECIMAL (38 , 0 ),
905+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
906+ HugeInt::parse (" 99999999999999999999999999999999999999" ))),
907+ " Decimal overflow in add" );
908+
909+ // Positive overflow with large positive and small positive.
910+ VELOX_ASSERT_USER_THROW (
911+ (checked_add<int128_t , int128_t >(
912+ DECIMAL (38 , 0 ),
913+ DECIMAL (38 , 0 ),
914+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
915+ 1 )),
916+ " Decimal overflow in add" );
917+
918+ // Negative overflow should throw.
919+ VELOX_ASSERT_USER_THROW (
920+ (checked_add<int128_t , int128_t >(
921+ DECIMAL (38 , 0 ),
922+ DECIMAL (38 , 0 ),
923+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
924+ HugeInt::parse (" -99999999999999999999999999999999999999" ))),
925+ " Decimal overflow in add" );
926+
927+ // Negative overflow with large negative and small negative.
928+ VELOX_ASSERT_USER_THROW (
929+ (checked_add<int128_t , int128_t >(
930+ DECIMAL (38 , 0 ),
931+ DECIMAL (38 , 0 ),
932+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
933+ -1 )),
934+ " Decimal overflow in add" );
935+ }
936+
937+ TEST_F (DecimalArithmeticTest, checkedSubtract) {
938+ // Normal cases should work.
939+ // Use DECIMAL(18, 2) so result precision (19) exceeds 18 (long decimal).
940+ EXPECT_EQ (
941+ (checked_subtract<int64_t , int64_t >(
942+ DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 300 , 200 )),
943+ 100 );
944+ EXPECT_EQ (
945+ (checked_subtract<int64_t , int128_t >(
946+ DECIMAL (18 , 2 ), DECIMAL (20 , 2 ), 300 , 200 )),
947+ 100 );
948+ EXPECT_EQ (
949+ (checked_subtract<int128_t , int64_t >(
950+ DECIMAL (20 , 2 ), DECIMAL (18 , 2 ), 300 , 200 )),
951+ 100 );
952+ EXPECT_EQ (
953+ (checked_subtract<int128_t , int128_t >(
954+ DECIMAL (20 , 2 ), DECIMAL (20 , 2 ), 300 , 200 )),
955+ 100 );
956+
957+ // Subtracting zero.
958+ EXPECT_EQ (
959+ (checked_subtract<int64_t , int64_t >(
960+ DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 100 , 0 )),
961+ 100 );
962+
963+ // Subtracting negative (effectively adding).
964+ EXPECT_EQ (
965+ (checked_subtract<int64_t , int64_t >(
966+ DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 100 , -50 )),
967+ 150 );
968+
969+ // Result precision capped at 38, no overflow.
970+ EXPECT_EQ (
971+ (checked_subtract<int128_t , int128_t >(
972+ DECIMAL (38 , 0 ), DECIMAL (38 , 0 ), 300 , 200 )),
973+ 100 );
974+
975+ // Near-boundary success: large values through addLarge path, but fits.
976+ EXPECT_EQ (
977+ (checked_subtract<int128_t , int128_t >(
978+ DECIMAL (38 , 0 ),
979+ DECIMAL (38 , 0 ),
980+ HugeInt::parse (" 49999999999999999999999999999999999999" ),
981+ HugeInt::parse (" -49999999999999999999999999999999999999" ))),
982+ HugeInt::parse (" 99999999999999999999999999999999999998" ));
983+
984+ // Negative overflow should throw.
985+ VELOX_ASSERT_USER_THROW (
986+ (checked_subtract<int128_t , int128_t >(
987+ DECIMAL (38 , 0 ),
988+ DECIMAL (38 , 0 ),
989+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
990+ HugeInt::parse (" 99999999999999999999999999999999999999" ))),
991+ " Decimal overflow in subtract" );
992+
993+ // Negative overflow with large negative and small positive.
994+ VELOX_ASSERT_USER_THROW (
995+ (checked_subtract<int128_t , int128_t >(
996+ DECIMAL (38 , 0 ),
997+ DECIMAL (38 , 0 ),
998+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
999+ 1 )),
1000+ " Decimal overflow in subtract" );
1001+
1002+ // Positive overflow should throw.
1003+ VELOX_ASSERT_USER_THROW (
1004+ (checked_subtract<int128_t , int128_t >(
1005+ DECIMAL (38 , 0 ),
1006+ DECIMAL (38 , 0 ),
1007+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
1008+ HugeInt::parse (" -99999999999999999999999999999999999999" ))),
1009+ " Decimal overflow in subtract" );
1010+
1011+ // Positive overflow with large positive and small negative.
1012+ VELOX_ASSERT_USER_THROW (
1013+ (checked_subtract<int128_t , int128_t >(
1014+ DECIMAL (38 , 0 ),
1015+ DECIMAL (38 , 0 ),
1016+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
1017+ -1 )),
1018+ " Decimal overflow in subtract" );
1019+ }
8361020} // namespace
8371021} // namespace facebook::velox::functions::sparksql::test
0 commit comments