jaulib v1.3.0
Jau Support Library (C++, Java, ..)
test_math_float.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020-2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <cassert>
25#include <cinttypes>
26#include <cstring>
27#include <cmath>
28
30
31#include <jau/float_math.hpp>
32#include <jau/basic_types.hpp>
33
34using namespace jau;
35
36static const float MACH_EPSILON = jau::machineEpsilon<float>();
37static const double MACH_EPSILON_DOUBLE = jau::machineEpsilon<double>();
38
41static const float POSITIVE_INFINITY = std::numeric_limits<float>::infinity();
42static const float NEGATIVE_INFINITY = -std::numeric_limits<float>::infinity();
43static const float NaN = std::numeric_limits<float>::quiet_NaN();
44
45// static const double MIN_VALUE_DBL = std::numeric_limits<double>::min();
46// static const double MAX_VALUE_DBL = std::numeric_limits<double>::max();
47static const double POSITIVE_INFINITY_DBL = std::numeric_limits<double>::infinity();
48static const double NEGATIVE_INFINITY_DBL = -std::numeric_limits<double>::infinity();
49static const double NaN_DBL = std::numeric_limits<double>::quiet_NaN();
50
55
58 // by definition not equal: REQUIRE( NaN == jau::float_value( jau::float_iec559_nan_bitval ) );
59
60 REQUIRE( true == std::isinf( jau::float_value( jau::float_iec559_positive_inf_bitval ) ) );
61 REQUIRE( true == std::isinf( jau::float_value( jau::float_iec559_negative_inf_bitval ) ) );
62 REQUIRE( true == std::isnan( jau::float_value( jau::float_iec559_nan_bitval ) ) );
63}
68
71 // by definition not equal: REQUIRE( NaN_DBL == jau::double_value( jau::double_iec559_nan_bitval ) );
72
73 REQUIRE( true == std::isinf( jau::double_value( jau::double_iec559_positive_inf_bitval ) ) );
74 REQUIRE( true == std::isinf( jau::double_value( jau::double_iec559_negative_inf_bitval ) ) );
75 REQUIRE( true == std::isnan( jau::double_value( jau::double_iec559_nan_bitval ) ) );
76}
77template<class T,
78 std::enable_if_t<!std::numeric_limits<T>::is_integer, bool> = true>
79void testFloatBits(const T a) {
80 typename jau::uint_bytes<sizeof(T)>::type a_bits = jau::bit_value(a);
81 const float a2 = jau::float_value(a_bits);
82 REQUIRE( a == a2 );
83}
84template<class T,
85 std::enable_if_t<!std::numeric_limits<T>::is_integer, bool> = true>
86void testFloatBits(const T a, const typename jau::uint_bytes<sizeof(T)>::type exp_a_bits) {
87 const typename jau::uint_bytes<sizeof(T)>::type a_bits = jau::bit_value(a);
88 REQUIRE( exp_a_bits == a_bits );
89 const float a2 = jau::float_value(a_bits);
90 REQUIRE( a == a2 );
91}
92
93TEST_CASE( "Float IEEE 754 (IEC 559) Test 00", "[math][datatype][float][iec559]" ) {
94 fprintf(stderr, "float: ieee 754 / iec559: has %d\n",
95 std::numeric_limits<float>::is_iec559);
96
97 fprintf(stderr, "float: +infinity: has %d, value 0x%X =?= 0x%X: %d\n",
98 std::numeric_limits<float>::has_infinity,
102
103 fprintf(stderr, "float: -infinity: has %d, value 0x%X =?= 0x%X: %d\n",
104 std::numeric_limits<float>::has_infinity,
108
109 fprintf(stderr, "float: quiet NAN: has %d, value 0x%X =?= 0x%X: %d\n",
110 std::numeric_limits<float>::has_quiet_NaN,
114
115 if( std::numeric_limits<float>::is_iec559 ) {
118 }
119 testFloatBits(0.0f);
120 testFloatBits(0.0f, 0);
123 testFloatBits(std::numeric_limits<float>::lowest());
124}
125
126TEST_CASE( "Float Epsilon Test 01", "[math][datatype][float][epsilon]" ) {
127 static float epsilon_f0 = std::numeric_limits<float>::epsilon();
128 static double epsilon_d0 = std::numeric_limits<double>::epsilon();
129
130 static float epsilon_f1 = MACH_EPSILON;
131 static double epsilon_d1 = MACH_EPSILON_DOUBLE;
132
133 float epsilon_fd = epsilon_f1 - epsilon_f0;
134 double epsilon_dd = epsilon_d1 - epsilon_d0;
135
136 fprintf(stderr, "std::numeric_limits<float>::epsilon() : %e\n", epsilon_f0);
137 fprintf(stderr, "std::numeric_limits<double>::epsilon() : %le\n", epsilon_d0);
138 fprintf(stderr, "jau::machineEpsilon<float>() : %e\n", epsilon_f1);
139 fprintf(stderr, "jau::machineEpsilon<double>() : %le\n", epsilon_d1);
140
141 fprintf(stderr, "float: approximation - numeric_limits : %e\n", epsilon_fd);
142 fprintf(stderr, "double: approximation - numeric_limits : %le\n", epsilon_dd);
143
144 REQUIRE(jau::equals(epsilon_f1, epsilon_f0, 5, epsilon_f0));
145 REQUIRE(jau::equals(epsilon_d1, epsilon_d0, 5, epsilon_d0));
146}
147
148//
149// Zero
150//
151
152static void testZeroWithEpsilon1(const int tstNum, const bool exp, const float a, const float epsilon=std::numeric_limits<float>::epsilon()) {
153 const bool zero = jau::is_zero(a, epsilon);
154 const float delta = a-0.0f;
155 fprintf(stderr, "Zero.WE.%d: a: %f, -> d %f, exp %d, zero %d, epsilon %f\n",
156 tstNum, a, delta, exp, zero, epsilon);
157 REQUIRE(exp == zero);
158}
159static void testZeroWithEpsilon1(int i, const float epsilon=std::numeric_limits<float>::epsilon()) {
160 testZeroWithEpsilon1(i++, true, 0.0f, epsilon);
161 testZeroWithEpsilon1(i++, true, 0.0f-epsilon/2.0f, epsilon);
162 testZeroWithEpsilon1(i++, true, 0.0f+epsilon/2.0f, epsilon);
163 testZeroWithEpsilon1(i++, true, 0.0f-MIN_VALUE, epsilon);
164 testZeroWithEpsilon1(i++, true, 0.0f+MIN_VALUE, epsilon);
165 testZeroWithEpsilon1(i++, true, -0.0f, epsilon);
166 testZeroWithEpsilon1(i++, true, +0.0f, epsilon);
167
168 testZeroWithEpsilon1(i++, false, 0.0f+epsilon+MIN_VALUE, epsilon);
169 testZeroWithEpsilon1(i++, false, 0.0f-epsilon-MIN_VALUE, epsilon);
170}
171TEST_CASE( "Float Zero Fixed Epsilon Test 10", "[math][datatype][float][epsilon]" ) {
173}
174TEST_CASE( "Float Zero Mach Epsilon Test 11", "[math][datatype][float][epsilon]" ) {
176}
177
178static void testZeroNoEpsilon(const int tstNum, const bool exp, const float a) {
179 const bool zero = jau::is_zero_raw(a);
180 const float delta = a-0.0f;
181 fprintf(stderr, "Zero.NE.%d: a: %f, -> d %f, exp %d, zero %d\n",
182 tstNum, a, delta, exp, zero);
183 REQUIRE(exp == zero);
184}
185TEST_CASE( "Float Zero No Epsilon Test 12", "[math][datatype][float][epsilon]" ) {
186 int i = 400;
187 testZeroNoEpsilon(i++, true, 0.0f);
188 testZeroNoEpsilon(i++, false, 0.0f-MIN_VALUE);
189 testZeroNoEpsilon(i++, false, 0.0f+MIN_VALUE);
190 testZeroNoEpsilon(i++, true, -0.0f);
191 testZeroNoEpsilon(i++, true, +0.0f);
192
193 testZeroNoEpsilon(i++, false, 0.0f+MIN_VALUE);
194 testZeroNoEpsilon(i++, false, 0.0f-MIN_VALUE);
195}
196
197//
198// Equals
199//
200
201static void testEqualsWithEpsilon(const int tstNum, const bool exp, const float a, const float b, const float epsilon=std::numeric_limits<float>::epsilon()) {
202 const bool equal = jau::equals(a, b, epsilon);
203 const int comp = jau::compare(a, b, epsilon);
204 const bool comp_eq = 0 == comp;
205 const float delta = a-b;
206 fprintf(stderr, "Equal.WE.%d: a: %f, b: %f -> d %f, exp %d, equal %d, comp %d, epsilon %f\n",
207 tstNum, a, b, delta, exp, equal, comp, epsilon);
208 REQUIRE(exp == comp_eq);
209 REQUIRE(exp == equal);
210}
211
212static void testEqualsWithEpsilon(int i, const float epsilon=std::numeric_limits<float>::epsilon()) {
213 testEqualsWithEpsilon(i++, true, 0.0f, 0.0f, epsilon);
214 testEqualsWithEpsilon(i++, true, 1.0f, 1.0f-epsilon/2.0f, epsilon);
215 testEqualsWithEpsilon(i++, true, 1.0f, 1.0f+epsilon/2.0f, epsilon);
216 testEqualsWithEpsilon(i++, true, 1.0f, 1.0f-MIN_VALUE, epsilon);
217 testEqualsWithEpsilon(i++, true, 1.0f, 1.0f+MIN_VALUE, epsilon);
218 testEqualsWithEpsilon(i++, true, MAX_VALUE, MAX_VALUE, epsilon);
219 testEqualsWithEpsilon(i++, true, MIN_VALUE, MIN_VALUE, epsilon);
222 testEqualsWithEpsilon(i++, true, NaN, NaN, epsilon);
223 testEqualsWithEpsilon(i++, true, -0.0f, 0.0f, epsilon);
224 testEqualsWithEpsilon(i++, true, 0.0f, -0.0f, epsilon);
225
226 testEqualsWithEpsilon(i++, false, 1.0f, 1.0f+epsilon+MIN_VALUE, epsilon);
227 testEqualsWithEpsilon(i++, false, 1.0f, 1.0f-epsilon-MIN_VALUE, epsilon);
228}
229
230TEST_CASE( "Float Equals Fixed Epsilon Test 20", "[math][datatype][float][epsilon]" ) {
232}
233
234TEST_CASE( "Float Equals Mach Epsilon Test 21", "[math][datatype][float][epsilon]" ) {
236}
237
238static void testEqualsNoEpsilon(const int tstNum, const bool exp, const float a, const float b) {
239 const bool equal = jau::equals_raw(a, b);
240 const int comp = jau::compare(a, b);
241 const bool comp_eq = 0 == comp;
242 const float delta = a-b;
243 fprintf(stderr, "Equal.NE.%d: a: %f, b: %f -> d %f, exp %d, equal %d, comp %d\n",
244 tstNum, a, b, delta, exp, equal, comp);
245 REQUIRE(exp == comp_eq);
246 REQUIRE(exp == equal);
247}
248
249TEST_CASE( "Float Equals No Epsilon Test 22", "[math][datatype][float][epsilon]" ) {
250 int i=0;
251 testEqualsNoEpsilon(i++, true, 0.0f, 0.0f);
252
257 testEqualsNoEpsilon(i++, true, NaN, NaN);
258 testEqualsNoEpsilon(i++, false, -0.0f, 0.0f);
259 testEqualsNoEpsilon(i++, false, 0.0f, -0.0f);
260}
261
262
263//
264// Compare
265//
266
267static void testCompareNoEpsilon(const int tstNum, const int exp, const float a, const float b) {
268 const bool equal = jau::equals_raw(a, b);
269 const int comp = jau::compare(a, b);
270 const float delta = a-b;
271 const uint32_t a_bits = jau::bit_value(a);
272 const uint32_t b_bits = jau::bit_value(b);
273 const int32_t a_sbits = static_cast<int32_t>(a_bits);
274 const int32_t b_sbits = static_cast<int32_t>(b_bits);
275 fprintf(stderr, "Comp.NE.%d: a: %f 0x%X %d, b: %f 0x%X %d -> d %f, equal %d, comp: exp %d, has %d\n",
276 tstNum, a, a_bits, a_sbits, b, b_bits, b_sbits, delta, equal, exp, comp);
277 REQUIRE(exp == comp);
278}
279
280TEST_CASE( "Float Compare Zero Epsilon Test 10", "[math][datatype][float][epsilon]" ) {
281 int i=0;
282 testCompareNoEpsilon(i++, 0, 0.0f, 0.0f);
285
286 testCompareNoEpsilon(i++, 1, 1.0f, 0.0f);
287 testCompareNoEpsilon(i++, -1, 0.0f, 1.0f);
288 testCompareNoEpsilon(i++, 1, 0.0f, -1.0f);
289 testCompareNoEpsilon(i++, -1, -1.0f, 0.0f);
290
293
294 testCompareNoEpsilon(i++, -1, -0.0f, 0.0f);
295 testCompareNoEpsilon(i++, 1, 0.0f, -0.0f);
296
297 if( std::numeric_limits<float>::has_infinity ) {
302 }
303
304 if( std::numeric_limits<float>::has_quiet_NaN ) {
305 testCompareNoEpsilon(i++, 0, NaN, NaN); // NAN
306 testCompareNoEpsilon(i++, -1, 0.0f, NaN);
307 testCompareNoEpsilon(i++, 1, NaN, 0.0f);
308 }
309}
310
311static void testCompareWithEpsilon(const int tstNum, const int exp, const float a, const float b, const float epsilon) {
312 const bool equal = jau::equals(a, b, epsilon);
313 const int comp = jau::compare(a, b, epsilon);
314 const float delta = a-b;
315 fprintf(stderr, "Comp.WE.%d: a: %f, b: %f -> d %f, equal %d, comp: exp %d, has %d\n",
316 tstNum, a, b, delta, equal, exp, comp);
317 REQUIRE(exp == comp);
318}
319
320static void test05CompareWithEpsilon(int i, const float epsilon) {
321 testCompareWithEpsilon(i++, 0, 0.0f, 0.0f, epsilon);
322 testCompareWithEpsilon(i++, 0, 1.0f, 1.0f-epsilon/2.0f, epsilon);
323 testCompareWithEpsilon(i++, 0, 1.0f, 1.0f+epsilon/2.0f, epsilon);
324 testCompareWithEpsilon(i++, 0, 1.0f, 1.0f-MIN_VALUE, epsilon);
325 testCompareWithEpsilon(i++, 0, 1.0f, 1.0f+MIN_VALUE, epsilon);
326 testCompareWithEpsilon(i++, 0, MAX_VALUE, MAX_VALUE, epsilon);
327 testCompareWithEpsilon(i++, 0, MIN_VALUE, MIN_VALUE, epsilon);
328
329 testCompareWithEpsilon(i++, 1, 1.0f, 0.0f, epsilon);
330 testCompareWithEpsilon(i++, -1, 0.0f, 1.0f, epsilon);
331 testCompareWithEpsilon(i++, 1, 0.0f, -1.0f, epsilon);
332 testCompareWithEpsilon(i++, -1, -1.0f, 0.0f, epsilon);
333
334 testCompareWithEpsilon(i++, 1, MAX_VALUE, MIN_VALUE, epsilon);
335 testCompareWithEpsilon(i++, -1, MIN_VALUE, MAX_VALUE, epsilon);
336
337 testCompareWithEpsilon(i++, 0, -0.0f, 0.0f, epsilon);
338 testCompareWithEpsilon(i++, 0, 0.0f, -0.0f, epsilon);
339
340 if( std::numeric_limits<float>::has_infinity ) {
345 }
346
347 if( std::numeric_limits<float>::has_quiet_NaN ) {
348 testCompareWithEpsilon(i++, 0, NaN, NaN, epsilon); // NAN
349 testCompareWithEpsilon(i++, -1, 0.0f, NaN, epsilon);
350 testCompareWithEpsilon(i++, 1, NaN, 0.0f, epsilon);
351 }
352}
353
354TEST_CASE( "Float Compare Fixed Epsilon Test 20", "[math][datatype][float][epsilon]" ) {
355 test05CompareWithEpsilon(100, std::numeric_limits<float>::epsilon());
356}
357
358TEST_CASE( "Float Compare Mac Epsilon Test 21", "[math][datatype][float][epsilon]" ) {
360}
361
constexpr uint32_t bit_value(const float a) noexcept
Returns the unsigned integer representation according to IEEE 754 (IEC 559) single floating-point bit...
Definition: float_math.hpp:174
constexpr uint32_t const float_iec559_positive_inf_bitval
Positive infinity bit-value of IEEE 754 (IEC 559) single float-point bit layout, i....
Definition: float_math.hpp:66
std::enable_if< std::is_floating_point_v< T >, bool >::type constexpr is_zero_raw(const T &a) noexcept
Returns true if the given value is zero, disregarding epsilon but considering NaN,...
Definition: float_math.hpp:286
constexpr float float_value(const uint32_t a) noexcept
Converting IEEE 754 (IEC 559) single floating-point bit layout to float, see bit_value()
Definition: float_math.hpp:182
std::enable_if< std::is_floating_point_v< T >, bool >::type constexpr is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
Definition: float_math.hpp:255
constexpr uint32_t const float_iec559_nan_bitval
NaN bit-value of IEEE 754 (IEC 559) single float-point bit layout, i.e.
Definition: float_math.hpp:72
std::enable_if< std::is_floating_point_v< T >, bool >::type constexpr equals(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if both values are equal, i.e.
Definition: float_math.hpp:394
std::enable_if< std::is_floating_point_v< T >, bool >::type constexpr equals_raw(const T &a, const T &b) noexcept
Returns true if both values are equal disregarding epsilon but considering NaN, -Inf and +Inf.
Definition: float_math.hpp:371
constexpr uint32_t const float_iec559_negative_inf_bitval
Negative infinity bit-value of IEEE 754 (IEC 559) single float-point bit layout, i....
Definition: float_math.hpp:69
jau::uint_bytes< sizeof(T)>::type bit_value_raw(const T a) noexcept
Returns the unsigned integer representation according to IEEE 754 (IEC 559) floating-point bit layout...
Definition: float_math.hpp:129
constexpr int compare(const T a, const T b) noexcept
Returns -1, 0 or 1 if a is less, equal or greater than b, disregarding epsilon but considering NaN,...
Definition: float_math.hpp:307
constexpr uint64_t const double_iec559_negative_inf_bitval
Negative infinity bit-value of IEEE 754 (IEC 559) double double-point bit layout, i....
Definition: float_math.hpp:87
constexpr double double_value(const uint64_t a) noexcept
Converting IEEE 754 (IEC 559) double floating-point bit layout to double, see bit_value()
Definition: float_math.hpp:228
constexpr uint64_t const double_iec559_nan_bitval
NaN bit-value of IEEE 754 (IEC 559) double double-point bit layout, i.e.
Definition: float_math.hpp:90
constexpr uint64_t const double_iec559_positive_inf_bitval
Positive infinity bit-value of IEEE 754 (IEC 559) double double-point bit layout, i....
Definition: float_math.hpp:84
constexpr T min(const T x, const T y) noexcept
Returns the minimum of two integrals (w/ branching) in O(1)
Definition: base_math.hpp:177
constexpr T max(const T x, const T y) noexcept
Returns the maximum of two integrals (w/ branching) in O(1)
Definition: base_math.hpp:191
constexpr const jau::fraction_i64 zero(0l, 1lu)
zero is 0/1
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
static void testZeroWithEpsilon1(const int tstNum, const bool exp, const float a, const float epsilon=std::numeric_limits< float >::epsilon())
void testIEC559DoubleType()
static void testZeroNoEpsilon(const int tstNum, const bool exp, const float a)
void testFloatBits(const T a)
static const double NEGATIVE_INFINITY_DBL
static const double NaN_DBL
static const double POSITIVE_INFINITY_DBL
static void testCompareNoEpsilon(const int tstNum, const int exp, const float a, const float b)
static void testEqualsNoEpsilon(const int tstNum, const bool exp, const float a, const float b)
static const float MAX_VALUE
void testIEC559FloatType()
static const float MACH_EPSILON
static const float POSITIVE_INFINITY
static void testCompareWithEpsilon(const int tstNum, const int exp, const float a, const float b, const float epsilon)
static void testEqualsWithEpsilon(const int tstNum, const bool exp, const float a, const float b, const float epsilon=std::numeric_limits< float >::epsilon())
static void test05CompareWithEpsilon(int i, const float epsilon)
static const float MIN_VALUE
static const float NEGATIVE_INFINITY
static const double MACH_EPSILON_DOUBLE
static const float NaN
TEST_CASE("Float IEEE 754 (IEC 559) Test 00", "[math][datatype][float][iec559]")