@@ -30,7 +30,6 @@ namespace modm
30
30
* @see https://en.wikipedia.org/wiki/Saturation_arithmetic
31
31
*
32
32
* @author Thomas Sommer
33
- *
34
33
* @ingroup modm_math_saturated
35
34
*/
36
35
@@ -42,7 +41,7 @@ class Saturated
42
41
using TP = std::remove_reference_t <T>;
43
42
using TS = std::conditional_t <std::is_signed_v<T>, T, std::make_signed_t <modm::WideType<TP>>>;
44
43
45
- T value = 0 ;
44
+ T value{ 0 } ;
46
45
private:
47
46
static constexpr TP min = std::numeric_limits<TP>::min();
48
47
static constexpr TP max = std::numeric_limits<TP>::max();
@@ -55,36 +54,38 @@ class Saturated
55
54
56
55
template <typename U>
57
56
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); }
60
59
61
60
template <typename U>
62
61
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); }
65
64
66
65
template <typename U>
67
66
requires std::integral<std::remove_reference_t <U>>
68
67
constexpr Saturated (const Saturated<U>& other)
69
68
{ value = std::clamp< modm::fits_any_t <TP, U> >(other.value , min, max); }
70
69
71
- TP
72
- getValue () const
70
+ T getValue () const
73
71
{ return value; }
74
72
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; }
82
77
83
78
// operator=
84
79
void
85
80
operator =(const Saturated& other)
86
81
{ value = other.value ; }
87
82
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
+
88
89
template <typename U>
89
90
requires std::integral<std::remove_reference_t <U>>
90
91
void
@@ -110,26 +111,26 @@ class Saturated
110
111
Saturated
111
112
operator ++(int )
112
113
{
113
- Saturated tmp (*this );
114
+ Saturated ret (*this );
114
115
if (value < max) value++;
115
- return tmp ;
116
+ return ret ;
116
117
}
117
118
118
119
Saturated
119
120
operator --(int )
120
121
{
121
- Saturated tmp (*this );
122
+ Saturated ret (*this );
122
123
if (value > min) value--;
123
- return tmp ;
124
+ return ret ;
124
125
}
125
126
126
127
// operator+=, operator-=, operator*=
127
128
template <typename U>
128
129
requires std::unsigned_integral<std::remove_reference_t <U>>
129
130
Saturated&
130
- operator +=(const Saturated<U> & other)
131
+ operator +=(const U & other)
131
132
{
132
- if (__builtin_add_overflow (value, other. value , &value))
133
+ if (__builtin_add_overflow (value, other, &value))
133
134
value = max;
134
135
135
136
return *this ;
@@ -138,25 +139,31 @@ class Saturated
138
139
template <typename U>
139
140
requires std::signed_integral<std::remove_reference_t <U>>
140
141
Saturated&
141
- operator +=(const Saturated<U> & other)
142
+ operator +=(const U & other)
142
143
{
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))
145
146
value = min;
146
147
} else {
147
- if (__builtin_add_overflow (value, other. value , &value))
148
+ if (__builtin_add_overflow (value, other, &value))
148
149
value = max;
149
150
}
150
151
151
152
return *this ;
152
153
}
153
154
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
+
154
161
template <typename U>
155
162
requires std::unsigned_integral<std::remove_reference_t <U>>
156
163
Saturated&
157
- operator -=(const Saturated<U> & other)
164
+ operator -=(const U & other)
158
165
{
159
- if (__builtin_sub_overflow (value, other. value , &value))
166
+ if (__builtin_sub_overflow (value, other, &value))
160
167
value = min;
161
168
162
169
return *this ;
@@ -165,25 +172,31 @@ class Saturated
165
172
template <typename U>
166
173
requires std::signed_integral<std::remove_reference_t <U>>
167
174
Saturated&
168
- operator -=(const Saturated<U> & other)
175
+ operator -=(const U & other)
169
176
{
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))
172
179
value = max;
173
180
} else {
174
- if (__builtin_sub_overflow (value, other. value , &value))
181
+ if (__builtin_sub_overflow (value, other, &value))
175
182
value = min;
176
183
}
177
184
178
185
return *this ;
179
186
}
180
187
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
+
181
194
template <typename U>
182
195
requires std::unsigned_integral<std::remove_reference_t <U>>
183
196
Saturated&
184
- operator *=(const Saturated<U> & other)
197
+ operator *=(const U & other)
185
198
{
186
- if (__builtin_mul_overflow (value, other. value , &value))
199
+ if (__builtin_mul_overflow (value, other, &value))
187
200
value = max;
188
201
189
202
return *this ;
@@ -192,126 +205,156 @@ class Saturated
192
205
template <typename U>
193
206
requires std::signed_integral<std::remove_reference_t <U>>
194
207
Saturated&
195
- operator *=(const Saturated<U> & other)
208
+ operator *=(const U & other)
196
209
{
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))
199
212
value = max;
200
213
value = -value;
201
214
} else {
202
- if (__builtin_mul_overflow (value, other. value , &value))
215
+ if (__builtin_mul_overflow (value, other, &value))
203
216
value = max;
204
217
}
205
218
206
219
return *this ;
207
220
}
208
221
209
- // OPTIMIZE By whatever reason, for operator*= the compiler doesn't implicitly construct Saturated types.
210
- // Overload plain types for now:
211
222
template <typename U>
212
- requires std::unsigned_integral <std::remove_reference_t <U>>
223
+ requires std::integral <std::remove_reference_t <U>>
213
224
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
215
233
{
216
- if (__builtin_mul_overflow (value, v, &value))
217
- value = max;
234
+ Saturated<TP> ret;
218
235
219
- return *this ;
236
+ if (__builtin_add_overflow (value, other, &ret.value ))
237
+ ret.value = max;
238
+
239
+ return ret;
220
240
}
221
241
222
242
template <typename U>
223
243
requires std::signed_integral<std::remove_reference_t <U>>
224
- Saturated&
225
- operator *= (const U& v)
244
+ TP
245
+ operator + (const U& other) const
226
246
{
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;
231
252
} 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;
234
255
}
235
256
236
- return * this ;
257
+ return ret ;
237
258
}
238
259
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
+
240
266
template <typename U>
241
267
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
244
270
{
245
- Saturated<TP> tmp ;
271
+ Saturated<TP> ret ;
246
272
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 ;
249
275
250
- return tmp. value ;
276
+ return ret ;
251
277
}
252
278
253
279
template <typename U>
254
280
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
257
283
{
258
- Saturated<TP> tmp ;
284
+ Saturated<TP> ret ;
259
285
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 ;
263
289
} 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 ;
266
292
}
267
293
268
- return tmp. value ;
294
+ return ret ;
269
295
}
270
296
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
+
271
303
template <typename U>
272
304
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
275
307
{
276
- Saturated<TP> tmp ;
308
+ Saturated<TP> ret ;
277
309
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 ;
280
312
281
- return tmp. value ;
313
+ return ret ;
282
314
}
283
315
284
316
template <typename U>
285
317
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
288
320
{
289
- Saturated<TP> tmp ;
321
+ Saturated<TP> ret ;
290
322
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 ;
294
327
} 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 ;
297
330
}
298
331
299
- return tmp. value ;
332
+ return ret ;
300
333
}
301
334
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
304
345
{
305
- Saturated<TP> tmp;
346
+ return Saturated<TP>(value / other);
347
+ }
306
348
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 ); }
309
354
310
- return tmp.value ;
311
- }
312
355
313
356
TS
314
- operator -()
357
+ operator -() const
315
358
{ return -TS (value); }
316
359
317
360
void
0 commit comments