Line data Source code
1 : // Copyright (c) 2021-2025 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_subtract() function.
21 : *
22 : * This file implements tests to verify that the saturated_subtract() function
23 : * will indeed saturate the result on an underflow.
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_subtract_uint", "[math]")
48 : {
49 5 : CATCH_START_SECTION("saturated_subtract_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::int32_t expected(n - m);
56 65536 : if(expected < 0) // easy to test in this case
57 : {
58 32640 : expected = 0;
59 : }
60 65536 : std::int32_t const result(snapdev::saturated_subtract(
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_subtract_uint16: test many possible cases with 16 bits")
70 : {
71 1333 : for(std::uint32_t n(0); n < 65536; n += rand() % 100)
72 : {
73 1764556 : for(std::uint32_t m(0); m < 65536; m += rand() % 100)
74 : {
75 1763224 : std::int32_t expected(n - m);
76 1763224 : if(expected < 0) // easy to test in this case
77 : {
78 878092 : expected = 0;
79 : }
80 1763224 : std::int32_t const result(snapdev::saturated_subtract(
81 : static_cast<std::uint16_t>(n)
82 1763224 : , static_cast<std::uint16_t>(m)));
83 1763224 : CATCH_REQUIRE(result == expected);
84 : }
85 : }
86 : }
87 5 : CATCH_END_SECTION()
88 :
89 5 : CATCH_START_SECTION("saturated_subtract: test a few possible cases with 32 bits")
90 : {
91 : // no underflow at all
92 : {
93 1 : std::uint32_t const result(snapdev::saturated_subtract(
94 : static_cast<std::uint32_t>(32'000)
95 : , static_cast<std::uint32_t>(1'000)));
96 1 : CATCH_REQUIRE(result == 31'000UL);
97 : }
98 :
99 : // no underflow, but close
100 : {
101 1 : std::uint32_t const result(snapdev::saturated_subtract(
102 : static_cast<std::uint32_t>(1'000)
103 : , static_cast<std::uint32_t>(1'000)));
104 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::min());
105 : }
106 :
107 : // underflow by 1
108 : {
109 1 : std::uint32_t const result(snapdev::saturated_subtract(
110 : static_cast<std::uint32_t>(1'000)
111 : , static_cast<std::uint32_t>(1'001)));
112 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::min());
113 : }
114 :
115 : // definitive underflow
116 : {
117 1 : std::uint32_t const result(snapdev::saturated_subtract(
118 : static_cast<std::uint32_t>(1'000)
119 : , static_cast<std::uint32_t>(3'501)));
120 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::min());
121 : }
122 :
123 : // max. underflow
124 : {
125 1 : std::uint32_t const result(snapdev::saturated_subtract(
126 : std::numeric_limits<std::uint32_t>::min()
127 : , std::numeric_limits<std::uint32_t>::max()));
128 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint32_t>::min());
129 : }
130 : }
131 5 : CATCH_END_SECTION()
132 :
133 5 : CATCH_START_SECTION("saturated_subtract_uint64: test a few possible cases with 64 bits")
134 : {
135 : // no underflow at all
136 : {
137 1 : std::uint64_t const result(snapdev::saturated_subtract(
138 : static_cast<std::uint64_t>(32'000)
139 : , static_cast<std::uint64_t>(1'000)));
140 1 : CATCH_REQUIRE(result == 31'000UL);
141 : }
142 :
143 : // no underflow, but close
144 : {
145 1 : std::uint64_t const result(snapdev::saturated_subtract(
146 : static_cast<std::uint64_t>(1'000)
147 : , static_cast<std::uint64_t>(1'000)));
148 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::min());
149 : }
150 :
151 : // underflow by 1
152 : {
153 1 : std::uint64_t const result(snapdev::saturated_subtract(
154 : static_cast<std::uint64_t>(1'000)
155 : , static_cast<std::uint64_t>(1'001)));
156 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::min());
157 : }
158 :
159 : // definitive underflow
160 : {
161 1 : std::uint64_t const result(snapdev::saturated_subtract(
162 : static_cast<std::uint64_t>(1'000)
163 : , static_cast<std::uint64_t>(3'501)));
164 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::min());
165 : }
166 :
167 : // max. underflow
168 : {
169 1 : std::uint64_t const result(snapdev::saturated_subtract(
170 : std::numeric_limits<std::uint64_t>::min()
171 : , std::numeric_limits<std::uint64_t>::max()));
172 1 : CATCH_REQUIRE(result == std::numeric_limits<std::uint64_t>::min());
173 : }
174 : }
175 5 : CATCH_END_SECTION()
176 :
177 5 : CATCH_START_SECTION("saturated_subtract_uint128: test a few possible cases with 128 bits")
178 : {
179 : #pragma GCC diagnostic push
180 : #pragma GCC diagnostic ignored "-Wpedantic"
181 : // no underflow at all
182 : {
183 1 : unsigned __int128 const result(snapdev::saturated_subtract(
184 : static_cast<unsigned __int128>(32'000)
185 : , static_cast<unsigned __int128>(1'000)));
186 1 : CATCH_REQUIRE(result == 31'000UL);
187 : }
188 :
189 : // no underflow, but close
190 : {
191 1 : unsigned __int128 const result(snapdev::saturated_subtract(
192 : static_cast<unsigned __int128>(1'000)
193 : , static_cast<unsigned __int128>(1'000)));
194 1 : CATCH_REQUIRE(result == std::numeric_limits<unsigned __int128>::min());
195 : }
196 :
197 : // underflow by 1
198 : {
199 1 : unsigned __int128 const result(snapdev::saturated_subtract(
200 : static_cast<unsigned __int128>(1'000)
201 : , static_cast<unsigned __int128>(1'001)));
202 1 : CATCH_REQUIRE(result == std::numeric_limits<unsigned __int128>::min());
203 : }
204 :
205 : // definitive underflow
206 : {
207 1 : unsigned __int128 const result(snapdev::saturated_subtract(
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>::min());
211 : }
212 :
213 : // max. underflow
214 : {
215 1 : unsigned __int128 const result(snapdev::saturated_subtract(
216 : std::numeric_limits<unsigned __int128>::min()
217 : , std::numeric_limits<unsigned __int128>::max()));
218 1 : CATCH_REQUIRE(result == std::numeric_limits<unsigned __int128>::min());
219 : }
220 : #pragma GCC diagnostic pop
221 : }
222 5 : CATCH_END_SECTION()
223 5 : }
224 :
225 :
226 : // vim: ts=4 sw=4 et
|