Skip to content
Prev Previous commit
Next Next commit
Add standalone support
  • Loading branch information
mborland committed Oct 15, 2022
commit 0a333eb92d2342eb2bd4fd4057584c9ffe59a017
53 changes: 52 additions & 1 deletion include/boost/math/special_functions/fast_float_distance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
#include <boost/math/special_functions/next.hpp>
#include <boost/math/tools/throw_exception.hpp>
#include <stdexcept>
#include <limits>

#if defined(BOOST_MATH_USE_FLOAT128) && !defined(BOOST_MATH_STANDALONE)
#include <boost/multiprecision/float128.hpp>
#include <boost/multiprecision/detail/standalone_config.hpp>
#define BOOST_MATH_USE_FAST_FLOAT128
#elif defined(BOOST_MATH_USE_FLOAT128) && defined(BOOST_MATH_STANDALONE)
# if __has_include(<quadmath.h>)
# include <quadmath.h>
# define BOOST_MATH_USE_FAST_STANDALONE_FLOAT128
# endif
#endif

namespace boost { namespace math {
Expand All @@ -36,7 +42,7 @@ boost::multiprecision::int128_type fast_float_distance(boost::multiprecision::fl
using std::abs;
using std::isfinite;

constexpr boost::multiprecision::float128_type tol = BOOST_MP_QUAD_MIN;
constexpr boost::multiprecision::float128_type tol = 2 * BOOST_MP_QUAD_MIN;

// 0, very small, and large magnitude distances all need special handling
if (abs(a) == 0 || abs(b) == 0)
Expand Down Expand Up @@ -74,6 +80,51 @@ boost::multiprecision::int128_type fast_float_distance(boost::multiprecision::fl
return result;
}

#elif defined(BOOST_MATH_USE_FAST_STANDALONE_FLOAT128)
__int128 fast_float_distance(__float128 a, __float128 b)
{
constexpr __float128 tol = 2 * static_cast<float128_type>(1) * static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) *
static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) *
static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) *
static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) *
static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) *
static_cast<float128_type>(DBL_MIN) * static_cast<float128_type>(DBL_MIN) / 1073741824;

// 0, very small, and large magnitude distances all need special handling
if (::fabsq(a) == 0 || ::fabsq(b) == 0)
{
return 0;
}
else if (::fabsq(a) < tol || ::fabsq(b) < tol)
{
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"));
}

if (!(::isinfq)(a) && !(::isnanq)(a))
{
BOOST_MATH_THROW_EXCEPTION(std::domain_error("Both arguments to fast_float_distnace must be finite"));
}
else if (!(::isinfq)(b) && !(::isnanq)(b))
{
BOOST_MATH_THROW_EXCEPTION(std::domain_error("Both arguments to fast_float_distnace must be finite"));
}

static_assert(sizeof(__int128) == sizeof(__float128));

__int128 ai;
__int128 bi;
std::memcpy(&ai, &a, sizeof(__float128));
std::memcpy(&bi, &b, sizeof(__float128));

__int128 result = bi - ai;

if (ai < 0 || bi < 0)
{
result = -result;
}

return result;
}
#endif

}} // Namespaces
Expand Down
6 changes: 0 additions & 6 deletions include/boost/math/special_functions/next.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@
#include <cstdint>
#include <cstring>

#if defined(BOOST_MATH_USE_FLOAT128) && !defined(BOOST_MATH_STANDALONE)
#include <boost/multiprecision/float128.hpp>
#include <boost/multiprecision/detail/standalone_config.hpp>
#define BOOST_MATH_USE_FAST_FLOAT128
#endif

#if !defined(_CRAYC) && !defined(__CUDACC__) && (!defined(__GNUC__) || (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ > 3)))
#if (defined(_M_IX86_FP) && (_M_IX86_FP >= 2)) || defined(__SSE2__)
#include "xmmintrin.h"
Expand Down
17 changes: 10 additions & 7 deletions test/test_fast_float_distance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <boost/math/special_functions/next.hpp>
#include <boost/math/special_functions/ulp.hpp>
#include <boost/math/special_functions/fast_float_distance.hpp>
#include <boost/math/tools/assert.hpp>
#include <boost/multiprecision/float128.hpp>

#include "math_unit_test.hpp"
Expand All @@ -19,16 +20,16 @@ void test_value(const T& val)
{
using namespace boost::math;

assert(fast_float_distance(float_next(val), val) == -1);
assert(float_next(val) > val);
assert(float_next(float_prior(val)) == val);
BOOST_MATH_ASSERT(fast_float_distance(float_next(val), val) == -1);
BOOST_MATH_ASSERT(float_next(val) > val);
BOOST_MATH_ASSERT(float_next(float_prior(val)) == val);

assert(fast_float_distance(float_advance(val, 4), val) == -4);
assert(fast_float_distance(float_advance(val, -4), val) == 4);
BOOST_MATH_ASSERT(fast_float_distance(float_advance(val, 4), val) == -4);
BOOST_MATH_ASSERT(fast_float_distance(float_advance(val, -4), val) == 4);
if(std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't the fast float distance unconditionally require denorm support?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just type punning so as long as the floating point and fixed width integer are the same number of bits you should be fine. I don't know of a platform without denorm support to test on.

{
assert(fast_float_distance(float_advance(float_next(float_next(val)), 4), float_next(float_next(val))) == -4);
assert(fast_float_distance(float_advance(float_next(float_next(val)), -4), float_next(float_next(val))) == 4);
BOOST_MATH_ASSERT(fast_float_distance(float_advance(float_next(float_next(val)), 4), float_next(float_next(val))) == -4);
BOOST_MATH_ASSERT(fast_float_distance(float_advance(float_next(float_next(val)), -4), float_next(float_next(val))) == 4);
}
}

Expand All @@ -40,5 +41,7 @@ int main(void)
#ifdef BOOST_MATH_USE_FAST_FLOAT128
test_value(boost::multiprecision::float128_type(0));
test_value(__float128(0));
#elif defined(BOOST_MATH_USE_FAST_STANDALONE_FLOAT128)
test_value(__float128(0));
#endif
}