Mantid
Loading...
Searching...
No Matches
FloatingPointComparison.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//-----------------------------------------------------------------------------
8// Includes
9//-----------------------------------------------------------------------------
11#include "MantidKernel/V3D.h"
12
13#include <cmath>
14#include <limits>
15
16namespace {
17typedef std::true_type MADE_FOR_INTEGERS;
18typedef std::false_type MADE_FOR_FLOATS;
19} // namespace
20
21namespace Mantid::Kernel {
22
33template <typename T> bool equals(T const x, T const y) { return equals(x, y, std::is_integral<T>()); }
34
41template <typename T> inline bool equals(T const x, T const y, MADE_FOR_INTEGERS) { return x == y; }
42
54template <typename T> inline bool equals(T const x, T const y, MADE_FOR_FLOATS) {
55 // handle infinities
56 if (std::isinf(x) && std::isinf(y)) {
57 // if x,y both +inf, x-y=NaN; if x,y both -inf, x-y=NaN; else not an NaN
58 return std::isnan(x - y);
59 } else {
60 // produce a scale for comparison
61 // in general can use either value, but use the second to work better with near differences,
62 // which call as equals(difference, tolerance), since tolerance will more often be finite
63 int const exp = y < std::numeric_limits<T>::min() ? std::numeric_limits<T>::min_exponent - 1 : std::ilogb(y);
64 // compare to within machine epsilon
65 return std::abs(x - y) <= std::ldexp(std::numeric_limits<T>::epsilon(), exp);
66 }
67}
68
77template <typename T> MANTID_KERNEL_DLL bool ltEquals(T const x, T const y) { return (equals(x, y) || x < y); }
78
87template <typename T> MANTID_KERNEL_DLL bool gtEquals(T const x, T const y) { return (equals(x, y) || x > y); }
88
90
91#ifdef __APPLE__
92#pragma clang diagnostic push
93#pragma clang diagnostic ignored "-Wabsolute-value"
94#endif
101template <typename T> T absoluteDifference(T const x, T const y) { return std::abs(x - y); }
102#ifdef __APPLE__
103#pragma clang diagnostic pop
104#endif
105
114template <typename T> T relativeDifference(T const x, T const y) {
115 // calculate numerator |x-y|
116 T const num = absoluteDifference<T>(x, y);
117 if (num <= std::numeric_limits<T>::epsilon()) {
118 // if |x-y| == 0.0 (within machine tolerance), relative difference is zero
119 return static_cast<T>(0);
120 } else {
121 // otherwise we have to calculate the denominator
122 T const denom = static_cast<T>((std::abs(x) + std::abs(y)) / static_cast<T>(2));
123 // NOTE if we made it this far, at least one of x or y is nonzero, so denom will be nonzero
124 return num / denom;
125 }
126}
127
137template <typename T, typename S> bool withinAbsoluteDifference(T const x, T const y, S const tolerance) {
138 return withinAbsoluteDifference(x, y, tolerance, std::is_integral<T>());
139}
140
141#ifdef __APPLE__
142#pragma clang diagnostic push
143#pragma clang diagnostic ignored "-Wabsolute-value"
144#endif
145// specialization for integer types
146template <typename T, typename S>
147inline bool withinAbsoluteDifference(T const x, T const y, S const tolerance, MADE_FOR_INTEGERS) {
148 return ltEquals(static_cast<S>(std::llabs(x - y)), tolerance);
149}
150#ifdef __APPLE__
151#pragma clang diagnostic pop
152#endif
153
163template <typename T, typename S>
164inline bool withinAbsoluteDifference(T const x, T const y, S const tolerance, MADE_FOR_FLOATS) {
165 // handle the case of infinities
166 if (std::isinf(x) && std::isinf(y))
167 // if both are +inf, return true; if both -inf, return true; else false
168 return std::isnan(static_cast<S>(x - y));
169 return ltEquals(static_cast<S>(absoluteDifference<T>(x, y)), tolerance);
170}
171
181template <typename T, typename S> bool withinRelativeDifference(T const x, T const y, S const tolerance) {
182 return withinRelativeDifference(x, y, tolerance, std::is_integral<T>());
183}
184
185#ifdef __APPLE__
186#pragma clang diagnostic push
187#pragma clang diagnostic ignored "-Wabsolute-value"
188#endif
189template <typename T, typename S>
190inline bool withinRelativeDifference(T const x, T const y, S const tolerance, MADE_FOR_INTEGERS) {
191 S const num = static_cast<S>(std::llabs(x - y));
192 if (num == static_cast<S>(0)) {
193 return true;
194 } else {
195 S const denom = static_cast<S>(std::llabs(x) + std::llabs(y));
196 return num <= static_cast<S>(2) * denom * tolerance;
197 }
198}
199#ifdef __APPLE__
200#pragma clang diagnostic pop
201#endif
202
212template <typename T, typename S>
213inline bool withinRelativeDifference(T const x, T const y, S const tolerance, MADE_FOR_FLOATS) {
214 // handles the case of infinities
215 if (std::isinf(x) && std::isinf(y))
216 // if both are +inf, return true; if both -inf, return true; else false
217 return std::isnan(static_cast<S>(x - y));
218 S const num = static_cast<S>(absoluteDifference<T>(x, y));
219 if (num <= std::numeric_limits<S>::epsilon()) {
220 // if |x-y| == 0.0 (within machine tolerance), this test passes
221 return true;
222 } else {
223 // otherwise we have to calculate the denominator
224 S const denom = static_cast<S>(0.5) * static_cast<S>(std::abs(x) + std::abs(y));
225 // if denom <= 1, then |x-y| > tol implies |x-y|/denom > tol, can return early
226 // NOTE can only return early if BOTH denom > tol AND |x-y| > tol.
227 if (denom <= static_cast<S>(1) && !ltEquals(num, tolerance)) {
228 return false;
229 } else {
230 // avoid division for improved performance
231 return ltEquals(num, denom * tolerance);
232 }
233 }
234}
235
237// Concrete instantiations
238template DLLExport bool equals<double>(double const, double const);
239template DLLExport bool equals<float>(float const, float const);
240template DLLExport bool ltEquals<double>(double const, double const);
241template DLLExport bool ltEquals<float>(float const, float const);
242template DLLExport bool gtEquals<double>(double const, double const);
243template DLLExport bool gtEquals<float>(float const, float const);
244// difference methods
245template DLLExport double absoluteDifference<double>(double const, double const);
246template DLLExport float absoluteDifference<float>(float const, float const);
247template DLLExport double relativeDifference<double>(double const, double const);
248template DLLExport float relativeDifference<float>(float const, float const);
249// within difference methods -- object and tolerance same
250template DLLExport bool withinAbsoluteDifference<double>(double const, double const, double const);
251template DLLExport bool withinAbsoluteDifference<float>(float const, float const, float const);
252template DLLExport bool withinAbsoluteDifference<int>(int const, int const, int const);
253template DLLExport bool withinAbsoluteDifference<unsigned int>(unsigned int const, unsigned int const,
254 unsigned int const);
255template DLLExport bool withinAbsoluteDifference<long>(long const, long const, long const);
256template DLLExport bool withinAbsoluteDifference<long long>(long long const, long long const, long long const);
257//
258template DLLExport bool withinRelativeDifference<double>(double const, double const, double const);
259template DLLExport bool withinRelativeDifference<float>(float const, float const, float const);
260template DLLExport bool withinRelativeDifference<int>(int const, int const, int const);
261template DLLExport bool withinRelativeDifference<unsigned int>(unsigned int const, unsigned int const,
262 unsigned int const);
263template DLLExport bool withinRelativeDifference<long>(long const, long const, long const);
264template DLLExport bool withinRelativeDifference<long long>(long long const, long long const, long long const);
265// within difference methods -- tolerance is double
266template DLLExport bool withinAbsoluteDifference<float, double>(float const, float const, double const);
267template DLLExport bool withinAbsoluteDifference<int, double>(int const, int const, double const);
268template DLLExport bool withinAbsoluteDifference<unsigned int, double>(unsigned int const, unsigned int const,
269 double const);
270template DLLExport bool withinAbsoluteDifference<long, double>(long const, long const, double const);
271template DLLExport bool withinAbsoluteDifference<unsigned long, double>(unsigned long const, unsigned long const,
272 double const);
273template DLLExport bool withinAbsoluteDifference<long long, double>(long long const, long long const, double const);
274template DLLExport bool withinAbsoluteDifference<unsigned long long, double>(unsigned long long const,
275 unsigned long long const, double const);
276//
277template DLLExport bool withinRelativeDifference<float, double>(float const, float const, double const);
278template DLLExport bool withinRelativeDifference<int, double>(int const, int const, double const);
279template DLLExport bool withinRelativeDifference<unsigned int, double>(unsigned int const, unsigned int const,
280 double const);
281template DLLExport bool withinRelativeDifference<long, double>(long const, long const, double const);
282template DLLExport bool withinRelativeDifference<unsigned long, double>(unsigned long const, unsigned long const,
283 double const);
284template DLLExport bool withinRelativeDifference<long long, double>(long long const, long long const, double const);
285template DLLExport bool withinRelativeDifference<unsigned long long, double>(unsigned long long const,
286 unsigned long long const, double const);
288
290
302template <> DLLExport bool withinAbsoluteDifference<V3D, double>(V3D const V1, V3D const V2, double const tolerance) {
303 // we want ||V1-V2|| < tol
304 // NOTE ||V1-V2||^2 < tol^2 --> ||V1-V2|| < tol, since both are non-negative
305 return ltEquals((V1 - V2).norm2(), tolerance * tolerance);
306}
307
319template <> DLLExport bool withinRelativeDifference<V3D, double>(V3D const V1, V3D const V2, double const tolerance) {
320 // NOTE we must avoid sqrt as much as possible
321 // cppcheck-suppress variableScope
322 double tol2 = tolerance * tolerance;
323 double diff2 = (V1 - V2).norm2(), denom4;
324 if (diff2 < 1 && diff2 <= std::numeric_limits<double>::epsilon()) {
325 // make sure not due to underflow
326 double const scale = std::max({V1.X(), V1.Y(), V1.Z()});
327 if (scale < 1 && scale * scale < std::numeric_limits<double>::epsilon()) {
328 V3D const v1 = V1 / scale, v2 = V2 / scale;
329 diff2 = (v1 - v2).norm();
330 denom4 = std::sqrt(v1.norm()) * sqrt(v2.norm());
331 return ltEquals(diff2, denom4 * tolerance);
332 }
333 // if the difference has zero length, the vectors are equal, this test passes
334 return true;
335 } else {
336 // otherwise we must calculate the denominator
337 denom4 = static_cast<double>(V1.norm2() * V2.norm2());
338 // NOTE for large numbers, risk of overflow -- normalize by max(V1)
339 // ||V1-V2||/sqrt(||V1||*||V2||) * (a/a) = ||(aV1)-(aV2)||/sqrt(||aV1||*||aV2||)
340 if (std::isinf(denom4)) {
341 double const scale = 1.0 / std::max({V1.X(), V1.Y(), V1.Z(), V2.X(), V2.Y(), V2.Z()});
342 V3D const v1 = V1 * scale, v2 = V2 * scale;
343 diff2 = (v1 - v2).norm();
344 denom4 = std::sqrt(v1.norm()) * sqrt(v2.norm());
345 tol2 = tolerance;
346 }
347 // if denom <= 1, then |x-y| > tol implies |x-y|/denom > tol, can return early
348 // NOTE can only return early if BOTH denom <= 1. AND |x-y| > tol.
349 if (denom4 <= 1. && !ltEquals(diff2, tol2)) { // NOTE ||V1||^2.||V2||^2 <= 1 --> sqrt(||V1||.||V2||) <= 1
350 return false;
351 } else {
352 // avoid division and sqrt for improved performance
353 // we want ||V1-V2||/sqrt(||V1||*||V2||) < tol
354 // NOTE ||V1-V2||^4 < ||V1||^2.||V2||^2 * tol^4 --> ||V1-V2||/sqrt(||V1||*||V2||) < tol
355 return ltEquals(diff2 * diff2, denom4 * tol2 * tol2);
356 }
357 }
358}
359
360} // namespace Mantid::Kernel
double tolerance
#define DLLExport
Definitions of the DLLImport compiler directives for MSVC.
Definition System.h:37
Class for 3D vectors.
Definition V3D.h:34
constexpr double X() const noexcept
Get x.
Definition V3D.h:238
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
double norm() const noexcept
Definition V3D.h:269
constexpr double norm2() const noexcept
Vector length squared.
Definition V3D.h:271
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
MANTID_KERNEL_DLL bool withinRelativeDifference(T const x, T const y, S const tolerance)
Test whether x, y are within relative tolerance tol.
MANTID_KERNEL_DLL bool equals(T const x, T const y)
Test for equality of doubles using compiler-defined precision.
MANTID_KERNEL_DLL bool gtEquals(T const x, T const y)
Test whether x>=y within machine precision.
MANTID_KERNEL_DLL T absoluteDifference(T const x, T const y)
Calculate absolute difference between x, y.
DLLExport bool withinAbsoluteDifference< V3D, double >(V3D const V1, V3D const V2, double const tolerance)
Template specialization for V3D.
MANTID_KERNEL_DLL T relativeDifference(T const x, T const y)
Calculate relative difference between x, y.
MANTID_KERNEL_DLL bool ltEquals(T const x, T const y)
Test whether x<=y within machine precision.
MANTID_KERNEL_DLL bool withinAbsoluteDifference(T const x, T const y, S const tolerance)
Test whether x, y are within absolute tolerance tol.
DLLExport bool withinRelativeDifference< V3D, double >(V3D const V1, V3D const V2, double const tolerance)
Compare 3D vectors (class V3D) for relative difference to within the given tolerance.