|
| 1 | +// (C) Copyright Matt Borland 2022. |
| 2 | +// Use, modification and distribution are subject to the |
| 3 | +// Boost Software License, Version 1.0. (See accompanying file |
| 4 | +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 5 | + |
| 6 | +#ifndef BOOST_MATH_SF_FAST_FLOAT_DISTANCE |
| 7 | +#define BOOST_MATH_SF_FAST_FLOAT_DISTANCE |
| 8 | + |
| 9 | +#include <boost/math/special_functions/next.hpp> |
| 10 | +#include <boost/math/tools/throw_exception.hpp> |
| 11 | +#include <stdexcept> |
| 12 | + |
| 13 | +#if defined(BOOST_MATH_USE_FLOAT128) && !defined(BOOST_MATH_STANDALONE) |
| 14 | +#include <boost/multiprecision/float128.hpp> |
| 15 | +#include <boost/multiprecision/detail/standalone_config.hpp> |
| 16 | +#define BOOST_MATH_USE_FAST_FLOAT128 |
| 17 | +#endif |
| 18 | + |
| 19 | +namespace boost { namespace math { |
| 20 | + |
| 21 | +// https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/ |
| 22 | +// https://blog.regehr.org/archives/959 |
| 23 | +inline std::int32_t fast_float_distance(float a, float b) |
| 24 | +{ |
| 25 | + return boost::math::float_distance(a, b); |
| 26 | +} |
| 27 | + |
| 28 | +inline std::int64_t fast_float_distance(double a, double b) |
| 29 | +{ |
| 30 | + return boost::math::float_distance(a, b); |
| 31 | +} |
| 32 | + |
| 33 | +#ifdef BOOST_MATH_USE_FAST_FLOAT128 |
| 34 | +boost::multiprecision::int128_type fast_float_distance(boost::multiprecision::float128_type a, boost::multiprecision::float128_type b) |
| 35 | +{ |
| 36 | + using std::abs; |
| 37 | + using std::isfinite; |
| 38 | + |
| 39 | + constexpr boost::multiprecision::float128_type tol = BOOST_MP_QUAD_MIN; |
| 40 | + |
| 41 | + // 0, very small, and large magnitude distances all need special handling |
| 42 | + if (abs(a) == 0 || abs(b) == 0) |
| 43 | + { |
| 44 | + return 0; |
| 45 | + } |
| 46 | + else if (abs(a) < tol || abs(b) < tol) |
| 47 | + { |
| 48 | + BOOST_MATH_THROW_EXCEPTION(std::domain_error("special handling is required for tiny distances. Please use boost::math::float_distance for a slower but safe solution")); |
| 49 | + } |
| 50 | + |
| 51 | + if (!(isfinite)(a)) |
| 52 | + { |
| 53 | + BOOST_MATH_THROW_EXCEPTION(std::domain_error("Both arguments to fast_float_distnace must be finite")); |
| 54 | + } |
| 55 | + else if (!(isfinite)(b)) |
| 56 | + { |
| 57 | + BOOST_MATH_THROW_EXCEPTION(std::domain_error("Both arguments to fast_float_distnace must be finite")); |
| 58 | + } |
| 59 | + |
| 60 | + static_assert(sizeof(boost::multiprecision::int128_type) == sizeof(boost::multiprecision::float128_type)); |
| 61 | + |
| 62 | + boost::multiprecision::int128_type ai; |
| 63 | + boost::multiprecision::int128_type bi; |
| 64 | + std::memcpy(&ai, &a, sizeof(boost::multiprecision::float128_type)); |
| 65 | + std::memcpy(&bi, &b, sizeof(boost::multiprecision::float128_type)); |
| 66 | + |
| 67 | + boost::multiprecision::int128_type result = bi - ai; |
| 68 | + |
| 69 | + if (ai < 0 || bi < 0) |
| 70 | + { |
| 71 | + result = -result; |
| 72 | + } |
| 73 | + |
| 74 | + return result; |
| 75 | +} |
| 76 | + |
| 77 | +#endif |
| 78 | + |
| 79 | +}} // Namespaces |
| 80 | + |
| 81 | +#endif |
0 commit comments