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