@@ -42,7 +42,7 @@ class Saturated
4242 using TP = std::remove_reference_t <T>;
4343 using TS = std::conditional_t <std::is_signed_v<T>, T, std::make_signed_t <modm::WideType<TP>>>;
4444
45- T value = 0 ;
45+ T value{ 0 } ;
4646private:
4747 static constexpr TP min = std::numeric_limits<TP>::min();
4848 static constexpr TP max = std::numeric_limits<TP>::max();
@@ -55,36 +55,63 @@ class Saturated
5555
5656 template <typename U>
5757 requires std::integral<std::remove_reference_t <U>>
58- constexpr Saturated (const U& v )
59- { value = std::clamp< modm::fits_any_t <TP, U> >(v , min, max); }
58+ constexpr Saturated (const U& value )
59+ { this -> value = std::clamp< modm::fits_any_t <TP, U> >(value , min, max); }
6060
6161 template <typename U>
6262 requires std::floating_point<std::remove_reference_t <U>>
63- constexpr Saturated (const U& v )
64- { value = std::clamp<float >(v , min, max); }
63+ constexpr Saturated (const U& value )
64+ { this -> value = std::clamp<float >(value , min, max); }
6565
6666 template <typename U>
6767 requires std::integral<std::remove_reference_t <U>>
6868 constexpr Saturated (const Saturated<U>& other)
6969 { value = std::clamp< modm::fits_any_t <TP, U> >(other.value , min, max); }
7070
71- TP
72- getValue () const
73- { return value; }
74-
75- // Implicitely serve underlying type so you can f.e. pass Saturated to std::abs()
76- operator T&() { return value; }
71+ // Implicit conversion to underlying type
72+ // @see: https://en.cppreference.com/w/cpp/language/implicit_conversion
7773 operator T () const { return value; }
7874
7975 // comparison operators
8076 constexpr auto
8177 operator <=>(const Saturated<T>&) const = default ;
8278
79+ template <typename U>
80+ constexpr bool
81+ operator ==(const U& other) const
82+ { return value == other; }
83+
84+ template <typename U>
85+ constexpr bool
86+ operator >(const U& other) const
87+ { return value > other; }
88+
89+ template <typename U>
90+ constexpr bool
91+ operator <(const U& other) const
92+ { return value < other; }
93+
94+ template <typename U>
95+ constexpr bool
96+ operator >=(const U& other) const
97+ { return value >= other; }
98+
99+ template <typename U>
100+ constexpr bool
101+ operator <=(const U& other) const
102+ { return value <= other; }
103+
83104 // operator=
84105 void
85106 operator =(const Saturated& other)
86107 { value = other.value ; }
87108
109+ template <typename U>
110+ requires std::integral<std::remove_reference_t <U>>
111+ void
112+ operator =(const U& other)
113+ { value = std::clamp< modm::fits_any_t <TP, U> >(other, min, max); }
114+
88115 template <typename U>
89116 requires std::integral<std::remove_reference_t <U>>
90117 void
@@ -124,6 +151,17 @@ class Saturated
124151 }
125152
126153 // operator+=, operator-=, operator*=
154+ template <typename U>
155+ requires std::unsigned_integral<std::remove_reference_t <U>>
156+ Saturated&
157+ operator +=(const U& other)
158+ {
159+ if (__builtin_add_overflow (value, other, &value))
160+ value = max;
161+
162+ return *this ;
163+ }
164+
127165 template <typename U>
128166 requires std::unsigned_integral<std::remove_reference_t <U>>
129167 Saturated&
@@ -135,6 +173,22 @@ class Saturated
135173 return *this ;
136174 }
137175
176+ template <typename U>
177+ requires std::signed_integral<std::remove_reference_t <U>>
178+ Saturated&
179+ operator +=(const U& other)
180+ {
181+ if (other < 0 ) {
182+ if (__builtin_sub_overflow (value, -other, &value))
183+ value = min;
184+ } else {
185+ if (__builtin_add_overflow (value, other, &value))
186+ value = max;
187+ }
188+
189+ return *this ;
190+ }
191+
138192 template <typename U>
139193 requires std::signed_integral<std::remove_reference_t <U>>
140194 Saturated&
@@ -151,6 +205,17 @@ class Saturated
151205 return *this ;
152206 }
153207
208+ template <typename U>
209+ requires std::unsigned_integral<std::remove_reference_t <U>>
210+ Saturated&
211+ operator -=(const U& other)
212+ {
213+ if (__builtin_sub_overflow (value, other, &value))
214+ value = min;
215+
216+ return *this ;
217+ }
218+
154219 template <typename U>
155220 requires std::unsigned_integral<std::remove_reference_t <U>>
156221 Saturated&
@@ -162,6 +227,22 @@ class Saturated
162227 return *this ;
163228 }
164229
230+ template <typename U>
231+ requires std::signed_integral<std::remove_reference_t <U>>
232+ Saturated&
233+ operator -=(const U& other)
234+ {
235+ if (other < 0 ) {
236+ if (__builtin_add_overflow (value, -other, &value))
237+ value = max;
238+ } else {
239+ if (__builtin_sub_overflow (value, other, &value))
240+ value = min;
241+ }
242+
243+ return *this ;
244+ }
245+
165246 template <typename U>
166247 requires std::signed_integral<std::remove_reference_t <U>>
167248 Saturated&
@@ -181,62 +262,73 @@ class Saturated
181262 template <typename U>
182263 requires std::unsigned_integral<std::remove_reference_t <U>>
183264 Saturated&
184- operator *=(const Saturated<U> & other)
265+ operator *=(const U & other)
185266 {
186- if (__builtin_mul_overflow (value, other. value , &value))
267+ if (__builtin_mul_overflow (value, other, &value))
187268 value = max;
188269
189270 return *this ;
190271 }
191272
192273 template <typename U>
193- requires std::signed_integral <std::remove_reference_t <U>>
274+ requires std::unsigned_integral <std::remove_reference_t <U>>
194275 Saturated&
195276 operator *=(const Saturated<U>& other)
196277 {
197- if (other.value < 0 ) {
198- if (__builtin_mul_overflow (value, -other.value , &value))
199- value = max;
200- value = -value;
201- } else {
202- if (__builtin_mul_overflow (value, other.value , &value))
203- value = max;
204- }
278+ if (__builtin_mul_overflow (value, other.value , &value))
279+ value = max;
205280
206281 return *this ;
207282 }
208283
209- // OPTIMIZE By whatever reason, for operator*= the compiler doesn't implicitly construct Saturated types.
210- // Overload plain types for now:
211284 template <typename U>
212- requires std::unsigned_integral <std::remove_reference_t <U>>
285+ requires std::signed_integral <std::remove_reference_t <U>>
213286 Saturated&
214- operator *=(const U& v )
287+ operator *=(const U& other )
215288 {
216- if (__builtin_mul_overflow (value, v, &value))
217- value = max;
289+ if (other < 0 ) {
290+ if (__builtin_mul_overflow (value, -other, &value))
291+ value = max;
292+ value = -value;
293+ } else {
294+ if (__builtin_mul_overflow (value, other, &value))
295+ value = max;
296+ }
218297
219298 return *this ;
220299 }
221300
222301 template <typename U>
223302 requires std::signed_integral<std::remove_reference_t <U>>
224303 Saturated&
225- operator *=(const U& v )
304+ operator *=(const Saturated<U>& other )
226305 {
227- if (v < 0 ) {
228- if (__builtin_mul_overflow (value, -v , &value))
306+ if (other. value < 0 ) {
307+ if (__builtin_mul_overflow (value, -other. value , &value))
229308 value = max;
230309 value = -value;
231310 } else {
232- if (__builtin_mul_overflow (value, v , &value))
311+ if (__builtin_mul_overflow (value, other. value , &value))
233312 value = max;
234313 }
235314
236315 return *this ;
237316 }
238317
239318 // operator+, operator-, operator*
319+ template <typename U>
320+ requires std::unsigned_integral<std::remove_reference_t <U>>
321+ TP
322+ operator +(const U& other)
323+ {
324+ Saturated<TP> tmp;
325+
326+ if (__builtin_add_overflow (value, other, &tmp.value ))
327+ tmp.value = max;
328+
329+ return tmp.value ;
330+ }
331+
240332 template <typename U>
241333 requires std::unsigned_integral<std::remove_reference_t <U>>
242334 TP
@@ -250,6 +342,24 @@ class Saturated
250342 return tmp.value ;
251343 }
252344
345+ template <typename U>
346+ requires std::signed_integral<std::remove_reference_t <U>>
347+ TP
348+ operator +(const U& other)
349+ {
350+ Saturated<TP> tmp;
351+
352+ if (other < 0 ) {
353+ if (__builtin_sub_overflow (value, -other, &tmp.value ))
354+ tmp.value = min;
355+ } else {
356+ if (__builtin_add_overflow (value, other, &tmp.value ))
357+ tmp.value = max;
358+ }
359+
360+ return tmp.value ;
361+ }
362+
253363 template <typename U>
254364 requires std::signed_integral<std::remove_reference_t <U>>
255365 TP
@@ -268,6 +378,19 @@ class Saturated
268378 return tmp.value ;
269379 }
270380
381+ template <typename U>
382+ requires std::unsigned_integral<std::remove_reference_t <U>>
383+ TP
384+ operator -(const U& other)
385+ {
386+ Saturated<TP> tmp;
387+
388+ if (__builtin_sub_overflow (value, other, &tmp.value ))
389+ tmp.value = min;
390+
391+ return tmp.value ;
392+ }
393+
271394 template <typename U>
272395 requires std::unsigned_integral<std::remove_reference_t <U>>
273396 TP
@@ -281,6 +404,24 @@ class Saturated
281404 return tmp.value ;
282405 }
283406
407+ template <typename U>
408+ requires std::signed_integral<std::remove_reference_t <U>>
409+ TP
410+ operator -(const U& other)
411+ {
412+ Saturated<TP> tmp;
413+
414+ if (other < 0 ) {
415+ if (__builtin_add_overflow (value, -other, &tmp.value ))
416+ tmp.value = max;
417+ } else {
418+ if (__builtin_sub_overflow (value, other, &tmp.value ))
419+ tmp.value = min;
420+ }
421+
422+ return tmp.value ;
423+ }
424+
284425 template <typename U>
285426 requires std::signed_integral<std::remove_reference_t <U>>
286427 TP
0 commit comments