@@ -30,7 +30,6 @@ namespace modm
3030 * @see https://en.wikipedia.org/wiki/Saturation_arithmetic
3131 *
3232 * @author Thomas Sommer
33- *
3433 * @ingroup modm_math_saturated
3534 */
3635
@@ -42,7 +41,7 @@ class Saturated
4241 using TP = std::remove_reference_t <T>;
4342 using TS = std::conditional_t <std::is_signed_v<T>, T, std::make_signed_t <modm::WideType<TP>>>;
4443
45- T value = 0 ;
44+ T value{ 0 } ;
4645private:
4746 static constexpr TP min = std::numeric_limits<TP>::min();
4847 static constexpr TP max = std::numeric_limits<TP>::max();
@@ -55,36 +54,35 @@ class Saturated
5554
5655 template <typename U>
5756 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); }
57+ constexpr Saturated (const U& value )
58+ { this -> value = std::clamp< modm::fits_any_t <TP, U> >(value , min, max); }
6059
6160 template <typename U>
6261 requires std::floating_point<std::remove_reference_t <U>>
63- constexpr Saturated (const U& v )
64- { value = std::clamp<float >(v , min, max); }
62+ constexpr Saturated (const U& value )
63+ { this -> value = std::clamp<float >(value , min, max); }
6564
6665 template <typename U>
6766 requires std::integral<std::remove_reference_t <U>>
6867 constexpr Saturated (const Saturated<U>& other)
6968 { value = std::clamp< modm::fits_any_t <TP, U> >(other.value , min, max); }
7069
71- TP
72- getValue () const
70+ // Cast to underlying type. No need to define getters and comparison operators.
71+ // @see https://en.cppreference.com/w/cpp/language/cast_operator
72+ operator T () const
7373 { return value; }
7474
75- // Implicitely serve underlying type so you can f.e. pass Saturated to std::abs()
76- operator T&() { return value; }
77- operator T () const { return value; }
78-
79- // comparison operators
80- constexpr auto
81- operator <=>(const Saturated<T>&) const = default ;
82-
8375 // operator=
8476 void
8577 operator =(const Saturated& other)
8678 { value = other.value ; }
8779
80+ template <typename U>
81+ requires std::integral<std::remove_reference_t <U>>
82+ void
83+ operator =(const U& other)
84+ { value = std::clamp< modm::fits_any_t <TP, U> >(other, min, max); }
85+
8886 template <typename U>
8987 requires std::integral<std::remove_reference_t <U>>
9088 void
@@ -110,26 +108,26 @@ class Saturated
110108 Saturated
111109 operator ++(int )
112110 {
113- Saturated tmp (*this );
111+ Saturated ret (*this );
114112 if (value < max) value++;
115- return tmp ;
113+ return ret ;
116114 }
117115
118116 Saturated
119117 operator --(int )
120118 {
121- Saturated tmp (*this );
119+ Saturated ret (*this );
122120 if (value > min) value--;
123- return tmp ;
121+ return ret ;
124122 }
125123
126124 // operator+=, operator-=, operator*=
127125 template <typename U>
128126 requires std::unsigned_integral<std::remove_reference_t <U>>
129127 Saturated&
130- operator +=(const Saturated<U> & other)
128+ operator +=(const U & other)
131129 {
132- if (__builtin_add_overflow (value, other. value , &value))
130+ if (__builtin_add_overflow (value, other, &value))
133131 value = max;
134132
135133 return *this ;
@@ -138,25 +136,31 @@ class Saturated
138136 template <typename U>
139137 requires std::signed_integral<std::remove_reference_t <U>>
140138 Saturated&
141- operator +=(const Saturated<U> & other)
139+ operator +=(const U & other)
142140 {
143- if (other. value < 0 ) {
144- if (__builtin_sub_overflow (value, -other. value , &value))
141+ if (other < 0 ) {
142+ if (__builtin_sub_overflow (value, -other, &value))
145143 value = min;
146144 } else {
147- if (__builtin_add_overflow (value, other. value , &value))
145+ if (__builtin_add_overflow (value, other, &value))
148146 value = max;
149147 }
150148
151149 return *this ;
152150 }
153151
152+ template <typename U>
153+ requires std::integral<std::remove_reference_t <U>>
154+ Saturated&
155+ operator +=(const Saturated<U>& other)
156+ { return this ->operator +=(other.value ); }
157+
154158 template <typename U>
155159 requires std::unsigned_integral<std::remove_reference_t <U>>
156160 Saturated&
157- operator -=(const Saturated<U> & other)
161+ operator -=(const U & other)
158162 {
159- if (__builtin_sub_overflow (value, other. value , &value))
163+ if (__builtin_sub_overflow (value, other, &value))
160164 value = min;
161165
162166 return *this ;
@@ -165,25 +169,31 @@ class Saturated
165169 template <typename U>
166170 requires std::signed_integral<std::remove_reference_t <U>>
167171 Saturated&
168- operator -=(const Saturated<U> & other)
172+ operator -=(const U & other)
169173 {
170- if (other. value < 0 ) {
171- if (__builtin_add_overflow (value, -other. value , &value))
174+ if (other < 0 ) {
175+ if (__builtin_add_overflow (value, -other, &value))
172176 value = max;
173177 } else {
174- if (__builtin_sub_overflow (value, other. value , &value))
178+ if (__builtin_sub_overflow (value, other, &value))
175179 value = min;
176180 }
177181
178182 return *this ;
179183 }
180184
185+ template <typename U>
186+ requires std::integral<std::remove_reference_t <U>>
187+ Saturated&
188+ operator -=(const Saturated<U>& other)
189+ { return this ->operator -=(other.value ); }
190+
181191 template <typename U>
182192 requires std::unsigned_integral<std::remove_reference_t <U>>
183193 Saturated&
184- operator *=(const Saturated<U> & other)
194+ operator *=(const U & other)
185195 {
186- if (__builtin_mul_overflow (value, other. value , &value))
196+ if (__builtin_mul_overflow (value, other, &value))
187197 value = max;
188198
189199 return *this ;
@@ -192,126 +202,156 @@ class Saturated
192202 template <typename U>
193203 requires std::signed_integral<std::remove_reference_t <U>>
194204 Saturated&
195- operator *=(const Saturated<U> & other)
205+ operator *=(const U & other)
196206 {
197- if (other. value < 0 ) {
198- if (__builtin_mul_overflow (value, -other. value , &value))
207+ if (other < 0 ) {
208+ if (__builtin_mul_overflow (value, -other, &value))
199209 value = max;
200210 value = -value;
201211 } else {
202- if (__builtin_mul_overflow (value, other. value , &value))
212+ if (__builtin_mul_overflow (value, other, &value))
203213 value = max;
204214 }
205215
206216 return *this ;
207217 }
208218
209- // OPTIMIZE By whatever reason, for operator*= the compiler doesn't implicitly construct Saturated types.
210- // Overload plain types for now:
211219 template <typename U>
212- requires std::unsigned_integral <std::remove_reference_t <U>>
220+ requires std::integral <std::remove_reference_t <U>>
213221 Saturated&
214- operator *=(const U& v)
222+ operator *=(const Saturated<U>& other)
223+ { return this ->operator *=(other.value ); }
224+
225+ // operator+, operator-, operator*
226+ template <typename U>
227+ requires std::unsigned_integral<std::remove_reference_t <U>>
228+ TP
229+ operator +(const U& other) const
215230 {
216- if (__builtin_mul_overflow (value, v, &value))
217- value = max;
231+ Saturated<TP> ret;
218232
219- return *this ;
233+ if (__builtin_add_overflow (value, other, &ret.value ))
234+ ret.value = max;
235+
236+ return ret;
220237 }
221238
222239 template <typename U>
223240 requires std::signed_integral<std::remove_reference_t <U>>
224- Saturated&
225- operator *= (const U& v)
241+ TP
242+ operator + (const U& other) const
226243 {
227- if (v < 0 ) {
228- if (__builtin_mul_overflow (value, -v, &value))
229- value = max;
230- value = -value;
244+ Saturated<TP> ret;
245+
246+ if (other < 0 ) {
247+ if (__builtin_sub_overflow (value, -other, &ret.value ))
248+ ret.value = min;
231249 } else {
232- if (__builtin_mul_overflow (value, v , &value))
233- value = max;
250+ if (__builtin_add_overflow (value, other , &ret. value ))
251+ ret. value = max;
234252 }
235253
236- return * this ;
254+ return ret ;
237255 }
238256
239- // operator+, operator-, operator*
257+ template <typename U>
258+ requires std::integral<std::remove_reference_t <U>>
259+ Saturated<TP>
260+ operator +(const Saturated<U>& other) const
261+ { return this ->operator +(other.value ); }
262+
240263 template <typename U>
241264 requires std::unsigned_integral<std::remove_reference_t <U>>
242- TP
243- operator + (const Saturated<U> & other)
265+ Saturated<TP>
266+ operator - (const U & other) const
244267 {
245- Saturated<TP> tmp ;
268+ Saturated<TP> ret ;
246269
247- if (__builtin_add_overflow (value, other. value , &tmp .value ))
248- tmp .value = max ;
270+ if (__builtin_sub_overflow (value, other, &ret .value ))
271+ ret .value = min ;
249272
250- return tmp. value ;
273+ return ret ;
251274 }
252275
253276 template <typename U>
254277 requires std::signed_integral<std::remove_reference_t <U>>
255- TP
256- operator + (const Saturated<U> & other)
278+ Saturated<TP>
279+ operator - (const U & other) const
257280 {
258- Saturated<TP> tmp ;
281+ Saturated<TP> ret ;
259282
260- if (other. value < 0 ) {
261- if (__builtin_sub_overflow (value, -other. value , &tmp .value ))
262- tmp .value = min ;
283+ if (other < 0 ) {
284+ if (__builtin_add_overflow (value, -other, &ret .value ))
285+ ret .value = max ;
263286 } else {
264- if (__builtin_add_overflow (value, other. value , &tmp .value ))
265- tmp .value = max ;
287+ if (__builtin_sub_overflow (value, other, &ret .value ))
288+ ret .value = min ;
266289 }
267290
268- return tmp. value ;
291+ return ret ;
269292 }
270293
294+ template <typename U>
295+ requires std::integral<std::remove_reference_t <U>>
296+ Saturated<TP>
297+ operator -(const Saturated<U>& other) const
298+ { return this ->operator -(other.value ); }
299+
271300 template <typename U>
272301 requires std::unsigned_integral<std::remove_reference_t <U>>
273- TP
274- operator - (const Saturated<U> & other)
302+ Saturated<TP>
303+ operator * (const U & other) const
275304 {
276- Saturated<TP> tmp ;
305+ Saturated<TP> ret ;
277306
278- if (__builtin_sub_overflow (value, other. value , &tmp .value ))
279- tmp .value = min ;
307+ if (__builtin_mul_overflow (value, other, &ret .value ))
308+ ret .value = max ;
280309
281- return tmp. value ;
310+ return ret ;
282311 }
283312
284313 template <typename U>
285314 requires std::signed_integral<std::remove_reference_t <U>>
286- TP
287- operator - (const Saturated<U> & other)
315+ Saturated<TP>
316+ operator * (const U & other) const
288317 {
289- Saturated<TP> tmp ;
318+ Saturated<TP> ret ;
290319
291- if (other.value < 0 ) {
292- if (__builtin_add_overflow (value, -other.value , &tmp.value ))
293- tmp.value = max;
320+ if (other < 0 ) {
321+ if (__builtin_mul_overflow (value, -other, &ret.value ))
322+ ret.value = max;
323+ ret.value = -ret.value ;
294324 } else {
295- if (__builtin_sub_overflow (value, other. value , &tmp .value ))
296- tmp .value = min ;
325+ if (__builtin_mul_overflow (value, other, &ret .value ))
326+ ret .value = max ;
297327 }
298328
299- return tmp. value ;
329+ return ret ;
300330 }
301331
302- TP
303- operator *(const Saturated<T>& other)
332+ template <typename U>
333+ requires std::integral<std::remove_reference_t <U>>
334+ Saturated<TP>
335+ operator *(const Saturated<U>& other) const
336+ { return this ->operator *(other.value ); }
337+
338+ template <typename U>
339+ requires std::integral<std::remove_reference_t <U>>
340+ Saturated<TP>
341+ operator /(const U& other) const
304342 {
305- Saturated<TP> tmp;
343+ return Saturated<TP>(value / other);
344+ }
306345
307- if (__builtin_mul_overflow (value, other.value , &tmp.value ))
308- tmp.value = max;
346+ template <typename U>
347+ requires std::integral<std::remove_reference_t <U>>
348+ Saturated<TP>
349+ operator /(const Saturated<U>& other) const
350+ { return this ->operator /(other.value ); }
309351
310- return tmp.value ;
311- }
312352
313353 TS
314- operator -()
354+ operator -() const
315355 { return -TS (value); }
316356
317357 void
0 commit comments