Line data Source code
1 : // Copyright (c) 2021-2023 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : /** \file
20 : * \brief Verify the saturated_add() functions.
21 : *
22 : * This file implements tests to verify that the saturated_add() functions
23 : * will indeed saturate the result on an overflow.
24 : */
25 :
26 : // self
27 : //
28 : #include "catch_main.h"
29 :
30 :
31 :
32 : // snapdev
33 : //
34 : #include <snapdev/math.h>
35 : #include <snapdev/ostream_int128.h>
36 :
37 :
38 : // last include
39 : //
40 : #include <snapdev/poison.h>
41 :
42 :
43 : // __int128 is not ISO C++ yet
44 : #pragma GCC diagnostic ignored "-Wpedantic"
45 :
46 :
47 5 : CATCH_TEST_CASE("saturated_add_uint", "[math]")
48 : {
49 5 : CATCH_START_SECTION("saturated_add_uint8: test all possible cases with 8 bits")
50 : {
51 257 : for(std::uint32_t n(0); n < 256; ++n)
52 : {
53 65792 : for(std::uint32_t m(0); m < 256; ++m)
54 : {
55 65536 : std::uint32_t expected(n + m);
56 65536 : if(expected > 255) // easy to test in this case
57 : {
58 32640 : expected = 255;
59 : }
60 65536 : std::uint32_t const result(snapdev::saturated_add(
61 : static_cast<std::uint8_t>(n)
62 65536 : , static_cast<std::uint8_t>(m)));
63 65536 : CATCH_REQUIRE(result == expected);
64 : }
65 : }
66 : }
67 5 : CATCH_END_SECTION()
68 :
69 5 : CATCH_START_SECTION("saturated_add_uint16: test many possible cases with 16 bits")
70 : {
71 1339 : for(std::uint32_t n(0); n < 65536; n += rand() % 100)
72 : {
73 1772451 : for(std::uint32_t m(0); m < 65536; m += rand() % 100)
74 : {
75 1771113 : std::uint32_t expected(n + m);
76 1771113 : if(expected > 65535) // easy to test in this case
77 : {
78 883923 : expected = 65535;
79 : }
80 1771113 : std::uint32_t const result(snapdev::saturated_add(
81 : static_cast<std::uint16_t>(n)
82 1771113 : , static_cast<std::uint16_t>(m)));
83 1771113 : CATCH_REQUIRE(result == expected);
84 : }
85 : }
86 : }
87 5 : CATCH_END_SECTION()
88 :
89 5 : CATCH_START_SECTION("saturated_add_uint32: test a few possible cases with 32 bits")
90 : {
91 : // no overflow at all
92 : {
93 1 : std::uint32_t const result(snapdev::saturated_add(
94 : static_cast<std::uint32_t>(1'000)
95 : , static_cast<std::uint32_t>(32'000)));
96 1 : CATCH_REQUIRE(result == 33'000);
97 : }
98 :
99 : // no overflow, but close
100 : {
101 1 : std::uint32_t const result(snapdev::saturated_add(
102 : static_cast<std::uint32_t>(1'000)
103 1 : , std::numeric_limits<std::uint32_t>::max() - 1'000));
104 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::max());
105 : }
106 :
107 : // overflow by 1
108 : {
109 1 : std::uint32_t const result(snapdev::saturated_add(
110 : static_cast<std::uint32_t>(1'001)
111 1 : , std::numeric_limits<std::uint32_t>::max() - 1'000));
112 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::max());
113 : }
114 :
115 : // definitive overflow
116 : {
117 1 : std::uint32_t const result(snapdev::saturated_add(
118 : static_cast<std::uint32_t>(3'501)
119 1 : , std::numeric_limits<std::uint32_t>::max() - 1'000));
120 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::max());
121 : }
122 :
123 : // max. overflow
124 : {
125 1 : std::uint32_t const result(snapdev::saturated_add(
126 : std::numeric_limits<std::uint32_t>::max()
127 : , std::numeric_limits<std::uint32_t>::max()));
128 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::max());
129 : }
130 : }
131 5 : CATCH_END_SECTION()
132 :
133 5 : CATCH_START_SECTION("saturated_add_uint64: test a few possible cases with 64 bits")
134 : {
135 : // no overflow at all
136 : {
137 1 : std::uint64_t const result(snapdev::saturated_add(
138 : static_cast<std::uint64_t>(1'000)
139 : , static_cast<std::uint64_t>(32'000)));
140 1 : CATCH_REQUIRE(result == 33'000);
141 : }
142 :
143 : // no overflow, but close
144 : {
145 1 : std::uint64_t const result(snapdev::saturated_add(
146 : static_cast<std::uint64_t>(1'000)
147 1 : , std::numeric_limits<std::uint64_t>::max() - 1'000));
148 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::max());
149 : }
150 :
151 : // overflow by 1
152 : {
153 1 : std::uint64_t const result(snapdev::saturated_add(
154 : static_cast<std::uint64_t>(1'001)
155 1 : , std::numeric_limits<std::uint64_t>::max() - 1'000));
156 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::max());
157 : }
158 :
159 : // definitive overflow
160 : {
161 1 : std::uint64_t const result(snapdev::saturated_add(
162 : static_cast<std::uint64_t>(3'501)
163 1 : , std::numeric_limits<std::uint64_t>::max() - 1'000));
164 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::max());
165 : }
166 :
167 : // max. overflow
168 : {
169 1 : std::uint64_t const result(snapdev::saturated_add(
170 : std::numeric_limits<std::uint64_t>::max()
171 : , std::numeric_limits<std::uint64_t>::max()));
172 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::max());
173 : }
174 : }
175 5 : CATCH_END_SECTION()
176 :
177 5 : CATCH_START_SECTION("saturated_add_uint128: test a few possible cases with 128 bits")
178 : {
179 : #pragma GCC diagnostic push
180 : #pragma GCC diagnostic ignored "-Wpedantic"
181 : // no overflow at all
182 : {
183 1 : unsigned __int128 const result(snapdev::saturated_add(
184 : static_cast<unsigned __int128>(1'000)
185 : , static_cast<unsigned __int128>(32'000)));
186 1 : CATCH_REQUIRE(result == 33'000);
187 : }
188 :
189 : // no overflow, but close
190 : {
191 1 : unsigned __int128 const result(snapdev::saturated_add(
192 : static_cast<unsigned __int128>(1'000)
193 1 : , std::numeric_limits<unsigned __int128>::max() - 1'000));
194 1 : CATCH_REQUIRE(result == std::numeric_limits<unsigned __int128>::max());
195 : }
196 :
197 : // overflow by 1
198 : {
199 1 : unsigned __int128 const result(snapdev::saturated_add(
200 : static_cast<unsigned __int128>(1'001)
201 1 : , std::numeric_limits<unsigned __int128>::max() - 1'000));
202 1 : CATCH_REQUIRE(result == std::numeric_limits<unsigned __int128>::max());
203 : }
204 :
205 : // definitive overflow
206 : {
207 1 : unsigned __int128 const result(snapdev::saturated_add(
208 : static_cast<unsigned __int128>(3'501)
209 1 : , std::numeric_limits<unsigned __int128>::max() - 1'000));
210 1 : CATCH_REQUIRE(result == std::numeric_limits<unsigned __int128>::max());
211 : }
212 :
213 : // max. overflow
214 : {
215 1 : unsigned __int128 const result(snapdev::saturated_add(
216 : std::numeric_limits<unsigned __int128>::max()
217 : , std::numeric_limits<unsigned __int128>::max()));
218 1 : CATCH_REQUIRE(result == std::numeric_limits<unsigned __int128>::max());
219 : }
220 : #pragma GCC diagnostic pop
221 : }
222 5 : CATCH_END_SECTION()
223 5 : }
224 :
225 :
226 5 : CATCH_TEST_CASE("saturated_add_int", "[math]")
227 : {
228 5 : CATCH_START_SECTION("saturated_add_int8: test all possible cases with 8 bits")
229 : {
230 257 : for(std::int32_t n(-128); n < 128; ++n)
231 : {
232 65792 : for(std::int32_t m(-128); m < 128; ++m)
233 : {
234 65536 : std::int32_t expected(n + m);
235 65536 : if(expected < -128)
236 : {
237 8256 : expected = -128;
238 : }
239 57280 : else if(expected > 127)
240 : {
241 8128 : expected = 127;
242 : }
243 65536 : std::int32_t const result(snapdev::saturated_add(
244 : static_cast<std::int8_t>(n)
245 65536 : , static_cast<std::int8_t>(m)));
246 65536 : CATCH_REQUIRE(result == expected);
247 : }
248 : }
249 : }
250 5 : CATCH_END_SECTION()
251 :
252 5 : CATCH_START_SECTION("saturated_add_int16: test many possible cases with 16 bits")
253 : {
254 1299 : for(std::int32_t n(-32768); n < 32768; n += rand() % 100)
255 : {
256 1721242 : for(std::int32_t m(-32768); m < 32768; m += rand() % 100)
257 : {
258 1719944 : std::int32_t expected(n + m);
259 1719944 : if(expected < -32768)
260 : {
261 212298 : expected = -32768;
262 : }
263 1507646 : else if(expected > 32767)
264 : {
265 220209 : expected = 32767;
266 : }
267 1719944 : std::int32_t const result(snapdev::saturated_add(
268 : static_cast<std::int16_t>(n)
269 1719944 : , static_cast<std::int16_t>(m)));
270 1719944 : CATCH_REQUIRE(result == expected);
271 : }
272 : }
273 : }
274 5 : CATCH_END_SECTION()
275 :
276 5 : CATCH_START_SECTION("saturated_add_int32: test a few possible cases with 32 bits")
277 : {
278 : // no overflow at all
279 : {
280 1 : std::int32_t const result(snapdev::saturated_add(
281 : static_cast<std::int32_t>(1'000)
282 : , static_cast<std::int32_t>(33'000)));
283 1 : CATCH_REQUIRE(result == 34'000);
284 : }
285 :
286 : // no overflow, but close
287 : {
288 1 : std::int32_t const result(snapdev::saturated_add(
289 : static_cast<std::int32_t>(1'000)
290 1 : , std::numeric_limits<std::int32_t>::max() - 1'000));
291 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int32_t>::max());
292 : }
293 :
294 : // overflow by 1
295 : {
296 1 : std::int32_t const result(snapdev::saturated_add(
297 : static_cast<std::int32_t>(1'001)
298 1 : , std::numeric_limits<std::int32_t>::max() - 1'000));
299 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int32_t>::max());
300 : }
301 :
302 : // definitive overflow
303 : {
304 1 : std::int32_t const result(snapdev::saturated_add(
305 : static_cast<std::int32_t>(3'501)
306 1 : , std::numeric_limits<std::int32_t>::max() - 1'000));
307 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int32_t>::max());
308 : }
309 :
310 : // max. overflow
311 : {
312 1 : std::int32_t const result(snapdev::saturated_add(
313 : std::numeric_limits<std::int32_t>::max()
314 : , std::numeric_limits<std::int32_t>::max()));
315 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int32_t>::max());
316 : }
317 : }
318 5 : CATCH_END_SECTION()
319 :
320 5 : CATCH_START_SECTION("saturated_add_int64: test a few possible cases with 64 bits")
321 : {
322 : // no overflow at all
323 : {
324 1 : std::int64_t const result(snapdev::saturated_add(
325 : static_cast<std::int64_t>(1'000)
326 : , static_cast<std::int64_t>(32'000)));
327 1 : CATCH_REQUIRE(result == 33'000);
328 : }
329 :
330 : // no overflow, but close
331 : {
332 1 : std::int64_t const result(snapdev::saturated_add(
333 : static_cast<std::int64_t>(1'000)
334 1 : , std::numeric_limits<std::int64_t>::max() - 1'000));
335 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int64_t>::max());
336 : }
337 :
338 : // overflow by 1
339 : {
340 1 : std::int64_t const result(snapdev::saturated_add(
341 : static_cast<std::int64_t>(1'001)
342 1 : , std::numeric_limits<std::int64_t>::max() - 1'000));
343 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int64_t>::max());
344 : }
345 :
346 : // definitive overflow
347 : {
348 1 : std::int64_t const result(snapdev::saturated_add(
349 : static_cast<std::int64_t>(3'501)
350 1 : , std::numeric_limits<std::int64_t>::max() - 1'000));
351 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int64_t>::max());
352 : }
353 :
354 : // max. overflow
355 : {
356 1 : std::int64_t const result(snapdev::saturated_add(
357 : std::numeric_limits<std::int64_t>::max()
358 : , std::numeric_limits<std::int64_t>::max()));
359 1 : CATCH_REQUIRE(result == std::numeric_limits<std::int64_t>::max());
360 : }
361 : }
362 5 : CATCH_END_SECTION()
363 :
364 5 : CATCH_START_SECTION("saturated_add_int128: test a few possible cases with 128 bits")
365 : {
366 : #pragma GCC diagnostic push
367 : #pragma GCC diagnostic ignored "-Wpedantic"
368 : // no overflow at all
369 : {
370 1 : __int128 const result(snapdev::saturated_add(
371 : static_cast<__int128>(1'000LL)
372 : , static_cast<__int128>(32'000'000LL)));
373 1 : CATCH_REQUIRE(result == 32'001'000LL);
374 : }
375 :
376 : // no overflow, but close
377 : {
378 1 : __int128 const result(snapdev::saturated_add(
379 : static_cast<__int128>(1'000)
380 1 : , std::numeric_limits<__int128>::max() - 1'000));
381 1 : CATCH_REQUIRE(result == std::numeric_limits<__int128>::max());
382 : }
383 :
384 : // overflow by 1
385 : {
386 1 : __int128 const result(snapdev::saturated_add(
387 : static_cast<__int128>(1'001)
388 1 : , std::numeric_limits<__int128>::max() - 1'000));
389 1 : CATCH_REQUIRE(result == std::numeric_limits<__int128>::max());
390 : }
391 :
392 : // definitive overflow
393 : {
394 1 : __int128 const result(snapdev::saturated_add(
395 : static_cast<__int128>(3'501)
396 1 : , std::numeric_limits<__int128>::max() - 1'000));
397 1 : CATCH_REQUIRE(result == std::numeric_limits<__int128>::max());
398 : }
399 :
400 : // max. overflow
401 : {
402 1 : __int128 const result(snapdev::saturated_add(
403 : std::numeric_limits<__int128>::max()
404 : , std::numeric_limits<__int128>::max()));
405 1 : CATCH_REQUIRE(result == std::numeric_limits<__int128>::max());
406 : }
407 : #pragma GCC diagnostic pop
408 : }
409 5 : CATCH_END_SECTION()
410 5 : }
411 :
412 :
413 : // vim: ts=4 sw=4 et
|