@@ -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,38 @@ 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+ T getValue () const
7371 { return value; }
7472
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 ;
73+ // Cast to underlying type. No more comparison operators required.
74+ // @see https://en.cppreference.com/w/cpp/language/cast_operator
75+ operator T () const
76+ { return value; }
8277
8378 // operator=
8479 void
8580 operator =(const Saturated& other)
8681 { value = other.value ; }
8782
83+ template <typename U>
84+ requires std::integral<std::remove_reference_t <U>>
85+ void
86+ operator =(const U& other)
87+ { value = std::clamp< modm::fits_any_t <TP, U> >(other, min, max); }
88+
8889 template <typename U>
8990 requires std::integral<std::remove_reference_t <U>>
9091 void
@@ -110,26 +111,26 @@ class Saturated
110111 Saturated
111112 operator ++(int )
112113 {
113- Saturated tmp (*this );
114+ Saturated ret (*this );
114115 if (value < max) value++;
115- return tmp ;
116+ return ret ;
116117 }
117118
118119 Saturated
119120 operator --(int )
120121 {
121- Saturated tmp (*this );
122+ Saturated ret (*this );
122123 if (value > min) value--;
123- return tmp ;
124+ return ret ;
124125 }
125126
126127 // operator+=, operator-=, operator*=
127128 template <typename U>
128129 requires std::unsigned_integral<std::remove_reference_t <U>>
129130 Saturated&
130- operator +=(const Saturated<U> & other)
131+ operator +=(const U & other)
131132 {
132- if (__builtin_add_overflow (value, other. value , &value))
133+ if (__builtin_add_overflow (value, other, &value))
133134 value = max;
134135
135136 return *this ;
@@ -138,25 +139,31 @@ class Saturated
138139 template <typename U>
139140 requires std::signed_integral<std::remove_reference_t <U>>
140141 Saturated&
141- operator +=(const Saturated<U> & other)
142+ operator +=(const U & other)
142143 {
143- if (other. value < 0 ) {
144- if (__builtin_sub_overflow (value, -other. value , &value))
144+ if (other < 0 ) {
145+ if (__builtin_sub_overflow (value, -other, &value))
145146 value = min;
146147 } else {
147- if (__builtin_add_overflow (value, other. value , &value))
148+ if (__builtin_add_overflow (value, other, &value))
148149 value = max;
149150 }
150151
151152 return *this ;
152153 }
153154
155+ template <typename U>
156+ requires std::integral<std::remove_reference_t <U>>
157+ Saturated&
158+ operator +=(const Saturated<U>& other)
159+ { return this ->operator +=(other.value ); }
160+
154161 template <typename U>
155162 requires std::unsigned_integral<std::remove_reference_t <U>>
156163 Saturated&
157- operator -=(const Saturated<U> & other)
164+ operator -=(const U & other)
158165 {
159- if (__builtin_sub_overflow (value, other. value , &value))
166+ if (__builtin_sub_overflow (value, other, &value))
160167 value = min;
161168
162169 return *this ;
@@ -165,25 +172,31 @@ class Saturated
165172 template <typename U>
166173 requires std::signed_integral<std::remove_reference_t <U>>
167174 Saturated&
168- operator -=(const Saturated<U> & other)
175+ operator -=(const U & other)
169176 {
170- if (other. value < 0 ) {
171- if (__builtin_add_overflow (value, -other. value , &value))
177+ if (other < 0 ) {
178+ if (__builtin_add_overflow (value, -other, &value))
172179 value = max;
173180 } else {
174- if (__builtin_sub_overflow (value, other. value , &value))
181+ if (__builtin_sub_overflow (value, other, &value))
175182 value = min;
176183 }
177184
178185 return *this ;
179186 }
180187
188+ template <typename U>
189+ requires std::integral<std::remove_reference_t <U>>
190+ Saturated&
191+ operator -=(const Saturated<U>& other)
192+ { return this ->operator -=(other.value ); }
193+
181194 template <typename U>
182195 requires std::unsigned_integral<std::remove_reference_t <U>>
183196 Saturated&
184- operator *=(const Saturated<U> & other)
197+ operator *=(const U & other)
185198 {
186- if (__builtin_mul_overflow (value, other. value , &value))
199+ if (__builtin_mul_overflow (value, other, &value))
187200 value = max;
188201
189202 return *this ;
@@ -192,126 +205,156 @@ class Saturated
192205 template <typename U>
193206 requires std::signed_integral<std::remove_reference_t <U>>
194207 Saturated&
195- operator *=(const Saturated<U> & other)
208+ operator *=(const U & other)
196209 {
197- if (other. value < 0 ) {
198- if (__builtin_mul_overflow (value, -other. value , &value))
210+ if (other < 0 ) {
211+ if (__builtin_mul_overflow (value, -other, &value))
199212 value = max;
200213 value = -value;
201214 } else {
202- if (__builtin_mul_overflow (value, other. value , &value))
215+ if (__builtin_mul_overflow (value, other, &value))
203216 value = max;
204217 }
205218
206219 return *this ;
207220 }
208221
209- // OPTIMIZE By whatever reason, for operator*= the compiler doesn't implicitly construct Saturated types.
210- // Overload plain types for now:
211222 template <typename U>
212- requires std::unsigned_integral <std::remove_reference_t <U>>
223+ requires std::integral <std::remove_reference_t <U>>
213224 Saturated&
214- operator *=(const U& v)
225+ operator *=(const Saturated<U>& other)
226+ { return this ->operator *=(other.value ); }
227+
228+ // operator+, operator-, operator*
229+ template <typename U>
230+ requires std::unsigned_integral<std::remove_reference_t <U>>
231+ TP
232+ operator +(const U& other) const
215233 {
216- if (__builtin_mul_overflow (value, v, &value))
217- value = max;
234+ Saturated<TP> ret;
218235
219- return *this ;
236+ if (__builtin_add_overflow (value, other, &ret.value ))
237+ ret.value = max;
238+
239+ return ret;
220240 }
221241
222242 template <typename U>
223243 requires std::signed_integral<std::remove_reference_t <U>>
224- Saturated&
225- operator *= (const U& v)
244+ TP
245+ operator + (const U& other) const
226246 {
227- if (v < 0 ) {
228- if (__builtin_mul_overflow (value, -v, &value))
229- value = max;
230- value = -value;
247+ Saturated<TP> ret;
248+
249+ if (other < 0 ) {
250+ if (__builtin_sub_overflow (value, -other, &ret.value ))
251+ ret.value = min;
231252 } else {
232- if (__builtin_mul_overflow (value, v , &value))
233- value = max;
253+ if (__builtin_add_overflow (value, other , &ret. value ))
254+ ret. value = max;
234255 }
235256
236- return * this ;
257+ return ret ;
237258 }
238259
239- // operator+, operator-, operator*
260+ template <typename U>
261+ requires std::integral<std::remove_reference_t <U>>
262+ Saturated<TP>
263+ operator +(const Saturated<U>& other) const
264+ { return this ->operator +(other.value ); }
265+
240266 template <typename U>
241267 requires std::unsigned_integral<std::remove_reference_t <U>>
242- TP
243- operator + (const Saturated<U> & other)
268+ Saturated<TP>
269+ operator - (const U & other) const
244270 {
245- Saturated<TP> tmp ;
271+ Saturated<TP> ret ;
246272
247- if (__builtin_add_overflow (value, other. value , &tmp .value ))
248- tmp .value = max ;
273+ if (__builtin_sub_overflow (value, other, &ret .value ))
274+ ret .value = min ;
249275
250- return tmp. value ;
276+ return ret ;
251277 }
252278
253279 template <typename U>
254280 requires std::signed_integral<std::remove_reference_t <U>>
255- TP
256- operator + (const Saturated<U> & other)
281+ Saturated<TP>
282+ operator - (const U & other) const
257283 {
258- Saturated<TP> tmp ;
284+ Saturated<TP> ret ;
259285
260- if (other. value < 0 ) {
261- if (__builtin_sub_overflow (value, -other. value , &tmp .value ))
262- tmp .value = min ;
286+ if (other < 0 ) {
287+ if (__builtin_add_overflow (value, -other, &ret .value ))
288+ ret .value = max ;
263289 } else {
264- if (__builtin_add_overflow (value, other. value , &tmp .value ))
265- tmp .value = max ;
290+ if (__builtin_sub_overflow (value, other, &ret .value ))
291+ ret .value = min ;
266292 }
267293
268- return tmp. value ;
294+ return ret ;
269295 }
270296
297+ template <typename U>
298+ requires std::integral<std::remove_reference_t <U>>
299+ Saturated<TP>
300+ operator -(const Saturated<U>& other) const
301+ { return this ->operator -(other.value ); }
302+
271303 template <typename U>
272304 requires std::unsigned_integral<std::remove_reference_t <U>>
273- TP
274- operator - (const Saturated<U> & other)
305+ Saturated<TP>
306+ operator * (const U & other) const
275307 {
276- Saturated<TP> tmp ;
308+ Saturated<TP> ret ;
277309
278- if (__builtin_sub_overflow (value, other. value , &tmp .value ))
279- tmp .value = min ;
310+ if (__builtin_mul_overflow (value, other, &ret .value ))
311+ ret .value = max ;
280312
281- return tmp. value ;
313+ return ret ;
282314 }
283315
284316 template <typename U>
285317 requires std::signed_integral<std::remove_reference_t <U>>
286- TP
287- operator - (const Saturated<U> & other)
318+ Saturated<TP>
319+ operator * (const U & other) const
288320 {
289- Saturated<TP> tmp ;
321+ Saturated<TP> ret ;
290322
291- if (other.value < 0 ) {
292- if (__builtin_add_overflow (value, -other.value , &tmp.value ))
293- tmp.value = max;
323+ if (other < 0 ) {
324+ if (__builtin_mul_overflow (value, -other, &ret.value ))
325+ ret.value = max;
326+ ret.value = -ret.value ;
294327 } else {
295- if (__builtin_sub_overflow (value, other. value , &tmp .value ))
296- tmp .value = min ;
328+ if (__builtin_mul_overflow (value, other, &ret .value ))
329+ ret .value = max ;
297330 }
298331
299- return tmp. value ;
332+ return ret ;
300333 }
301334
302- TP
303- operator *(const Saturated<T>& other)
335+ template <typename U>
336+ requires std::integral<std::remove_reference_t <U>>
337+ Saturated<TP>
338+ operator *(const Saturated<U>& other) const
339+ { return this ->operator *(other.value ); }
340+
341+ template <typename U>
342+ requires std::integral<std::remove_reference_t <U>>
343+ Saturated<TP>
344+ operator /(const U& other) const
304345 {
305- Saturated<TP> tmp;
346+ return Saturated<TP>(value / other);
347+ }
306348
307- if (__builtin_mul_overflow (value, other.value , &tmp.value ))
308- tmp.value = max;
349+ template <typename U>
350+ requires std::integral<std::remove_reference_t <U>>
351+ Saturated<TP>
352+ operator /(const Saturated<U>& other) const
353+ { return this ->operator /(other.value ); }
309354
310- return tmp.value ;
311- }
312355
313356 TS
314- operator -()
357+ operator -() const
315358 { return -TS (value); }
316359
317360 void
0 commit comments