Skip to content

File almostequal.h

File List > code_source > templat > include > TempLat > util > almostequal.h

Go to the documentation of this file

#ifndef TEMPLAT_UTIL_ALMOSTEQUAL_H
#define TEMPLAT_UTIL_ALMOSTEQUAL_H

/* This file is part of TempLat, available at https://cosmolattice.github.io/templat .
   Copyright 2021-2026 The TempLat authors, see AUTHORS.md.
   Released under the MIT license, see LICENSE.md. */

// File info: Main contributor(s): Wessel Valkenburg, Year: 2019

#include <limits> 
#include "TempLat/parallel/device.h"

namespace TempLat
{
  template <typename T1, typename T2, typename T3 = T1>
    requires(std::is_arithmetic_v<T1> && std::is_arithmetic_v<T2> && std::is_arithmetic_v<T3>)
  DEVICE_FUNCTION bool AlmostEqual(const T1 &a, const T2 &b,
                                   const T3 &epsilon = std::sqrt(std::numeric_limits<T3>::epsilon()))
  {
    if (std::isnan(a) || std::isnan(b)) return false;
    if (a == b) return true;

    // Relative error is meaningless once either side approaches zero, so below `epsilon` we fall back
    // to comparing the difference absolutely. This has to stay symmetric in a and b: comparing
    // "is the other one also below epsilon" instead would report two values straddling epsilon as
    // unequal however well they agree -- for epsilon = 1e3 * sqrt(FLT_EPSILON) = 0.345, the pair
    // (0.345338, 0.345192) took that branch and failed despite agreeing to 4e-4 relative.
    const bool aNearZero = std::abs(a) < epsilon;
    const bool bNearZero = std::abs(b) < epsilon;
    if (aNearZero || bNearZero) return (aNearZero && bNearZero) || std::abs(a - b) < epsilon;

    // Also test the absolute difference, to catch values that are effectively equal.
    return std::abs(a / b - 1) < epsilon || std::abs(a - b) < std::numeric_limits<T3>::epsilon() * 2;
  };

  template <typename T>
  DEVICE_FUNCTION bool AlmostEqual(const complex<T> &a, const complex<T> &b,
                                   const T epsilon = std::sqrt(std::numeric_limits<T>::epsilon()))
  {
    return AlmostEqual(a.real(), b.real(), epsilon) && AlmostEqual(a.imag(), b.imag(), epsilon);
  };

  template <typename T, size_t N>
  DEVICE_FUNCTION bool AlmostEqual(const std::array<T, N> &a, const std::array<T, N> &b,
                                   const T epsilon = std::sqrt(std::numeric_limits<T>::epsilon()))
  {
    bool result = true;
    for (ptrdiff_t i = 0; i < (ptrdiff_t)N; ++i) {
      result = result && AlmostEqual(a[i], b[i], epsilon);
    }
    return result;
  };

} // namespace TempLat

#endif