@@ -12,6 +12,38 @@ using u128 = uint128_t;
1212#define MAKE_U128 (low, high ) u128 (low, high)
1313#define CHECK_HIGH (val, expected ) CHECK((val).high == (expected))
1414#define CHECK_LOW (val, expected ) CHECK((val).low == (expected))
15+
16+ // Compile-time checks: verify constexpr operators are truly constexpr
17+ static_assert (uint128_t (0 ) == uint128_t (0 ), " == must be constexpr" );
18+ static_assert (uint128_t (1 ) != uint128_t (2 ), " != must be constexpr" );
19+ static_assert (uint128_t (1 ) < uint128_t (2 ), " < must be constexpr" );
20+ static_assert (uint128_t (2 ) > uint128_t (1 ), "> must be constexpr ");
21+ static_assert (uint128_t (1 ) <= uint128_t (1 ), " <= must be constexpr" );
22+ static_assert (uint128_t (1 ) >= uint128_t (1 ), " >= must be constexpr" );
23+ static_assert (static_cast <bool >(uint128_t (1 )),
24+ " operator bool must be constexpr" );
25+ static_assert (!static_cast <bool >(uint128_t (0 )),
26+ " operator bool(0) must be constexpr" );
27+ static_assert (static_cast <uint64_t >(uint128_t (42 )) == 42 ,
28+ " operator uint64_t must be constexpr" );
29+ static_assert (static_cast <int64_t >(uint128_t (7 )) == 7 ,
30+ " operator int64_t must be constexpr" );
31+ static_assert ((uint128_t (0xFF ) | uint128_t (0x100 )) == uint128_t (0x1FF ),
32+ " | must be constexpr" );
33+ static_assert ((uint128_t (0xFF ) & uint128_t (0x0F )) == uint128_t(0x0F ),
34+ "& must be constexpr ");
35+ static_assert ((uint128_t (0xFF ) ^ uint128_t (0x0F )) == uint128_t (0xF0 ),
36+ " ^ must be constexpr" );
37+ static_assert ((~uint128_t (0 )) ==
38+ uint128_t (0xFFFFFFFFFFFFFFFF , 0xFFFFFFFFFFFFFFFF ),
39+ "~ must be constexpr ");
40+ static_assert ((uint128_t (1 ) << 4 ) == uint128_t (16 ), " << int must be constexpr" );
41+ static_assert ((uint128_t (16 ) >> 4 ) == uint128_t (1 ), " >> int must be constexpr" );
42+ static_assert ((uint128_t (1 ) << uint128_t (4 )) == uint128_t (16 ),
43+ " << u128 must be constexpr" );
44+ static_assert ((uint128_t (16 ) >> uint128_t (4 )) == uint128_t(1 ),
45+ ">> u128 must be constexpr ");
46+
1547#else
1648using u128 = __uint128_t ;
1749#define MAKE_U128 (low, high ) ((u128 (high) << 64 ) | low)
@@ -202,3 +234,101 @@ TEST_CASE("String Output (Decimal)", "[uint128][print]")
202234 ss << big;
203235 REQUIRE (ss.str () == " 18446744073709551616" );
204236}
237+
238+ TEST_CASE (" Division by Zero Guard" , " [uint128][division][error]" )
239+ {
240+ #if defined(_MSC_VER)
241+ u128 numerator (100 );
242+ u128 zero (0 );
243+
244+ // Division by zero should throw std::domain_error
245+ REQUIRE_THROWS_AS (numerator / zero, std::domain_error);
246+
247+ // Test with zero constructed from MAKE_U128
248+ u128 zero_via_macro = MAKE_U128 (0 , 0 );
249+ REQUIRE_THROWS_AS (numerator / zero_via_macro, std::domain_error);
250+
251+ // Valid division should not throw
252+ u128 ten (10 );
253+ REQUIRE_NOTHROW (numerator / ten);
254+ #endif
255+ }
256+
257+ TEST_CASE (" Compound Shift Operators with uint128_t" ,
258+ " [uint128][shift][compound]" )
259+ {
260+ SECTION (" Left Shift Compound Operator (uint128_t)" )
261+ {
262+ u128 val = u128 (1 );
263+ u128 shift_amt = u128 (1 );
264+
265+ // val <<= shift_amt
266+ val <<= shift_amt;
267+ REQUIRE (val == u128 (2 )); // 1 << 1 = 2
268+
269+ // Test crossing boundary
270+ u128 val2 = u128 (1 );
271+ u128 shift_64 = u128 (64 );
272+ val2 <<= shift_64;
273+ CHECK_HIGH (val2, 1 );
274+ CHECK_LOW (val2, 0 );
275+ }
276+
277+ SECTION (" Right Shift Compound Operator (uint128_t)" )
278+ {
279+ u128 val = MAKE_U128 (0 , 1 ); // high=1, low=0 (represents 2^64)
280+ u128 shift_amt = u128 (1 );
281+
282+ // val >>= shift_amt
283+ val >>= shift_amt;
284+ CHECK_HIGH (val, 0 );
285+ CHECK_LOW (val, (1ULL << 63 )); // 2^63
286+
287+ // Test crossing boundary with larger shift
288+ u128 val2 = MAKE_U128 (0 , 1 );
289+ u128 shift_64 = u128 (64 );
290+ val2 >>= shift_64;
291+ CHECK_HIGH (val2, 0 );
292+ CHECK_LOW (val2, 1 );
293+ }
294+
295+ SECTION (" Chained Shift Operations (uint128_t)" )
296+ {
297+ u128 val = u128 (1 );
298+ u128 shift1 = u128 (3 );
299+ u128 shift2 = u128 (2 );
300+
301+ // (1 << 3) << 2 = 1 << 5 = 32
302+ val <<= shift1;
303+ val <<= shift2;
304+ REQUIRE (val == u128 (32 ));
305+ }
306+
307+ SECTION (" Large Shift via uint128_t" )
308+ {
309+ u128 one (1 );
310+ u128 shift_100 (100 );
311+
312+ one <<= shift_100;
313+ // 1 << 100 results in high bit (1 << (100-64)) = 1 << 36
314+ CHECK_HIGH (one, (1ULL << 36 ));
315+ CHECK_LOW (one, 0 );
316+ }
317+
318+ SECTION (" Over-shift Behavior (uint128_t)" )
319+ {
320+ #if defined(_MSC_VER)
321+ u128 pattern = MAKE_U128 (0xFF , 0xFF );
322+ u128 huge_shift (128 );
323+
324+ // pattern <<= 128 should wrap (modulo 128)
325+ pattern <<= huge_shift;
326+ REQUIRE (pattern == MAKE_U128 (0xFF , 0xFF )); // No effective change
327+
328+ // Test right shift over-shift
329+ u128 pattern2 = MAKE_U128 (0xFF , 0xFF );
330+ pattern2 >>= huge_shift;
331+ REQUIRE (pattern2 == MAKE_U128 (0xFF , 0xFF )); // No effective change
332+ #endif
333+ }
334+ }
0 commit comments