.. _program_listing_file_src_approx.hpp: Program Listing for File approx.hpp =================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/approx.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* This file is part of brille. Copyright © 2019,2020 Greg Tucker brille is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. brille is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with brille. If not, see . */ #ifndef BRILLE_APPROX_HPP_ #define BRILLE_APPROX_HPP_ #include #include #include #include #include // for std::overflow_error #include #include // #include // included by debug #include #include #include "debug.hpp" namespace brille{ template std::enable_if_t, T> abs(const T x){ return std::abs(x); } template std::enable_if_t, T> abs(const T x){ return x; } namespace approx{ // // courtesy of https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon // template // std::enable_if_t::is_integer, bool> // almost_equal(T x, T y, int ulp){ // // the machine epsilon has to be scaled to the magnitude of the values used // // and multiplied by the desired precision in ULPs (units in the last place) // return std::fabs(x-y) <= std::numeric_limits::epsilon() * std::fabs(x+y) * ulp // // unless the result is subnormal // || std::fabs(x-y) < std::numeric_limits::min(); // } // A mulitplier for the approximate-comparison tolerance // 10000 is too big for Monoclinic system (5.7224, 5.70957, 4.13651),(90,90.498,90),'C -2y' // but setting it any lower (9000 tried) causes other test lattices, namely, // (7.189, 4.407, 5.069) (90,90.04,90) '-C 2y' to throw a runtime error #if defined(_MSC_VER) || defined(__MINW32__) const int TOL_MULT=20000; #else const int TOL_MULT=10000; #endif /* isfpT | isfpR | which? | why? ------|-------|--------|----- 0 0 either both Trel and Rrel are 0 1 1 Trel R is convertible to T 0 1 Rrel Trel is 0, so use Rrel 1 0 Trel Rrel is 0, so use Trel */ template std::tuple tols(const int tol=1){ T Trel = std::numeric_limits::epsilon(); // zero for integer-type T R Rrel = std::numeric_limits::epsilon(); // zero for integer-type R T Tabs = T(5)/1000000000; // 0 or 5e-9 R Rabs = R(5)/1000000000; // 0 or 5e-9 bool TorRisInteger = Trel*Rrel==0 || std::is_convertible::value; bool TisFloatingPt = Trel > 0; Trel *= static_cast(tol)*static_cast(TOL_MULT); Rrel *= static_cast(tol)*static_cast(TOL_MULT); return std::make_tuple(TorRisInteger, TisFloatingPt, Trel, Rrel, Tabs, Rabs); } /* \NOTE For future Greg: The following _* templates *CAN NOT BE PREDECLARED* in the hpp file. Doing so may not upset the compiler -- after all, it finds a suitable definition of _* for every call -- but will anger the linker since the actual definitions get overlooked entirely and the symbols never get built. Leave these templates alone if you can. If you can't try not to be clever. */ // Catchall case to capture unsigned T and unsigned R template std::enable_if_t && std::is_integral_v, bool> _scalar(const T a, const R b, const bool, const T, const R, const T, const R){ return a==b; } // Integral T and Floating Point R template std::enable_if_t && !std::is_integral_v, bool> _scalar(const T a, const R b, const bool, const T, const R Rrel, const T, const R Rabs){ // if ( a == T(0) && brille::abs(b) <= Rrel ) return true; auto x = brille::abs(a-b); return x <= Rabs + Rrel*brille::abs(a+b) || x < std::numeric_limits::min(); } // Floating Point T and Integral R template std::enable_if_t && std::is_integral_v, bool> _scalar(const T a, const R b, const bool, const T Trel, const R, const T Tabs, const R){ // if ( brille::abs(a) <= Trel && b == R(0) ) return true; auto x = brille::abs(a-b); return x <= Tabs + Trel*brille::abs(a+b) || x < std::numeric_limits::min(); } // Floating Point T and R template std::enable_if_t && !std::is_integral_v, bool> _scalar(const T a, const R b, const bool useT, const T Trel, const R Rrel, const T Tabs, const R Rabs){ // if both a and b are close to epsilon for its type, our comparison of |a-b| to |a+b| might fail auto x = brille::abs(a-b); // if ( brille::abs(a) <= Trel && brille::abs(b) <= Rrel ) // return x <= (useTrel ? Trel :Rrel); if (useT) return x <= Tabs + Trel*brille::abs(a+b) || x < std::numeric_limits::min(); else return x <= Rabs + Rrel*brille::abs(a+b) || x < std::numeric_limits::min(); } template bool scalar(const T a, const R b, const int tol=1){ auto [convertible, useT, Trel, Rrel, Tabs, Rabs] = tols(tol); return convertible && _scalar(a, b, useT, Trel, Rrel, Tabs, Rabs); } template bool _array(const size_t NM, const T* a, const R* b, const bool useT, const T Trel, const R Rrel, const T Tabs, const R Rabs){ bool answer=true; // we need <= in case T and R are integer, otherwise this is *always* false since 0 !< 0 if (useT){ T Tmin = std::numeric_limits::min(); for (size_t i=0; i::min(); for (size_t i=0; i bool array(const size_t N, const size_t M,const T *a, const R *b, const int tol=1){ auto [convertible, useT, Trel, Rrel, Tabs, Rabs] = tols(tol); return convertible && _array(N*M, a, b, useT, Trel, Rrel, Tabs, Rabs); } template bool matrix(const size_t N, const T *a, const R *b, const int tol=1){ auto [convertible, useT, Trel, Rrel, Tabs, Rabs] = tols(tol); return convertible && _array(N*N, a, b, useT, Trel, Rrel, Tabs, Rabs); } template bool vector(const size_t N, const T *a, const R *b, const int tol=1){ auto [convertible, useT, Trel, Rrel, Tabs, Rabs] = tols(tol); return convertible && _array(N, a, b, useT, Trel, Rrel, Tabs, Rabs); } template bool array(const T *a, const R *b, const int tol=1){ return brille::approx::array(N,M,a,b,tol); } template bool matrix(const T *a, const R *b, const int tol=1){ return brille::approx::matrix(N,a,b,tol); } template bool vector(const T *a, const R *b, const int tol=1){ return brille::approx::vector(N,a,b,tol); } } // approx:: } // brille:: #endif // _APPROX_HPP_