Pioneer
FloatComparison.h
Go to the documentation of this file.
1 // Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #ifndef _FLOATCOMPARISON_H
5 #define _FLOATCOMPARISON_H
6 
7 #include <SDL_stdinc.h>
8 #include <limits>
9 #ifdef _MSC_VER
10 #include <float.h> // for _finite
11 #else
12 #include <cmath> // for std::isfinite
13 #endif
14 
15 // Fuzzy floating point comparisons based on:
16 // http://realtimecollisiondetection.net/blog/?p=89
17 // (absolute & relative error tolerance)
18 //
19 // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
20 // (ULP based tolerance)
21 //
22 // ULP-based tolerance implementation takes some architectural ideas from the
23 // implementation in the Google test framework, and
24 // http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison
25 
26 // provides (for float & double):
27 
28 // bool is_equal_exact(float a, float b);
29 // bool is_equal_ulps(float a, float b, int ulps = DefaultUlpTolerance);
30 // int32_t float_ulp_difference(float a, float b);
31 // bool is_equal_relative(float a, float b, float tolerance = DefaultRelTolerance());
32 // bool is_equal_absolute(float a, float b, float tolerance = DefaultAbsTolerance());
33 // bool is_equal_general(float a, float b, float tolerance = DefaultTolerance());
34 // bool is_equal_general(float a, float b, float relative_tolerance, float absolute_tolerance);
35 
36 // bool is_zero_exact(float x);
37 // bool is_zero_general(float x, float tolerance = IEEEFloatTraits<float>::DefaultRelTolerance());
38 
39 // bool is_nan(float x);
40 // bool is_finite(float x);
41 
42 // ====================================================================
43 
44 // in the following code, IEEEFloatTraits<T>::bool_type is used to limit
45 // the application of the functions by SFINAE
46 
47 template <typename T>
48 struct IEEEFloatTraits;
49 
50 // --- float function helpers
51 
52 template <typename T>
54 {
55  return (x < T(0)) ? (-x) : x;
56 }
57 
58 template <typename T>
59 inline typename IEEEFloatTraits<T>::float_type float_max(T x, T y)
60 {
61  return (y > x) ? y : x;
62 }
63 
64 template <typename T>
65 inline typename IEEEFloatTraits<T>::float_type float_max(T x, T y, T z)
66 {
67  return float_max(x, float_max(y, z));
68 }
69 
70 // --- float property helpers
71 
72 template <typename T>
74 {
75  typedef typename IEEEFloatTraits<T>::uint_type uint_type;
76  const uint_type top = IEEEFloatTraits<T>::TopBit;
77  const uint_type ebits = IEEEFloatTraits<T>::ExponentBits;
78 
79  // NaN has the exponent bits set, and at least one mantissa bit set
80  // (therefore, if you mask off the top bit, the result must be strictly greater than
81  // just the exponent bits set; if it's equal then it's just an infinity; if it's
82  // less, then it's a valid finite number)
83  return ((bits & ~top) > ebits);
84 }
85 
86 template <typename T>
88 {
89  typedef typename IEEEFloatTraits<T>::uint_type uint_type;
90  const uint_type ebits = IEEEFloatTraits<T>::ExponentBits;
91  return ((bits & ebits) != ebits);
92 }
93 
94 // --- infinity
95 
96 template <typename T>
98 {
99 #ifdef _MSC_VER
100  return _finite(x);
101 #else
102  return std::isfinite(x);
103 #endif
104 }
105 
106 // --- exact comparisons, and checking for NaN
107 
108 #ifdef __GNUC__
109 #pragma GCC diagnostic push
110 #pragma GCC diagnostic ignored "-Wfloat-equal"
111 #endif
112 inline bool is_equal_exact(float a, float b)
113 {
114  return (a == b);
115 }
116 inline bool is_equal_exact(double a, double b) { return (a == b); }
117 
118 inline bool is_zero_exact(float x) { return (x == 0.0f); }
119 inline bool is_zero_exact(double x) { return (x == 0.0); }
120 
121 inline bool is_nan(float x) { return (x != x); }
122 inline bool is_nan(double x) { return (x != x); }
123 #ifdef __GNUC__
124 #pragma GCC diagnostic pop
125 #endif
126 
127 // --- relative & absolute error comparisons
128 
129 template <typename T>
131 {
132  return (float_abs(a - b) <= tol * float_max(float_abs(a), float_abs(b)));
133 }
134 
135 template <typename T>
137 {
138  return (float_abs(a - b) <= tol);
139 }
140 
141 template <typename T>
142 inline typename IEEEFloatTraits<T>::bool_type is_equal_general(T a, T b, T rel_tol, T abs_tol)
143 {
144  return (float_abs(a - b) <= float_max(abs_tol, rel_tol * float_max(float_abs(a), float_abs(b))));
145 }
146 
147 template <typename T>
149 {
150  return (float_abs(a - b) <= tol * float_max(T(1), float_abs(a), float_abs(b)));
151 }
152 
153 template <typename T>
155 {
156  return (float_abs(x) <= tol);
157 }
158 
159 // --- ulp-based comparisons
160 
161 template <typename T>
163 {
164  typedef typename IEEEFloatTraits<T>::FloatOrInt union_type;
165  union_type afi, bfi;
166  afi.f = a;
167  bfi.f = b;
168 
169  // transform from sign-magnitude to two's-complement
170  if (afi.i < 0) afi.ui = (IEEEFloatTraits<T>::TopBit - afi.ui);
171  if (bfi.i < 0) bfi.ui = (IEEEFloatTraits<T>::TopBit - bfi.ui);
172 
173  return (bfi.i - afi.i);
174 }
175 
176 // IEEEFloatTraits<T>::bool_type used for SFINAE
177 template <typename T>
179 {
180  typedef typename IEEEFloatTraits<T>::FloatOrInt union_type;
181  typedef typename IEEEFloatTraits<T>::int_type int_type;
182  union_type afi, bfi;
183  afi.f = a;
184  bfi.f = b;
185 
186  // Infinities aren't close to anything except themselves
187  if ((!is_finite_bits<T>(afi.ui) && is_finite_bits<T>(bfi.ui)) || (is_finite_bits<T>(afi.ui) && !is_finite_bits<T>(bfi.ui)))
188  return false;
189 
190  // IEEE says NaNs are unequal to everything (even themselves)
191  if (is_nan_bits<T>(afi.ui) || is_nan_bits<T>(bfi.ui))
192  return false;
193 
194  // transform from sign-magnitude to two's-complement
195  if (afi.i < 0) afi.ui = (IEEEFloatTraits<T>::TopBit - afi.ui);
196  if (bfi.i < 0) bfi.ui = (IEEEFloatTraits<T>::TopBit - bfi.ui);
197 
198  int_type difference = (bfi.i - afi.i);
199  difference = (difference < int_type(0)) ? -difference : difference;
200  return (difference <= max_ulps);
201 }
202 
203 // ====================================================================
204 
205 template <typename T>
206 struct IEEEFloatTraits {};
207 
208 template <>
209 struct IEEEFloatTraits<double> {
210  typedef double float_type;
211  typedef bool bool_type;
212 
213  typedef int64_t int_type;
214  typedef uint64_t uint_type;
215 
216  union FloatOrInt {
217  double f;
220  };
221 
222  static const uint_type TopBit = static_cast<uint_type>(1) << (sizeof(double) * 8 - 1);
223  static const uint_type ExponentBits = (~static_cast<uint_type>(0) << std::numeric_limits<double>::digits) & ~TopBit;
224  static const uint_type MantissaBits = ~TopBit & ~ExponentBits;
225 
226  static const int_type DefaultUlpTolerance = 16;
227 
228  static double DefaultAbsTolerance() { return 1e-12; }
229  static double DefaultRelTolerance() { return 1e-6; }
230  static double DefaultTolerance() { return 1e-8; }
231  static double SmallestNormalisedValue() { return std::numeric_limits<double>::min(); }
232 };
233 
234 template <>
235 struct IEEEFloatTraits<float> {
236  typedef float float_type;
237  typedef bool bool_type;
238 
239  typedef int32_t int_type;
240  typedef uint32_t uint_type;
241 
242  union FloatOrInt {
243  float f;
246  };
247 
248  static const uint_type TopBit = uint_type(1) << (sizeof(float) * 8 - 1);
249  static const uint_type ExponentBits = (~uint_type(0) << std::numeric_limits<float>::digits) & ~TopBit;
250  static const uint_type MantissaBits = ~TopBit & ~ExponentBits;
251 
252  static const int_type DefaultUlpTolerance = 4;
253 
254  static float DefaultAbsTolerance() { return 1e-6f; }
255  static float DefaultRelTolerance() { return 1e-5f; }
256  static float DefaultTolerance() { return 1e-5f; }
257  static float SmallestNormalisedValue() { return std::numeric_limits<float>::min(); }
258 };
259 
260 #endif
IEEEFloatTraits< T >::int_type float_ulp_difference(T a, T b)
Definition: FloatComparison.h:162
IEEEFloatTraits< T >::bool_type is_finite_bits(const typename IEEEFloatTraits< T >::uint_type &bits)
Definition: FloatComparison.h:87
bool is_zero_exact(float x)
Definition: FloatComparison.h:118
IEEEFloatTraits< T >::bool_type is_equal_ulps(T a, T b, typename IEEEFloatTraits< T >::int_type max_ulps=IEEEFloatTraits< T >::DefaultUlpTolerance)
Definition: FloatComparison.h:178
IEEEFloatTraits< T >::bool_type is_equal_general(T a, T b, T rel_tol, T abs_tol)
Definition: FloatComparison.h:142
IEEEFloatTraits< T >::bool_type is_nan_bits(const typename IEEEFloatTraits< T >::uint_type &bits)
Definition: FloatComparison.h:73
IEEEFloatTraits< T >::bool_type is_equal_absolute(T a, T b, T tol=IEEEFloatTraits< T >::DefaultAbsTolerance())
Definition: FloatComparison.h:136
IEEEFloatTraits< T >::float_type float_max(T x, T y)
Definition: FloatComparison.h:59
IEEEFloatTraits< T >::float_type float_abs(T x)
Definition: FloatComparison.h:53
IEEEFloatTraits< T >::bool_type is_equal_relative(T a, T b, T tol=IEEEFloatTraits< T >::DefaultRelTolerance())
Definition: FloatComparison.h:130
IEEEFloatTraits< T >::bool_type is_finite(T x)
Definition: FloatComparison.h:97
IEEEFloatTraits< T >::bool_type is_zero_general(T x, T tol=IEEEFloatTraits< T >::DefaultRelTolerance())
Definition: FloatComparison.h:154
bool is_equal_exact(float a, float b)
Definition: FloatComparison.h:112
bool is_nan(float x)
Definition: FloatComparison.h:121
bool bool_type
Definition: FloatComparison.h:211
uint64_t uint_type
Definition: FloatComparison.h:214
static double DefaultRelTolerance()
Definition: FloatComparison.h:229
int64_t int_type
Definition: FloatComparison.h:213
double float_type
Definition: FloatComparison.h:210
static double DefaultTolerance()
Definition: FloatComparison.h:230
static double SmallestNormalisedValue()
Definition: FloatComparison.h:231
static double DefaultAbsTolerance()
Definition: FloatComparison.h:228
static float DefaultAbsTolerance()
Definition: FloatComparison.h:254
static float DefaultTolerance()
Definition: FloatComparison.h:256
int32_t int_type
Definition: FloatComparison.h:239
float float_type
Definition: FloatComparison.h:236
bool bool_type
Definition: FloatComparison.h:237
static float DefaultRelTolerance()
Definition: FloatComparison.h:255
uint32_t uint_type
Definition: FloatComparison.h:240
static float SmallestNormalisedValue()
Definition: FloatComparison.h:257
Definition: FloatComparison.h:206
int_type i
Definition: FloatComparison.h:219
uint_type ui
Definition: FloatComparison.h:218
double f
Definition: FloatComparison.h:217
int_type i
Definition: FloatComparison.h:245
uint_type ui
Definition: FloatComparison.h:244
float f
Definition: FloatComparison.h:243