unit-tests
approx.h
Go to the documentation of this file.
1
#pragma once
2
3
#include "
catch.h
"
4
#include <limits>
5
#include <sstream>
6
#include <iomanip>
7
8
9
/*
10
11
We need to compare floating point values, therefore we need an approximation
12
function, which Catch provides for us:
13
REQUIRE( performComputation() == Approx( 2.1 ));
14
(see https://github.com/catchorg/Catch2/blob/master/docs/assertions.md)
15
For example (with the default epsilon):
16
2.61007666588 ~= 2.61007662723
17
This may not be good enough for us...
18
19
Three controls exist for the comparison:
20
- margin (absolute difference)
21
|a-b| <= margin
22
- scale - ignored for now; see below
23
- epsilon (relative difference)
24
|a-b| <= epsilon * |b|
25
26
27
Catch v1 vs v2
28
----------------
29
30
In v1, the formula for approx was:
31
|a-b| <= epsilon * (scale + max( |a|, |b| ))
32
With the default for scale being 1.
33
With v2, this changed to:
34
|a-b| <= margin || |a-b| <= epsilon * (scale + b )
35
(it's really slightly different, but the gist is the above)
36
The scale has changed to 0!
37
Note that it's now only relative to the "golden" number to which we're comparing!
38
39
40
Absolute vs relative comparisons
41
----------------------------------
42
43
Absolute and relative tolerances are tested as:
44
|a-b| <= MARGIN
45
and:
46
|a-b| <= EPSILON * max(|a|, |b|)
47
48
The absolute tolerance test fails when x and y become large, and the relative
49
tolerance test fails when they become small. It is therefore best to combine
50
the two tests together in a single test.
51
52
But this is always subject to context: generalizing here is convenient, that's all...
53
54
55
Approx to 0
56
-------------
57
58
Because the scale is 0 in v2, and the margin defaults to 0, there is essentially no
59
approximate comparison to 0! We must use a margin if we want to do this.
60
61
Which value to choose is a good question, though. Because most of our math is in
62
floats, we choose to use the float epsilon: any two numbers are deemed equal if their
63
difference is less than the smallest float number representable:
64
*/
65
#if ! defined( __APPROX_MARGIN )
66
#define __APPROX_MARGIN std::numeric_limits<float>::epsilon()
67
#endif
68
template
<
typename
F >
struct
__approx_margin
{};
69
template
<>
struct
__approx_margin
< double > {
static
constexpr
double
value
() {
return
__APPROX_MARGIN
; } };
70
template
<>
struct
__approx_margin
< float > {
static
constexpr
float
value
() {
return
__APPROX_MARGIN
* 4; } };
71
template
<
typename
F > F
approx_margin
( F ) {
return
__approx_margin< F >::value
(); }
72
73
/*
74
But note that for floats, this number is scaled up!
75
76
77
Epsilon
78
---------
79
Approx sets its epsilon to:
80
std::numeric_limits<float>::epsilon()*100
81
This might be too big.
82
83
Instead, we set the epsilon to the same as the margin, by default:
84
*/
85
#if ! defined( __APPROX_EPSILON )
86
#define __APPROX_EPSILON __APPROX_MARGIN
87
#endif
88
template
<
typename
F >
struct
__approx_epsilon
{};
89
template
<>
struct
__approx_epsilon
< double > {
static
constexpr
double
value
() {
return
__APPROX_EPSILON
; } };
90
template
<>
struct
__approx_epsilon
< float > {
static
constexpr
float
value
() {
return
__APPROX_EPSILON
* 4; } };
91
template
<
typename
F > F
approx_epsilon
( F ) {
return
__approx_epsilon< F >::value
(); }
92
/*
93
Note that this is still way smaller than the default!
94
95
96
How?
97
------
98
99
We provide our own functions to do approximate comparison:
100
REQUIRE( performComputation() == approx( 2.1 ));
101
*/
102
103
// Custom version of Approx, ==> better replaced by matchers <== for more control,
104
// but provides LRS defaults that should closely (but not exactly) match them
105
template
<
typename
F >
106
inline
Catch::Approx
approx
( F
f
)
107
{
108
return
Catch::Approx(
f
)
109
.margin(
__approx_margin< F >::value
() )
110
.epsilon(
__approx_epsilon< F >::value
() );
111
}
112
113
/*
114
115
Literals
116
----------
117
118
Note that Catch has literals that make the syntax nice:
119
using namespace Catch::literals;
120
REQUIRE( performComputation() == 2.1_a );
121
Because we have our own implementatin (and because it's more verbose) we do NOT want
122
to use the literal that Catch supplies.
123
124
125
Matchers
126
----------
127
128
The above are good, but if you want more control, matchers provide a customizable
129
comparison:
130
REQUIRE_THAT( performComputation(), approx_equals( 2.1 ));
131
Or, for more control:
132
REQUIRE_THAT( performComputation(), approx_abs( 2.1 ));
133
REQUIRE_THAT( performComputation(), approx_rel( 2.1 ));
134
Or, with the Catch matchers, even more:
135
REQUIRE_THAT( performComputation(), WithinAbs( 2.1, 0.1 )); // 2.0 -> 2.2
136
REQUIRE_THAT( performComputation(), WithinRel( 2.1, 0.05 )); // 5% from 2.1
137
REQUIRE_THAT( performComputation(), WithinUlps( 2.1, 2 )); // two epsilons from 2.1
138
These matchers are type-sensitive (float vs. double).
139
*/
140
#define approx_abs(D) \
141
Catch::Matchers::WithinAbs( (D), approx_margin((D)) )
142
#define approx_rel(D) \
143
Catch::Matchers::WithinRel( (D), approx_epsilon((D)) )
144
#define approx_equals(D) \
145
( approx_abs(D) || approx_rel(D) )
146
147
148
// Utility function to help debug precision errors:
149
// INFO( full_precision( d ) );
150
// REQUIRE( 0.0 == d );
151
template
<
class
T >
152
std::string
full_precision
( T
const
d
)
153
{
154
std::ostringstream
s
;
155
s
<< std::setprecision( std::numeric_limits< T >::max_digits10 ) <<
d
;
156
return
s
.str();
157
}
158
__approx_margin< float >::value
static constexpr float value()
Definition:
approx.h:70
string
GLsizei const GLchar *const * string
Definition:
glad/glad/glad.h:2861
__APPROX_MARGIN
#define __APPROX_MARGIN
Definition:
approx.h:66
__approx_margin< double >::value
static constexpr double value()
Definition:
approx.h:69
__approx_margin
Definition:
approx.h:68
catch.h
value
GLfloat value
Definition:
glad/glad/glad.h:2099
f
GLdouble f
Definition:
glad/glad/glad.h:1517
approx_margin
F approx_margin(F)
Definition:
approx.h:71
approx_epsilon
F approx_epsilon(F)
Definition:
approx.h:91
__approx_epsilon
Definition:
approx.h:88
full_precision
std::string full_precision(T const d)
Definition:
approx.h:152
__approx_epsilon< float >::value
static constexpr float value()
Definition:
approx.h:90
approx
Catch::Approx approx(F f)
Definition:
approx.h:106
__approx_epsilon< double >::value
static constexpr double value()
Definition:
approx.h:89
s
GLdouble s
Definition:
glad/glad/glad.h:2441
__APPROX_EPSILON
#define __APPROX_EPSILON
Definition:
approx.h:86
rmse.d
d
Definition:
rmse.py:171
librealsense2
Author(s): LibRealSense ROS Team
autogenerated on Fri Aug 2 2024 08:30:00