66
77#include < cstdint>
88#include < immintrin.h>
9+ #include < stdexcept>
910
1011class uint128_t
1112{
1213public:
13- uint64_t low;
14- uint64_t high;
14+ uint64_t low = 0 ;
15+ uint64_t high = 0 ;
1516
16- // --- Constructors ---
17- constexpr uint128_t ()
18- : low(0 )
19- , high(0 )
20- {
21- }
2217 constexpr uint128_t (uint64_t l)
2318 : low(l)
2419 , high(0 )
@@ -31,18 +26,20 @@ class uint128_t
3126 }
3227
3328 // --- Type Conversions ---
34- explicit operator bool () const { return low || high; }
35- explicit operator uint64_t () const { return low; }
36- explicit operator int64_t () const { return static_cast <int64_t >(low); }
29+ constexpr explicit operator bool () const { return low || high; }
30+ constexpr explicit operator uint64_t () const { return low; }
31+ constexpr explicit operator int64_t () const
32+ {
33+ return static_cast <int64_t >(low);
34+ }
3735
3836 // --- Arithmetic Operators ---
3937
40- // Addition
4138 uint128_t operator +(const uint128_t & rhs) const
4239 {
43- uint128_t result;
44- unsigned char carry = _addcarry_u64 (0 , low, rhs.low , &result.low );
45- _addcarry_u64 (carry, high, rhs.high , &result.high );
40+ uint128_t result = 0 ;
41+ const unsigned char carry = _addcarry_u64 (0 , low, rhs.low , &result.low );
42+ std::ignore = _addcarry_u64 (carry, high, rhs.high , &result.high );
4643 return result;
4744 }
4845
@@ -52,11 +49,10 @@ class uint128_t
5249 return *this ;
5350 }
5451
55- // Subtraction
5652 uint128_t operator -(const uint128_t & rhs) const
5753 {
58- uint128_t result;
59- unsigned char borrow = _subborrow_u64 (0 , low, rhs.low , &result.low );
54+ uint128_t result = 0 ;
55+ const unsigned char borrow = _subborrow_u64 (0 , low, rhs.low , &result.low );
6056 _subborrow_u64 (borrow, high, rhs.high , &result.high );
6157 return result;
6258 }
@@ -67,16 +63,11 @@ class uint128_t
6763 return *this ;
6864 }
6965
70- // Multiplication
7166 uint128_t operator *(const uint128_t & rhs) const
7267 {
73- uint64_t product_high;
74- uint64_t product_low = _umul128 (low, rhs.low , &product_high);
75-
76- // The total high part is the high part of (low * rhs.low)
77- // plus the cross terms (low * rhs.high) and (high * rhs.low)
68+ uint64_t product_high = 0 ;
69+ const uint64_t product_low = _umul128 (low, rhs.low , &product_high);
7870 product_high += (low * rhs.high ) + (high * rhs.low );
79-
8071 return uint128_t (product_low, product_high);
8172 }
8273
@@ -86,50 +77,33 @@ class uint128_t
8677 return *this ;
8778 }
8879
89- // Division (Note: Full 128-bit division is complex to implement purely with
90- // intrinsics if the divisor is > 64 bits. This is a simplified version
91- // handling common cases). For production-grade full 128/128 division, usage
92- // of a library like Boost is strongly advised. However, if divisor fits in 64
93- // bits, we can use _udiv128.
80+ // Division by a 64-bit divisor uses _udiv128. For a full 128-bit divisor,
81+ // falls back to binary long division.
9482 uint128_t operator /(const uint128_t & rhs) const
9583 {
84+ if (!rhs) {
85+ throw std::domain_error (" uint128 division by zero" );
86+ }
9687 if (rhs.high == 0 ) {
97- // Optimization for 64-bit divisor
98- uint64_t remainder;
99- uint64_t quotient_high = 0 ; // High part of result
100- uint64_t quotient_low;
101-
102- // If our high part is distinct, we divide the high part first
10388 if (high > 0 ) {
104- // This is slightly tricky with _udiv128 directly as it does 128/64
105- // -> 64. Standard long division algorithm is safer here for the general
106- // implementation. For simplicity in this snippet, we will fallback to a
107- // naive loop or simple approximation OR promote strictly the 64-bit
108- // divisor case which is most common:
109-
110- quotient_high = high / rhs.low ;
111- uint64_t r_high = high % rhs.low ;
112-
113- quotient_low = _udiv128 (r_high, low, rhs.low , &remainder);
89+ const uint64_t quotient_high = high / rhs.low ;
90+ const uint64_t r_high = high % rhs.low ;
91+ uint64_t remainder = 0 ;
92+ const uint64_t quotient_low =
93+ _udiv128 (r_high, low, rhs.low , &remainder);
11494 return uint128_t (quotient_low, quotient_high);
115- } else {
116- return uint128_t (low / rhs.low , 0 );
11795 }
96+ return uint128_t (low / rhs.low , 0 );
11897 }
119- // Fallback for full 128-bit divisor: Very slow basic binary long division
98+ // Binary long division for 128-bit divisor
12099 if (rhs > *this )
121100 return uint128_t (0 );
122101 if (rhs == *this )
123102 return uint128_t (1 );
124103
125104 uint128_t temp = *this ;
126105 uint128_t quot = 0 ;
127- uint128_t one = 1 ;
128-
129- // This is slow O(N) division, acceptable for simple utility, bad for heavy
130- // math
131106 while (temp >= rhs) {
132- // Find shift
133107 uint128_t shift_rhs = rhs;
134108 uint128_t shift_count = 1 ;
135109 while ((shift_rhs.high & 0x8000000000000000 ) == 0 &&
@@ -143,7 +117,6 @@ class uint128_t
143117 return quot;
144118 }
145119
146- // Modulus
147120 uint128_t operator %(const uint128_t & rhs) const
148121 {
149122 return *this - (*this / rhs) * rhs;
@@ -156,10 +129,9 @@ class uint128_t
156129 }
157130
158131 // --- Bitwise Operators ---
159- uint128_t operator <<(int shift) const
132+ constexpr uint128_t operator <<(int shift) const
160133 {
161- shift &= 127 ; // Mask the shift amount to imitate native hardware behavior
162- // (modulo 128)
134+ shift &= 127 ; // wrap modulo 128, matching hardware behavior
163135 if (shift == 0 )
164136 return *this ;
165137 if (shift >= 64 ) {
@@ -168,10 +140,9 @@ class uint128_t
168140 return uint128_t ((low << shift), (high << shift) | (low >> (64 - shift)));
169141 }
170142
171- uint128_t operator >>(int shift) const
143+ constexpr uint128_t operator >>(int shift) const
172144 {
173- shift &= 127 ; // Mask the shift amount to imitate native hardware behavior
174- // (modulo 128)
145+ shift &= 127 ; // wrap modulo 128, matching hardware behavior
175146 if (shift == 0 )
176147 return *this ;
177148 if (shift >= 64 ) {
@@ -181,56 +152,72 @@ class uint128_t
181152 }
182153
183154 // --- Shift by uint128_t Overloads ---
184- uint128_t operator >>(const uint128_t & shift) const
155+ constexpr uint128_t operator >>(const uint128_t & shift) const
185156 {
186- // If shift amount is >= 128, the result behavior mimics hardware (modulo
187- // 128)
188157 return *this >> static_cast <int >(shift.low );
189158 }
190159
191- uint128_t operator <<(const uint128_t & shift) const
160+ constexpr uint128_t operator <<(const uint128_t & shift) const
192161 {
193- // If shift amount is >= 128, the result behavior mimics hardware (modulo
194- // 128)
195162 return *this << static_cast <int >(shift.low );
196163 }
197164
198- uint128_t & operator <<=(int shift)
165+ constexpr uint128_t & operator <<=(int shift)
199166 {
200167 *this = *this << shift;
201168 return *this ;
202169 }
203- uint128_t & operator >>=(int shift)
170+ constexpr uint128_t & operator >>=(int shift)
204171 {
205172 *this = *this >> shift;
206173 return *this ;
207174 }
208175
209- uint128_t operator |(const uint128_t & rhs) const
176+ constexpr uint128_t & operator <<=(const uint128_t & shift)
177+ {
178+ *this = *this << shift;
179+ return *this ;
180+ }
181+ constexpr uint128_t & operator >>=(const uint128_t & shift)
182+ {
183+ *this = *this >> shift;
184+ return *this ;
185+ }
186+
187+ constexpr uint128_t operator |(const uint128_t & rhs) const
210188 {
211189 return uint128_t (low | rhs.low , high | rhs.high );
212190 }
213- uint128_t operator &(const uint128_t & rhs) const
191+ constexpr uint128_t operator &(const uint128_t & rhs) const
214192 {
215193 return uint128_t (low & rhs.low , high & rhs.high );
216194 }
217- uint128_t operator ^(const uint128_t & rhs) const
195+ constexpr uint128_t operator ^(const uint128_t & rhs) const
218196 {
219197 return uint128_t (low ^ rhs.low , high ^ rhs.high );
220198 }
221- uint128_t operator ~() const { return uint128_t (~low, ~high); }
199+ constexpr uint128_t operator ~() const { return uint128_t (~low, ~high); }
222200
223201 // --- Comparison Operators ---
224- bool operator ==(const uint128_t & rhs) const
202+ constexpr bool operator ==(const uint128_t & rhs) const
225203 {
226204 return low == rhs.low && high == rhs.high ;
227205 }
228- bool operator !=(const uint128_t & rhs) const { return !(*this == rhs); }
229- bool operator <(const uint128_t & rhs) const
206+ constexpr bool operator !=(const uint128_t & rhs) const
207+ {
208+ return !(*this == rhs);
209+ }
210+ constexpr bool operator <(const uint128_t & rhs) const
230211 {
231212 return high < rhs.high || (high == rhs.high && low < rhs.low );
232213 }
233- bool operator >(const uint128_t & rhs) const { return rhs < *this ; }
234- bool operator <=(const uint128_t & rhs) const { return !(*this > rhs); }
235- bool operator >=(const uint128_t & rhs) const { return !(*this < rhs); }
214+ constexpr bool operator >(const uint128_t & rhs) const { return rhs < *this ; }
215+ constexpr bool operator <=(const uint128_t & rhs) const
216+ {
217+ return !(*this > rhs);
218+ }
219+ constexpr bool operator >=(const uint128_t & rhs) const
220+ {
221+ return !(*this < rhs);
222+ }
236223};
0 commit comments