Skip to content

Commit 35209da

Browse files
committed
improved and completed modm::Saturated
1 parent 78308b0 commit 35209da

File tree

3 files changed

+252
-193
lines changed

3 files changed

+252
-193
lines changed

src/modm/math/saturation/saturated.hpp

Lines changed: 133 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -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};
4645
private:
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

Comments
 (0)