Skip to content
This repository was archived by the owner on Feb 22, 2024. It is now read-only.

Commit 69dfb5b

Browse files
authored
Merge pull request #1 from robocin/fuzzy_compare
Add fuzzy_compare library
2 parents 3547d27 + 06f0842 commit 69dfb5b

11 files changed

Lines changed: 633 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ In progress...
3333

3434
## Codemap
3535

36-
In progress...
36+
- [`utility`](robocin/utility): a collection of utility and helper code.
3737

3838
<a name="contributing"></a>
3939

robocin/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_subdirectory(utility)

robocin/utility/CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
if (NOT ROBOCIN_FLOAT_EPSILON AND NOT ROBOCIN_DOUBLE_EPSILON AND NOT ROBOCIN_LONG_DOUBLE_EPSILON)
2+
message(WARNING "No epsilon value has been set: ROBOCIN_FLOAT_EPSILON, ROBOCIN_DOUBLE_EPSILON, ROBOCIN_LONG_DOUBLE_EPSILON.\n"
3+
"It is recommended to set epsilon values to avoid numerical instabilities.\n"
4+
"We suggest to set epsilon values to:\n"
5+
" -- 1e-2F for float;\n"
6+
" -- 1e-4 for double;\n"
7+
" -- 1e-6L for long double.")
8+
endif ()
9+
10+
robocin_cpp_library(
11+
NAME type_traits
12+
HDRS type_traits.h
13+
SRCS type_traits.cpp
14+
)
15+
16+
robocin_cpp_library(
17+
NAME concepts
18+
HDRS concepts.h
19+
SRCS concepts.cpp
20+
)
21+
22+
robocin_cpp_library(
23+
NAME fuzzy_compare
24+
HDRS fuzzy_compare.h
25+
SRCS fuzzy_compare.cpp
26+
CONFIGS epsilon.h.in
27+
DEPS type_traits concepts
28+
)

