Skip to content

Commit d5985c5

Browse files
committed
improved and completed modm::Saturated
1 parent b735568 commit d5985c5

File tree

3 files changed

+254
-192
lines changed

3 files changed

+254
-192
lines changed

src/modm/math/saturation/saturated.hpp

Lines changed: 135 additions & 92 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,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

Comments
 (0)