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/DllConfig.h"
12#include "MantidKernel/V3D.h"
13
14#include <cmath>
15#include <limits>
16
17namespace {
18typedef std::true_type MADE_FOR_INTEGERS;
19typedef std::false_type MADE_FOR_FLOATS;
20} // namespace
21
22namespace Mantid::Kernel {
23
34template <typename T> bool equals(T const x, T const y) { return equals(x, y, std::is_integral<T>()); }
35
42template <typename T> inline bool equals(T const x, T const y, MADE_FOR_INTEGERS) { return x == y; }
43
55template <typename T> inline bool equals(T const x, T const y, MADE_FOR_FLOATS) {
56 // handle infinities
57 if (std::isinf(x) && std::isinf(y)) {
58 // if x,y both +inf, x-y=NaN; if x,y both -inf, x-y=NaN; else not an NaN
59 return std::isnan(x - y);
60 } else {
61 // produce a scale for comparison
62 // in general can use either value, but use the second to work better with near differences,
63 // which call as equals(difference, tolerance), since tolerance will more often be finite
64 int const exp = y < std::numeric_limits<T>::min() ? std::numeric_limits<T>::min_exponent - 1 : std::ilogb(y);
65 // compare to within machine epsilon
66 return std::abs(x - y) <= std::ldexp(std::numeric_limits<T>::epsilon(), exp);
67 }
68}
69
78template <typename T> MANTID_KERNEL_DLL bool ltEquals(T const x, T const y) { return (equals(x, y) || x < y); }
79
88template <typename T> MANTID_KERNEL_DLL bool gtEquals(T const x, T const y) { return (equals(x, y) || x > y); }
89
91
92#ifdef __APPLE__
93#pragma clang diagnostic push
94#pragma clang diagnostic ignored "-Wabsolute-value"
95#endif
102template <typename T> T absoluteDifference(T const x, T const y) { return std::abs(x - y); }
103#ifdef __APPLE__
104#pragma clang diagnostic pop
105#endif
106
115template <typename T> T relativeDifference(T const x, T const y) {
116 // calculate numerator |x-y|
117 T const num = absoluteDifference<T>(x, y);
118 if (num <= std::numeric_limits<T>::epsilon()) {
119 // if |x-y| == 0.0 (within machine tolerance), relative difference is zero
120 return static_cast<T>(0);
121 } else {
122 // otherwise we have to calculate the denominator
123 T const denom = static_cast<T>((std::abs(x) + std::abs(y)) / static_cast<T>(2));
124 // NOTE if we made it this far, at least one of x or y is nonzero, so denom will be nonzero
125 return num / denom;
126 }
127}
128
138template <typename T, typename S> bool withinAbsoluteDifference(T const x, T const y, S const tolerance) {
139 return withinAbsoluteDifference(x, y, tolerance, std::is_integral<T>());
140}
141
142#ifdef __APPLE__
143#pragma clang diagnostic push
144#pragma clang diagnostic ignored "-Wabsolute-value"
145#endif
146// specialization for integer types
147template <typename T, typename S>
148inline bool withinAbsoluteDifference(T const x, T const y, S const tolerance, MADE_FOR_INTEGERS) {
149 return ltEquals(static_cast<S>(std::llabs(x - y)), tolerance);
150}
151#ifdef __APPLE__
152#pragma clang diagnostic pop
153#endif
154
164template <typename T, typename S>
165inline bool withinAbsoluteDifference(T const x, T const y, S const tolerance, MADE_FOR_FLOATS) {
166 // handle the case of infinities
167 if (std::isinf(x) && std::isinf(y))
168 // if both are +inf, return true; if both -inf, return true; else false
169 return std::isnan(static_cast<S>(x - y));
170 return ltEquals(static_cast<S>(absoluteDifference<T>(x, y)), tolerance);
171}
172
182template <typename T, typename S> bool withinRelativeDifference(T const x, T const y, S const tolerance) {
183 return withinRelativeDifference(x, y, tolerance, std::is_integral<T>());
184}
185
186#ifdef __APPLE__
187#pragma clang diagnostic push
188#pragma clang diagnostic ignored "-Wabsolute-value"
189#endif
190template <typename T, typename S>
191inline bool withinRelativeDifference(T const x, T const y, S const tolerance, MADE_FOR_INTEGERS) {
192 S const num = static_cast<S>(std::llabs(x - y));
193 if (num == static_cast<S>(0)) {
194 return true;
195 } else {
196 S const denom = static_cast<S>(std::llabs(x) + std::llabs(y));
197 return num <= static_cast<S>(2) * denom * tolerance;
198 }
199}
200#ifdef __APPLE__
201#pragma clang diagnostic pop
202#endif
203
213template <typename T, typename S>
214inline bool withinRelativeDifference(T const x, T const y, S const tolerance, MADE_FOR_FLOATS) {
215 // handles the case of infinities
216 if (std::isinf(x) && std::isinf(y))
217 // if both are +inf, return true; if both -inf, return true; else false
218 return std::isnan(static_cast<S>(x - y));
219 S const num = static_cast<S>(absoluteDifference<T>(x, y));
220 if (num <= std::numeric_limits<S>::epsilon()) {
221 // if |x-y| == 0.0 (within machine tolerance), this test passes
222 return true;
223 } else {
224 // otherwise we have to calculate the denominator
225 S const denom = static_cast<S>(0.5) * static_cast<S>(std::abs(x) + std::abs(y));
226 // if denom <= 1, then |x-y| > tol implies |x-y|/denom > tol, can return early
227 // NOTE can only return early if BOTH denom > tol AND |x-y| > tol.
228 if (denom <= static_cast<S>(1) && !ltEquals(num, tolerance)) {
229 return false;
230 } else {
231 // avoid division for improved performance
232 return ltEquals(num, denom * tolerance);
233 }
234 }
235}
236
238// Concrete instantiations
239template MANTID_KERNEL_DLL bool equals<double>(double const, double const);
240template MANTID_KERNEL_DLL bool equals<float>(float const, float const);
241template MANTID_KERNEL_DLL bool ltEquals<double>(double const, double const);
242template MANTID_KERNEL_DLL bool ltEquals<float>(float const, float const);
243template MANTID_KERNEL_DLL bool gtEquals<double>(double const, double const);
244template MANTID_KERNEL_DLL bool gtEquals<float>(float const, float const);
245// difference methods
246template MANTID_KERNEL_DLL double absoluteDifference<double>(double const, double const);
247template MANTID_KERNEL_DLL float absoluteDifference<float>(float const, float const);
248template MANTID_KERNEL_DLL double relativeDifference<double>(double const, double const);
249template MANTID_KERNEL_DLL float relativeDifference<float>(float const, float const);
250// within difference methods -- object and tolerance same
251template MANTID_KERNEL_DLL bool withinAbsoluteDifference<double>(double const, double const, double const);
252template MANTID_KERNEL_DLL bool withinAbsoluteDifference<float>(float const, float const, float const);
253template MANTID_KERNEL_DLL bool withinAbsoluteDifference<int>(int const, int const, int const);
254template MANTID_KERNEL_DLL bool withinAbsoluteDifference<unsigned int>(unsigned int const, unsigned int const,
255 unsigned int const);
256template MANTID_KERNEL_DLL bool withinAbsoluteDifference<long>(long const, long const, long const);
257template MANTID_KERNEL_DLL bool withinAbsoluteDifference<long long>(long long const, long long const, long long const);
258//
259template MANTID_KERNEL_DLL bool withinRelativeDifference<double>(double const, double const, double const);
260template MANTID_KERNEL_DLL bool withinRelativeDifference<float>(float const, float const, float const);
261template MANTID_KERNEL_DLL bool withinRelativeDifference<int>(int const, int const, int const);
262template MANTID_KERNEL_DLL bool withinRelativeDifference<unsigned int>(unsigned int const, unsigned int const,
263 unsigned int const);
264template MANTID_KERNEL_DLL bool withinRelativeDifference<long>(long const, long const, long const);
265template MANTID_KERNEL_DLL bool withinRelativeDifference<long long>(long long const, long long const, long long const);
266// within difference methods -- tolerance is double
267template MANTID_KERNEL_DLL bool withinAbsoluteDifference<float, double>(float const, float const, double const);
268template MANTID_KERNEL_DLL bool withinAbsoluteDifference<int, double>(int const, int const, double const);
269template MANTID_KERNEL_DLL bool withinAbsoluteDifference<unsigned int, double>(unsigned int const, unsigned int const,
270 double const);
271template MANTID_KERNEL_DLL bool withinAbsoluteDifference<long, double>(long const, long const, double const);
272template MANTID_KERNEL_DLL bool withinAbsoluteDifference<unsigned long, double>(unsigned long const,
273 unsigned long const, double const);
274template MANTID_KERNEL_DLL bool withinAbsoluteDifference<long long, double>(long long const, long long const,
275 double const);
276template MANTID_KERNEL_DLL bool
277withinAbsoluteDifference<unsigned long long, double>(unsigned long long const, unsigned long long const, double const);
278//
279template MANTID_KERNEL_DLL bool withinRelativeDifference<float, double>(float const, float const, double const);
280template MANTID_KERNEL_DLL bool withinRelativeDifference<int, double>(int const, int const, double const);
281template MANTID_KERNEL_DLL bool withinRelativeDifference<unsigned int, double>(unsigned int const, unsigned int const,
282 double const);
283template MANTID_KERNEL_DLL bool withinRelativeDifference<long, double>(long const, long const, double const);
284template MANTID_KERNEL_DLL bool withinRelativeDifference<unsigned long, double>(unsigned long const,
285 unsigned long const, double const);
286template MANTID_KERNEL_DLL bool withinRelativeDifference<long long, double>(long long const, long long const,
287 double const);
288template MANTID_KERNEL_DLL bool
289withinRelativeDifference<unsigned long long, double>(unsigned long long const, unsigned long long const, double const);
291
293
305template <>
306MANTID_KERNEL_DLL bool withinAbsoluteDifference<V3D, double>(V3D const V1, V3D const V2, double const tolerance) {
307 // we want ||V1-V2|| < tol
308 // NOTE ||V1-V2||^2 < tol^2 --> ||V1-V2|| < tol, since both are non-negative
309 return ltEquals((V1 - V2).norm2(), tolerance * tolerance);
310}
311
323template <>
324MANTID_KERNEL_DLL bool withinRelativeDifference<V3D, double>(V3D const V1, V3D const V2, double const tolerance) {
325 // NOTE we must avoid sqrt as much as possible
326 // cppcheck-suppress variableScope
327 double tol2 = tolerance * tolerance;
328 double diff2 = (V1 - V2).norm2(), denom4;
329 if (diff2 < 1 && diff2 <= std::numeric_limits<double>::epsilon()) {
330 // make sure not due to underflow
331 double const scale = std::max({V1.X(), V1.Y(), V1.Z()});
332 if (scale < 1 && scale * scale < std::numeric_limits<double>::epsilon()) {
333 V3D const v1 = V1 / scale, v2 = V2 / scale;
334 diff2 = (v1 - v2).norm();
335 denom4 = std::sqrt(v1.norm()) * sqrt(v2.norm());
336 return ltEquals(diff2, denom4 * tolerance);
337 }
338 // if the difference has zero length, the vectors are equal, this test passes
339 return true;
340 } else {
341 // otherwise we must calculate the denominator
342 denom4 = static_cast<double>(V1.norm2() * V2.norm2());
343 // NOTE for large numbers, risk of overflow -- normalize by max(V1)
344 // ||V1-V2||/sqrt(||V1||*||V2||) * (a/a) = ||(aV1)-(aV2)||/sqrt(||aV1||*||aV2||)
345 if (std::isinf(denom4)) {
346 double const scale = 1.0 / std::max({V1.X(), V1.Y(), V1.Z(), V2.X(), V2.Y(), V2.Z()});
347 V3D const v1 = V1 * scale, v2 = V2 * scale;
348 diff2 = (v1 - v2).norm();
349 denom4 = std::sqrt(v1.norm()) * sqrt(v2.norm());
350 tol2 = tolerance;
351 }
352 // if denom <= 1, then |x-y| > tol implies |x-y|/denom > tol, can return early
353 // NOTE can only return early if BOTH denom <= 1. AND |x-y| > tol.
354 if (denom4 <= 1. && !ltEquals(diff2, tol2)) { // NOTE ||V1||^2.||V2||^2 <= 1 --> sqrt(||V1||.||V2||) <= 1
355 return false;
356 } else {
357 // avoid division and sqrt for improved performance
358 // we want ||V1-V2||/sqrt(||V1||*||V2||) < tol
359 // NOTE ||V1-V2||^4 < ||V1||^2.||V2||^2 * tol^4 --> ||V1-V2||/sqrt(||V1||*||V2||) < tol
360 return ltEquals(diff2 * diff2, denom4 * tol2 * tol2);
361 }
362 }
363}
364
365} // namespace Mantid::Kernel
double tolerance
Class for 3D vectors.
Definition V3D.h:35
constexpr double X() const noexcept
Get x.
Definition V3D.h:239
constexpr double Y() const noexcept
Get y.
Definition V3D.h:240
double norm() const noexcept
Definition V3D.h:270
constexpr double norm2() const noexcept
Vector length squared.
Definition V3D.h:272
constexpr double Z() const noexcept
Get z.
Definition V3D.h:241
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.
MANTID_KERNEL_DLL T relativeDifference(T const x, T const y)
Calculate relative difference between x, y.
MANTID_KERNEL_DLL 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.
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.
MANTID_KERNEL_DLL bool withinAbsoluteDifference< V3D, double >(V3D const V1, V3D const V2, double const tolerance)
Template specialization for V3D.