robocin/utility/README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# utility
2+
3+
A collection of utility functions and classes.
4+
5+
## Table of Contents
6+
7+
- [concepts](#concepts)
8+
- [epsilon](#epsilon)
9+
- [fuzzy_compare](#fuzzy_compare)
10+
- [type_traits](#type_traits)
11+
12+
<a name="concepts"></a>
13+
14+
## [`concepts`](concepts.h)
15+
16+
The [concepts](concepts.h) header provides a set of concepts extended the standard library.
17+
18+
- `arithmetic`: a concept that represents a type that is either an integral or floating point type;
19+
20+
<a name="epsilon"></a>
21+
22+
## [`epsilon`](epsilon.h.in)
23+
24+
The [epsilon](epsilon.h.in) is a [CMake configure file](https://cmake.org/cmake/help/latest/command/configure_file.html)
25+
that defines the epsilon value passed to CMake during project build through the `ROBOCIN_FLOAT_EPSILON`,
26+
`ROBOCIN_DOUBLE_EPSILON` and `ROBOCIN_LONG_DOUBLE_EPSILON` flags (see [CMakeLists.txt](CMakeLists.txt)).
27+
28+
These values will be used by the library to perform real number comparisons.
29+
30+
* The existence of these values can be verified as follows:
31+
32+
```cpp
33+
robocin::has_epsilon<float>::value;
34+
robocin::has_epsilon<double>::value;
35+
robocin::has_epsilon<long double>::value;
36+
```
37+
38+
or:
39+
40+
```cpp
41+
robocin::has_epsilon_v<float>;
42+
robocin::has_epsilon_v<double>;
43+
robocin::has_epsilon_v<long double>;
44+
```
45+
46+
* When provided, you can access them without using:
47+
48+
```cpp
49+
robocin::has_epsilon<float>::epsilon;
50+
robocin::has_epsilon<double>::epsilon;
51+
robocin::has_epsilon<long double>::epsilon;
52+
```
53+
54+
or:
55+
56+
```cpp
57+
robocin::epsilon_v<float>;
58+
robocin::epsilon_v<double>;
59+
robocin::epsilon_v<long double>;
60+
```
61+
62+
<a name="fuzzy_compare"></a>
63+
64+
## [`fuzzy_compare`](fuzzy_compare.h)
65+
66+
The [fuzzy_compare](fuzzy_compare.h) header provides a set of functions to perform fuzzy comparisons and functor classes
67+
to be used with the standard library algorithms. There are many ways to compare real numbers, but for the applications
68+
that RobôCIn and other robotics teams use, the way chosen is to compare the absolute difference against
69+
a [tolerance margin](#epsilon):
70+
71+
```cpp
72+
// returns true if a floating point number 'is zero':
73+
std::abs(value) <= epsilon;
74+
75+
// returns true if two floating point numbers 'are equal':
76+
std::abs(lhs - rhs) <= epsilon;
77+
```
78+
79+
- `fuzzyIsZero`: returns true if a floating point number is close to zero;
80+
- `fuzzyCmpEqual`: returns true if two floating point numbers are close to each other;
81+
- `fuzzyCmpNotEqual`: returns true if two floating point numbers are not close to each other;
82+
- `fuzzyCmpThreeWay`: returns true if two floating point numbers are close to each other, returning a C++20 three-way
83+
comparison result (see
84+
[Three-way comparison](https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison));
85+
- `fuzzyCmpLess`: returns true if a floating point number is less than another;
86+
- `fuzzyCmpLessEqual`: returns true if a floating point number is less than or equal to another;
87+
- `fuzzyCmpGreater`: returns true if a floating point number is greater than another;
88+
- `fuzzyCmpGreaterEqual`: returns true if a floating point number is greater than or equal to another.
89+
- Functors:
90+
- `FuzzyIsZero`: functor that returns true if a floating point number is close to zero;
91+
- `FuzzyEqualTo`: functor that returns true if two floating point numbers are close to each other;
92+
- `FuzzyNotEqualTo`: functor that returns true if two floating point numbers are not close to each other;
93+
- `FuzzyThreeWay`: functor to perform a three-way comparison between two floating point numbers;
94+
- `FuzzyLess`: functor that returns true if a floating point number is less than another;
95+
- `FuzzyLessEqual`: functor that returns true if a floating point number is less than or equal to another;
96+
- `FuzzyGreater`: functor that returns true if a floating point number is greater than another;
97+
- `FuzzyGreaterEqual`: functor that returns true if a floating point number is greater than or equal to another.
98+
99+
> **Note**: The `fuzzy*` functions / `Fuzzy*` functors with implicit epsilon are only available when
100+
> the [epsilon](#epsilon) is defined.
101+
> Otherwise, you must explicitly pass the epsilon value to the function / during construction.
102+
103+
<a name="type_traits"></a>
104+
105+
## [`type_traits`](type_traits.h)
106+
107+
The [type_traits](type_traits.h) header provides a set of type traits extended the standard library.
108+
109+
- `common_floating_point_for_comparison`: a type trait that represents the common floating point type for comparison
110+
between two arithmetic types (the tolerance of the lowest precision floating point is prioritized);
111+
- `common_floating_point_for_comparison_t`: a helper alias for `common_floating_point_for_comparison::type`;

robocin/utility/concepts.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//
2+
// Created by José Cruz <joseviccruz> on 07/04/23.
3+
// Copyright (c) 2023 RobôCIn.
4+
//
5+
6+
#include "robocin/utility/concepts.h"

robocin/utility/concepts.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// Created by José Cruz <joseviccruz> on 07/04/23.
3+
// Copyright (c) 2023 RobôCIn.
4+
//
5+
6+
#ifndef ROBOCIN_UTILITY_CONCEPTS_H
7+
#define ROBOCIN_UTILITY_CONCEPTS_H
8+
9+
#include <concepts>
10+
11+
namespace robocin {
12+
13+
template <class T>
14+
concept arithmetic = std::is_arithmetic_v<T>;
15+
16+
} // namespace robocin
17+
18+
#endif // ROBOCIN_UTILITY_CONCEPTS_H

robocin/utility/epsilon.h.in

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// Created by José Cruz <joseviccruz> on 24/02/23.
3+
// Copyright (c) 2023 RobôCIn.
4+
// Auto generated by CMake.
5+
//
6+
7+
#ifndef ROBOCIN_UTILITY_EPSILON_H
8+
#define ROBOCIN_UTILITY_EPSILON_H
9+
10+
#include <type_traits>
11+
12+
// clang-format off
13+
#cmakedefine ROBOCIN_FLOAT_EPSILON ${ROBOCIN_FLOAT_EPSILON} // Injected by CMake.
14+
15+
#cmakedefine ROBOCIN_DOUBLE_EPSILON ${ROBOCIN_DOUBLE_EPSILON} // Injected by CMake.
16+
17+
#cmakedefine ROBOCIN_LONG_DOUBLE_EPSILON ${ROBOCIN_LONG_DOUBLE_EPSILON} // Injected by CMake.
18+
// clang-format on
19+
20+
namespace robocin {
21+
22+
template <class T>
23+
struct has_epsilon : std::false_type {}; // NOLINT(readability-identifier-naming)
24+
25+
template <class T>
26+
inline constexpr bool has_epsilon_v = has_epsilon<T>::value;
27+
28+
template <class T>
29+
inline constexpr std::enable_if_t<has_epsilon<T>::value, T> epsilon_v = has_epsilon<T>::epsilon;
30+
31+
#if defined(ROBOCIN_FLOAT_EPSILON)
32+
template <>
33+
struct has_epsilon<float> : std::true_type {
34+
static constexpr float epsilon = ROBOCIN_FLOAT_EPSILON;
35+
36+
static_assert(0.0F < epsilon and epsilon < 1.0F, "float epsilon must be in the range (0, 1).");
37+
};
38+
#endif
39+
40+
#if defined(ROBOCIN_DOUBLE_EPSILON)
41+
template <>
42+
struct has_epsilon<double> : std::true_type {
43+
static constexpr double epsilon = ROBOCIN_DOUBLE_EPSILON;
44+
45+
static_assert(0.0 < epsilon and epsilon < 1.0, "double epsilon must be in the range (0, 1).");
46+
};
47+
#endif
48+
49+
#if defined(ROBOCIN_LONG_DOUBLE_EPSILON)
50+
template <>
51+
struct has_epsilon<long double> : std::true_type {
52+
static constexpr long double epsilon = ROBOCIN_LONG_DOUBLE_EPSILON;
53+
54+
static_assert(0.0L < epsilon and epsilon < 1.0L,
55+
"long double epsilon must be in the range (0, 1).");
56+
};
57+
#endif
58+
59+
} // namespace robocin
60+
61+
#endif // ROBOCIN_UTILITY_EPSILON_H

robocin/utility/fuzzy_compare.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Created by José Cruz <joseviccruz> on 08/04/23.
3+
// Copyright (c) 2023 RobôCIn.
4+
//
5+
6+
#include "robocin/utility/fuzzy_compare.h"
7+
8+
namespace robocin {
9+
10+
template class FuzzyIsZero<float>;
11+
template class FuzzyIsZero<double>;
12+
template class FuzzyIsZero<long double>;
13+
14+
template class FuzzyEqualTo<float>;
15+
template class FuzzyEqualTo<double>;
16+
template class FuzzyEqualTo<long double>;
17+
18+
template class FuzzyNotEqualTo<float>;
19+
template class FuzzyNotEqualTo<double>;
20+
template class FuzzyNotEqualTo<long double>;
21+
22+
template class FuzzyThreeWay<float>;
23+
template class FuzzyThreeWay<double>;
24+
template class FuzzyThreeWay<long double>;
25+
26+
template class FuzzyLess<float>;
27+
template class FuzzyLess<double>;
28+
template class FuzzyLess<long double>;
29+
30+
template class FuzzyLessEqual<float>;
31+
template class FuzzyLessEqual<double>;
32+
template class FuzzyLessEqual<long double>;
33+
34+
template class FuzzyGreater<float>;
35+
template class FuzzyGreater<double>;
36+
template class FuzzyGreater<long double>;
37+
38+
template class FuzzyGreaterEqual<float>;
39+
template class FuzzyGreaterEqual<double>;
40+
template class FuzzyGreaterEqual<long double>;
41+
42+
} // namespace robocin

0 commit comments

Comments
 (0)