GCC Code Coverage Report


Directory: include/tts/
File: include/tts/test/precision.hpp
Date: 2026-10-14 16:28:14
Exec Total Coverage
Lines: 29 36 80.6%
Functions: 97 98 99.0%
Branches: 15 19 78.9%

Line Branch Exec Source
1 //======================================================================================================================
2 //! @file
3 /*
4 TTS - Tiny Test System
5 Copyright : TTS Contributors & Maintainers
6 SPDX-License-Identifier: BSL-1.0
7 */
8 //======================================================================================================================
9 #pragma once
10
11 #include <tts/engine/info.hpp>
12 #include <tts/tools/preprocessor.hpp>
13 #include <tts/tools/precision.hpp>
14 #include <tts/engine/logger.hpp>
15 #include <tts/engine/check.hpp>
16 #include <tts/tools/as_text.hpp>
17
18 namespace tts::_::measure
19 {
20 struct absolute
21 {
22 144 template<typename A, typename B> static double of(A const& a, B const& b)
23 {
24 144 return ::tts::absolute_check(a, b);
25 }
26 };
27
28 struct relative
29 {
30 171 template<typename A, typename B> static double of(A const& a, B const& b)
31 {
32 171 return ::tts::relative_check(a, b);
33 }
34 };
35
36 struct ulp
37 {
38 163 template<typename A, typename B> static double of(A const& a, B const& b)
39 {
40 163 return ::tts::ulp_check(a, b);
41 }
42 };
43 }
44
45 namespace tts::_
46 {
47 template<typename Measure, typename A, typename B, typename N>
48 489 logger check_precision(A const& a,
49 B const& b,
50 N const& n,
51 char const* unit,
52 int digits,
53 check_site const& site)
54 {
55
1/1
✓ Branch 1 taken 265 times.
489 double distance = Measure::of(a, b);
56
57
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 3 times.
489 if(distance <= n)
58 {
59
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 262 times.
483 if(check_passed())
60 ✗ check_report(site,
61 ::tts::text {"Expression: %s == %s within %.*g %s (over %.*g %s).",
62 ✗ site.lhs,
63 ✗ site.rhs,
64 digits,
65 distance,
66 unit,
67 digits,
68 static_cast<double>(n),
69 unit});
70 483 return logger {false};
71 }
72
73 18 return check_failed(
74 site,
75 ::tts::text {"Expected: %s == %s but %s == %s within %.*g %s when %.*g %s were expected.",
76 6 site.lhs,
77 6 site.rhs,
78
1/1
✓ Branch 1 taken 3 times.
12 ::tts::as_text(a).data(),
79
1/1
✓ Branch 1 taken 3 times.
12 ::tts::as_text(b).data(),
80 digits,
81 distance,
82 unit,
83 digits,
84 static_cast<double>(n),
85
1/1
✓ Branch 1 taken 3 times.
6 unit});
86 ✗ }
87
88 27 template<typename A, typename B> logger check_ieee(A const& a, B const& b, check_site const& site)
89 {
90
3/3
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 1 times.
27 if(::tts::ieee_check(a, b))
91 {
92
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
26 if(check_passed())
93 ✗ check_report(site, ::tts::text {"Expression: %s == %s.", site.lhs, site.rhs});
94 26 return logger {false};
95 }
96
97 3 return check_failed(site,
98 ::tts::text {"Expression: %s == %s is false because %s != %s.",
99 1 site.lhs,
100 1 site.rhs,
101
1/1
✓ Branch 1 taken 1 times.
2 ::tts::as_text(a).data(),
102
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
3 ::tts::as_text(b).data()});
103 ✗ }
104
105 2 inline logger check_percent_tolerance(double n, check_site const& site)
106 {
107 return check_failed(
108 site,
109 2 ::tts::text {
110
1/1
✓ Branch 2 taken 2 times.
4 "Tolerance %.*g reads as a percentage: TTS 4 compares a ratio, divide it by a hundred.", 8, n});
111 ✗ }
112 }
113
114 #define TTS_PRECISION_IMPL(LHS, RHS, N, UNIT, MEASURE, PREC, FAILURE) \
115 ::tts::_::check_precision<::tts::_::measure::MEASURE>( \
116 LHS, RHS, N, UNIT, PREC, TTS_CHECK_SITE(FAILURE, TTS_STRING(LHS), "", TTS_STRING(RHS), "")) /**/
117
118 #define TTS_PRECISION(L, R, N, U, F, P, ...) TTS_PRECISION_##__VA_ARGS__(L, R, N, U, F, P)
119 #define TTS_PRECISION_(L, R, N, U, F, P) TTS_PRECISION_IMPL(L, R, N, U, F, P, TTS_FAIL)
120 #define TTS_PRECISION_REQUIRED(L, R, N, U, F, P) TTS_PRECISION_IMPL(L, R, N, U, F, P, TTS_FATAL)
121
122 //======================================================================================================================
123 /**
124 @defgroup test-precision Precision Tests Macros
125 @brief Macros for performing tests taking floating-point precision into account.
126 @{
127 **/
128 //======================================================================================================================
129
130 //======================================================================================================================
131 /**
132 @def TTS_ABSOLUTE_EQUAL
133 @brief Checks if the absolute distance between values is less or equal to a threshold
134
135 This comparison is performed by using the proper tts::absolute_distance overload.
136
137 `L` and `R` must have the same type. A mismatch is a compile-time error: the distance would
138 be measured after promotion to the common type, hence in the unit of that type rather than
139 the one under test.
140
141 @param L, R Expressions to compare.
142 @param N Maximum absolute distance accepted between `L` and `R`.
143 @param ... Optional tag. If equals to `REQUIRED`, this test will stop the program if it fails.
144
145 @groupheader{Example}
146 @snippet doc/absolute.cpp snippet
147 **/
148 //======================================================================================================================
149 #if defined(TTS_DOXYGEN_INVOKED)
150 #define TTS_ABSOLUTE_EQUAL(L, R, N, ...)
151 #else
152 #define TTS_ABSOLUTE_EQUAL(L, R, N, ...) TTS_PRECISION(L, R, N, "unit", absolute, 8, __VA_ARGS__)
153 #endif
154
155 //======================================================================================================================
156 /**
157 @def TTS_RELATIVE_EQUAL
158 @brief Checks if values are within a given relative distance.
159
160 This comparison is performed by using the proper tts::relative_distance overload.
161
162 `L` and `R` must have the same type. A mismatch is a compile-time error: the distance would
163 be measured after promotion to the common type, hence in the unit of that type rather than
164 the one under test.
165
166 @param L, R Expressions to compare.
167 @param N Maximum relative distance accepted between `L` and `R`, as a ratio: `0.005` is
168 half a percent. `abs(L-R) / max(abs(L), abs(R), 1)` is what gets compared to it.
169 @param ... Optional tag. If equals to `REQUIRED`, this test will stop the program if it fails.
170
171 @groupheader{Example}
172 @snippet doc/relative.cpp snippet
173 **/
174 //======================================================================================================================
175 #if defined(TTS_DOXYGEN_INVOKED)
176 #define TTS_RELATIVE_EQUAL(L, R, N, ...)
177 #else
178 #define TTS_RELATIVE_EQUAL(L, R, N, ...) \
179 ( \
180 ::tts::_::reads_as_percent<decltype(L)>(N) \
181 ? TTS_PERCENT_TOLERANCE_##__VA_ARGS__(N) \
182 : TTS_PRECISION(L, R, N, "rel", relative, 8, __VA_ARGS__))
183
184 #define TTS_PERCENT_TOLERANCE_(N) TTS_PERCENT_TOLERANCE_IMPL(N, TTS_FAIL)
185 #define TTS_PERCENT_TOLERANCE_REQUIRED(N) TTS_PERCENT_TOLERANCE_IMPL(N, TTS_FATAL)
186 #define TTS_PERCENT_TOLERANCE_IMPL(N, FAILURE) \
187 ::tts::_::check_percent_tolerance(static_cast<double>(N), \
188 TTS_CHECK_SITE(FAILURE, "", "", "", "")) /**/
189 #endif
190
191 //======================================================================================================================
192 /**
193 @def TTS_ULP_EQUAL
194 @brief Checks if two values are within a given ULP distance
195
196 This comparison is performed by using the proper tts::ulp_distance overload.
197
198 `L` and `R` must have the same type. A mismatch is a compile-time error: the distance would
199 be measured after promotion to the common type, hence in the unit of that type rather than
200 the one under test.
201
202 @param L, R Expressions to compare.
203 @param N Maximum ULPs accepted between `L` and `R`.
204 @param ... Optional tag. If equals to `REQUIRED`, this test will stop the program if it fails.
205
206 @groupheader{Example}
207 @snippet doc/ulp.cpp snippet
208 **/
209 //======================================================================================================================
210 #if defined(TTS_DOXYGEN_INVOKED)
211 #define TTS_ULP_EQUAL(L, R, N, ...)
212 #else
213 #define TTS_ULP_EQUAL(L, R, N, ...) TTS_PRECISION(L, R, N, "ULP", ulp, 2, __VA_ARGS__)
214 #endif
215
216 #define TTS_DO_IEEE_EQUAL_IMPL(LHS, RHS, FAILURE) \
217 ::tts::_::check_ieee( \
218 LHS, RHS, TTS_CHECK_SITE(FAILURE, TTS_STRING(LHS), "", TTS_STRING(RHS), "")) /**/
219
220 #define TTS_DO_IEEE_EQUAL(L, R, ...) TTS_DO_IEEE_EQUAL_##__VA_ARGS__(L, R)
221 #define TTS_DO_IEEE_EQUAL_(L, R) TTS_DO_IEEE_EQUAL_IMPL(L, R, TTS_FAIL)
222 #define TTS_DO_IEEE_EQUAL_REQUIRED(L, R) TTS_DO_IEEE_EQUAL_IMPL(L, R, TTS_FATAL)
223
224 //======================================================================================================================
225 /**
226 @def TTS_IEEE_EQUAL
227 @brief Checks if two values are exactly within 0 ULP.
228
229 This also allow for infinites and NaNs to be compared equal if both values are the
230 same infinites or are both $NaN$. This comparison is performed by using the proper
231 tts::ulp_distance overload.
232
233 @param L, R Expressions to compare.
234 @param ... Optional tag. If equals to `REQUIRED`, this test will stop the program if it fails.
235
236 @groupheader{Example}
237 @snippet doc/ieee.cpp snippet
238 **/
239 //======================================================================================================================
240 #if defined(TTS_DOXYGEN_INVOKED)
241 #define TTS_IEEE_EQUAL(L, R, ...)
242 #else
243 #define TTS_IEEE_EQUAL(L, R, ...) TTS_DO_IEEE_EQUAL(L, R, __VA_ARGS__)
244 #endif
245
246 //======================================================================================================================
247 /**
248 @}
249 **/
250 //======================================================================================================================
251