@@ -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,168 @@ 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 >(DECIMAL (18 , 2 ), DECIMAL (20 , 2 ), 100 , 200 )),
864+ 300 );
865+ EXPECT_EQ (
866+ (checked_add<int128_t , int64_t >(DECIMAL (20 , 2 ), DECIMAL (18 , 2 ), 100 , 200 )),
867+ 300 );
868+ EXPECT_EQ (
869+ (checked_add<int128_t , int128_t >(
870+ DECIMAL (20 , 2 ), DECIMAL (20 , 2 ), 100 , 200 )),
871+ 300 );
872+
873+ // Adding with zero.
874+ EXPECT_EQ (
875+ (checked_add<int64_t , int64_t >(DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 0 , 100 )),
876+ 100 );
877+
878+ // Adding negative numbers.
879+ EXPECT_EQ (
880+ (checked_add<int64_t , int64_t >(DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), -100 , 50 )),
881+ -50 );
882+
883+ // Result precision capped at 38, no overflow.
884+ EXPECT_EQ (
885+ (checked_add<int128_t , int128_t >(
886+ DECIMAL (38 , 0 ), DECIMAL (38 , 0 ), 100 , 200 )),
887+ 300 );
888+
889+ // Near-boundary success: large values through addLarge path, but fits.
890+ EXPECT_EQ (
891+ (checked_add<int128_t , int128_t >(
892+ DECIMAL (38 , 0 ),
893+ DECIMAL (38 , 0 ),
894+ HugeInt::parse (" 49999999999999999999999999999999999999" ),
895+ HugeInt::parse (" 49999999999999999999999999999999999999" ))),
896+ HugeInt::parse (" 99999999999999999999999999999999999998" ));
897+
898+ // Positive overflow should throw.
899+ VELOX_ASSERT_USER_THROW (
900+ (checked_add<int128_t , int128_t >(
901+ DECIMAL (38 , 0 ),
902+ DECIMAL (38 , 0 ),
903+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
904+ HugeInt::parse (" 99999999999999999999999999999999999999" ))),
905+ " Decimal overflow in add" );
906+
907+ // Positive overflow with large positive and small positive.
908+ VELOX_ASSERT_USER_THROW (
909+ (checked_add<int128_t , int128_t >(
910+ DECIMAL (38 , 0 ),
911+ DECIMAL (38 , 0 ),
912+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
913+ 1 )),
914+ " Decimal overflow in add" );
915+
916+ // Negative overflow should throw.
917+ VELOX_ASSERT_USER_THROW (
918+ (checked_add<int128_t , int128_t >(
919+ DECIMAL (38 , 0 ),
920+ DECIMAL (38 , 0 ),
921+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
922+ HugeInt::parse (" -99999999999999999999999999999999999999" ))),
923+ " Decimal overflow in add" );
924+
925+ // Negative overflow with large negative and small negative.
926+ VELOX_ASSERT_USER_THROW (
927+ (checked_add<int128_t , int128_t >(
928+ DECIMAL (38 , 0 ),
929+ DECIMAL (38 , 0 ),
930+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
931+ -1 )),
932+ " Decimal overflow in add" );
933+ }
934+
935+ TEST_F (DecimalArithmeticTest, checkedSubtract) {
936+ // Normal cases should work.
937+ // Use DECIMAL(18, 2) so result precision (19) exceeds 18 (long decimal).
938+ EXPECT_EQ (
939+ (checked_subtract<int64_t , int64_t >(
940+ DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 300 , 200 )),
941+ 100 );
942+ EXPECT_EQ (
943+ (checked_subtract<int64_t , int128_t >(
944+ DECIMAL (18 , 2 ), DECIMAL (20 , 2 ), 300 , 200 )),
945+ 100 );
946+ EXPECT_EQ (
947+ (checked_subtract<int128_t , int64_t >(
948+ DECIMAL (20 , 2 ), DECIMAL (18 , 2 ), 300 , 200 )),
949+ 100 );
950+ EXPECT_EQ (
951+ (checked_subtract<int128_t , int128_t >(
952+ DECIMAL (20 , 2 ), DECIMAL (20 , 2 ), 300 , 200 )),
953+ 100 );
954+
955+ // Subtracting zero.
956+ EXPECT_EQ (
957+ (checked_subtract<int64_t , int64_t >(
958+ DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 100 , 0 )),
959+ 100 );
960+
961+ // Subtracting negative (effectively adding).
962+ EXPECT_EQ (
963+ (checked_subtract<int64_t , int64_t >(
964+ DECIMAL (18 , 2 ), DECIMAL (18 , 2 ), 100 , -50 )),
965+ 150 );
966+
967+ // Result precision capped at 38, no overflow.
968+ EXPECT_EQ (
969+ (checked_subtract<int128_t , int128_t >(
970+ DECIMAL (38 , 0 ), DECIMAL (38 , 0 ), 300 , 200 )),
971+ 100 );
972+
973+ // Near-boundary success: large values through addLarge path, but fits.
974+ EXPECT_EQ (
975+ (checked_subtract<int128_t , int128_t >(
976+ DECIMAL (38 , 0 ),
977+ DECIMAL (38 , 0 ),
978+ HugeInt::parse (" 49999999999999999999999999999999999999" ),
979+ HugeInt::parse (" -49999999999999999999999999999999999999" ))),
980+ HugeInt::parse (" 99999999999999999999999999999999999998" ));
981+
982+ // Negative overflow should throw.
983+ VELOX_ASSERT_USER_THROW (
984+ (checked_subtract<int128_t , int128_t >(
985+ DECIMAL (38 , 0 ),
986+ DECIMAL (38 , 0 ),
987+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
988+ HugeInt::parse (" 99999999999999999999999999999999999999" ))),
989+ " Decimal overflow in subtract" );
990+
991+ // Negative overflow with large negative and small positive.
992+ VELOX_ASSERT_USER_THROW (
993+ (checked_subtract<int128_t , int128_t >(
994+ DECIMAL (38 , 0 ),
995+ DECIMAL (38 , 0 ),
996+ HugeInt::parse (" -99999999999999999999999999999999999999" ),
997+ 1 )),
998+ " Decimal overflow in subtract" );
999+
1000+ // Positive overflow should throw.
1001+ VELOX_ASSERT_USER_THROW (
1002+ (checked_subtract<int128_t , int128_t >(
1003+ DECIMAL (38 , 0 ),
1004+ DECIMAL (38 , 0 ),
1005+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
1006+ HugeInt::parse (" -99999999999999999999999999999999999999" ))),
1007+ " Decimal overflow in subtract" );
1008+
1009+ // Positive overflow with large positive and small negative.
1010+ VELOX_ASSERT_USER_THROW (
1011+ (checked_subtract<int128_t , int128_t >(
1012+ DECIMAL (38 , 0 ),
1013+ DECIMAL (38 , 0 ),
1014+ HugeInt::parse (" 99999999999999999999999999999999999999" ),
1015+ -1 )),
1016+ " Decimal overflow in subtract" );
1017+ }
8361018} // namespace
8371019} // namespace facebook::velox::functions::sparksql::test
0 commit comments