Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix up cpp_bin_float for very small bit counts. #577

Merged
merged 2 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 60 additions & 12 deletions include/boost/multiprecision/cpp_bin_float.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,13 @@ class cpp_bin_float

#ifdef BOOST_HAS_FLOAT128
template <class Float>
typename std::enable_if<std::is_same<Float, float128_type>::value, cpp_bin_float&>::type assign_float(Float f)
typename std::enable_if<std::is_same<Float, float128_type>::value && (std::numeric_limits<Float>::digits > Digits), cpp_bin_float&>::type assign_float(Float f)
{
cpp_bin_float<113, DigitBase, Allocator, Exponent, MinExponent, MaxExponent> bf(f);
return *this = bf;
}
template <class Float>
typename std::enable_if<std::is_same<Float, float128_type>::value && (std::numeric_limits<Float>::digits <= Digits), cpp_bin_float&>::type assign_float(Float f)
{
using default_ops::eval_add;
using bf_int_type = typename boost::multiprecision::detail::canonical<int, cpp_bin_float>::type;
Expand Down Expand Up @@ -337,7 +343,7 @@ class cpp_bin_float
m_sign = false;
m_exponent = 0;

constexpr std::ptrdiff_t bits = sizeof(int) * CHAR_BIT - 1;
constexpr std::ptrdiff_t bits = sizeof(int) * CHAR_BIT - 1 < MaxExponent - 1 ? sizeof(int) * CHAR_BIT - 1 : 3;
int e;
f = frexpq(f, &e);
while (f)
Expand All @@ -352,15 +358,36 @@ class cpp_bin_float
eval_add(*this, t);
}
m_exponent += static_cast<Exponent>(e);
if (m_exponent > max_exponent)
{
m_exponent = exponent_infinity;
m_data = static_cast<ui_type>(0u);
}
else if (m_exponent < min_exponent)
{
m_exponent = exponent_zero;
m_data = static_cast<ui_type>(0u);
}
return *this;
}
#endif
#ifdef BOOST_HAS_FLOAT128
template <class Float>
typename std::enable_if<std::is_floating_point<Float>::value && !std::is_same<Float, float128_type>::value, cpp_bin_float&>::type assign_float(Float f)
typename std::enable_if<std::is_floating_point<Float>::value && !std::is_same<Float, float128_type>::value && (std::numeric_limits<Float>::digits > Digits), cpp_bin_float&>::type assign_float(Float f)
#else
template <class Float>
typename std::enable_if<std::is_floating_point<Float>::value, cpp_bin_float&>::type assign_float(Float f)
typename std::enable_if<std::is_floating_point<Float>::value && (std::numeric_limits<Float>::digits > Digits), cpp_bin_float&>::type assign_float(Float f)
#endif
{
cpp_bin_float<std::numeric_limits<Float>::digits, DigitBase, Allocator, Exponent, MinExponent, MaxExponent> bf(f);
return *this = bf;
}
#ifdef BOOST_HAS_FLOAT128
template <class Float>
typename std::enable_if<std::is_floating_point<Float>::value && !std::is_same<Float, float128_type>::value && (std::numeric_limits<Float>::digits <= Digits), cpp_bin_float&>::type assign_float(Float f)
#else
template <class Float>
typename std::enable_if<std::is_floating_point<Float>::value && (std::numeric_limits<Float>::digits <= Digits), cpp_bin_float&>::type assign_float(Float f)
#endif
{
using std::frexp;
Expand Down Expand Up @@ -399,7 +426,13 @@ class cpp_bin_float
m_sign = false;
m_exponent = 0;

constexpr std::ptrdiff_t bits = sizeof(int) * CHAR_BIT - 1;
//
// This code picks off the bits in f a few at a time and injects them into *this.
// It does not do roundingm so we must have more digits precision in *this than
// in the floating point value (the normal situation, unless we're emulating another
// type like float16_t).
//
constexpr std::ptrdiff_t bits = static_cast<std::ptrdiff_t>(sizeof(int) * CHAR_BIT - 1) < static_cast<std::ptrdiff_t>(MaxExponent - 1) ? static_cast<std::ptrdiff_t>(sizeof(int) * CHAR_BIT - 1) : 3;
int e;
f = frexp(f, &e);
while (f != static_cast<Float>(0.0F))
Expand All @@ -414,6 +447,16 @@ class cpp_bin_float
eval_add(*this, t);
}
m_exponent += static_cast<Exponent>(e);
if (m_exponent > max_exponent)
{
m_exponent = exponent_infinity;
m_data = static_cast<ui_type>(0u);
}
else if(m_exponent < min_exponent)
{
m_exponent = exponent_zero;
m_data = static_cast<ui_type>(0u);
}
return *this;
}

Expand Down Expand Up @@ -514,7 +557,12 @@ class cpp_bin_float
using ar_type = typename boost::multiprecision::detail::canonical<ui_type, rep_type>::type;
m_data = static_cast<ar_type>(fi);
std::size_t shift = msb(fi);
if (shift >= bit_count)
if (shift > max_exponent)
{
m_exponent = exponent_infinity;
m_data = static_cast<limb_type>(0);
}
else if (shift >= bit_count)
{
m_exponent = static_cast<Exponent>(shift);
m_data = static_cast<ar_type>(fi >> (shift + 1 - bit_count));
Expand All @@ -524,7 +572,7 @@ class cpp_bin_float
m_exponent = static_cast<Exponent>(shift);
eval_left_shift(m_data, bit_count - shift - 1);
}
BOOST_MP_ASSERT(eval_bit_test(m_data, bit_count - 1));
BOOST_MP_ASSERT((m_exponent == exponent_infinity) || eval_bit_test(m_data, bit_count - 1));
m_sign = detail::is_negative(i);
}
return *this;
Expand Down Expand Up @@ -1281,7 +1329,7 @@ inline void eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, Mi

template <unsigned Digits, digit_base_type DigitBase, class Allocator, class Exponent, Exponent MinE, Exponent MaxE,
class Allocator2, class Exponent2, Exponent MinE2, Exponent MaxE2, class U>
inline typename std::enable_if<boost::multiprecision::detail::is_unsigned<U>::value>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res,
inline typename std::enable_if<boost::multiprecision::detail::is_unsigned<U>::value && (std::numeric_limits<U>::digits <= Digits)>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res,
const cpp_bin_float<Digits, DigitBase, Allocator2, Exponent2, MinE2, MaxE2>& u, const U& v)
{
#ifdef BOOST_MSVC
Expand Down Expand Up @@ -1396,14 +1444,14 @@ inline typename std::enable_if<boost::multiprecision::detail::is_unsigned<U>::va
}

template <unsigned Digits, digit_base_type DigitBase, class Allocator, class Exponent, Exponent MinE, Exponent MaxE, class U>
inline typename std::enable_if<boost::multiprecision::detail::is_unsigned<U>::value>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res, const U& v)
inline typename std::enable_if<boost::multiprecision::detail::is_unsigned<U>::value && (std::numeric_limits<U>::digits <= Digits)>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res, const U& v)
{
eval_divide(res, res, v);
}

template <unsigned Digits, digit_base_type DigitBase, class Allocator, class Exponent, Exponent MinE, Exponent MaxE,
class Allocator2, class Exponent2, Exponent MinE2, Exponent MaxE2, class S>
inline typename std::enable_if<boost::multiprecision::detail::is_signed<S>::value && boost::multiprecision::detail::is_integral<S>::value>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res,
inline typename std::enable_if<boost::multiprecision::detail::is_signed<S>::value && boost::multiprecision::detail::is_integral<S>::value && (std::numeric_limits<S>::digits <= Digits)>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res,
const cpp_bin_float<Digits, DigitBase, Allocator2, Exponent2, MinE2, MaxE2>& u, const S& v)
{
using ui_type = typename boost::multiprecision::detail::make_unsigned<S>::type;
Expand All @@ -1413,7 +1461,7 @@ inline typename std::enable_if<boost::multiprecision::detail::is_signed<S>::valu
}

template <unsigned Digits, digit_base_type DigitBase, class Allocator, class Exponent, Exponent MinE, Exponent MaxE, class S>
inline typename std::enable_if<boost::multiprecision::detail::is_signed<S>::value && boost::multiprecision::detail::is_integral<S>::value>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res, const S& v)
inline typename std::enable_if<boost::multiprecision::detail::is_signed<S>::value && boost::multiprecision::detail::is_integral<S>::value && (std::numeric_limits<S>::digits <= Digits)>::type eval_divide(cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>& res, const S& v)
{
eval_divide(res, res, v);
}
Expand Down Expand Up @@ -1658,7 +1706,7 @@ inline typename std::enable_if<std::is_floating_point<Float>::value>::type eval_
//
// Perform rounding first, then afterwards extract the digits:
//
cpp_bin_float<static_cast<unsigned>(float_digits), digit_base_2, Allocator, Exponent, MinE, MaxE> arg;
cpp_bin_float<static_cast<unsigned>(float_digits), digit_base_2, Allocator, Exponent, 0, 0> arg;
typename cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>::rep_type bits(original_arg.bits());
arg.exponent() = original_arg.exponent();
copy_and_round(arg, bits, (std::ptrdiff_t)digits_to_round_to);
Expand Down
2 changes: 2 additions & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ test-suite arithmetic_tests :
[ run test_arithmetic_cpp_bin_float_3.cpp no_eh_support : : : <toolset>msvc:<cxxflags>-bigobj [ check-target-builds ../config//has_float128 : <source>quadmath ] ]
[ run test_arithmetic_cpp_bin_float_4.cpp no_eh_support : : : <toolset>msvc:<cxxflags>-bigobj [ check-target-builds ../config//has_float128 : <source>quadmath ] ]
[ run test_arithmetic_cpp_bin_float_5.cpp no_eh_support : : : <toolset>msvc:<cxxflags>-bigobj [ check-target-builds ../config//has_float128 : <source>quadmath ] ]
[ run test_arithmetic_cpp_bin_float_6.cpp no_eh_support : : : <toolset>msvc:<cxxflags>-bigobj [ check-target-builds ../config//has_float128 : <source>quadmath ] [ requires cxx17_if_constexpr ] ]

[ run test_arithmetic_mpf_50.cpp gmp no_eh_support : : : [ check-target-builds ../config//has_gmp : : <build>no ] [ check-target-builds ../config//has_float128 : <source>quadmath ] ]
[ run test_arithmetic_mpf.cpp gmp no_eh_support : : : [ check-target-builds ../config//has_gmp : : <build>no ] [ check-target-builds ../config//has_float128 : <source>quadmath ] ]
Expand Down Expand Up @@ -1232,6 +1233,7 @@ test-suite misc :
[ run git_issue_526.cpp ]
[ run git_issue_540.cpp ]
[ run git_issue_573.cpp : : : <toolset>msvc:<cxxflags>-sdl ]
[ run git_issue_576.cpp : : : [ check-target-builds ../config//has_float128 : <define>TEST_FLOAT128 <source>quadmath : ] ]
[ compile git_issue_98.cpp :
[ check-target-builds ../config//has_float128 : <define>TEST_FLOAT128 <source>quadmath : ]
[ check-target-builds ../config//has_gmp : <define>TEST_GMP <source>gmp : ]
Expand Down
122 changes: 122 additions & 0 deletions test/git_issue_576.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright 2023 John Maddock. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/multiprecision/cpp_bin_float.hpp>
#include <iostream>
#include "test.hpp"

template <class Float, class F>
void test(F f, bool big)
{
Float f2(f);
if (big)
{
F tol = f * static_cast<F>(std::numeric_limits<Float>::epsilon());
F max = static_cast<F>((std::numeric_limits<Float>::max)());
F diff = static_cast<F>(f2) - f;
if (diff < 0)
diff = -diff;
if (f > max)
{
BOOST_CHECK(isinf(f2) || (diff < tol));
}
else
{
BOOST_CHECK_LE(diff, tol);
}
}
else
{
BOOST_CHECK_EQUAL(static_cast<F>(f2), f);
}
}


template <class Float>
void test()
{
std::int64_t i = static_cast<std::int64_t>(1) << (std::numeric_limits<Float>::max_exponent + 1);
Float f(i);
BOOST_CHECK_EQUAL(f, std::numeric_limits<Float>::infinity());

if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<float>::digits)
{
BOOST_CHECK_EQUAL(Float(static_cast<float>(i)), std::numeric_limits<Float>::infinity());
}
if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<double>::digits)
{
BOOST_CHECK_EQUAL(Float(static_cast<double>(i)), std::numeric_limits<Float>::infinity());
}
if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<long double>::digits)
{
BOOST_CHECK_EQUAL(Float(static_cast<long double>(i)), std::numeric_limits<Float>::infinity());
}
#ifdef BOOST_HAS_FLOAT128
if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<__float128>::digits)
{
BOOST_CHECK_EQUAL(Float(static_cast<__float128>(i)), std::numeric_limits<Float>::infinity());
}
#endif

--i;

while (i)
{
Float f2(i);
BOOST_CHECK_NE(f2, std::numeric_limits<Float>::infinity());
bool big = boost::multiprecision::msb(i) >= std::numeric_limits<Float>::digits;
if (big)
{
BOOST_CHECK_LE(static_cast<std::int64_t>(f2), i);
}
else
{
BOOST_CHECK_EQUAL(static_cast<std::int64_t>(f2), i);
}

if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<float>::digits)
{
test<Float>(static_cast<float>(i), big);
for (int exp = -1; exp >= std::numeric_limits<Float>::min_exponent; --exp)
test<Float>(std::ldexp(static_cast<float>(i), exp), big);
}
if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<double>::digits)
{
test<Float>(static_cast<double>(i), big);
for (int exp = -1; exp >= std::numeric_limits<Float>::min_exponent; --exp)
test<Float>(std::ldexp(static_cast<double>(i), exp), big);
}
if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<long double>::digits)
{
test<Float>(static_cast<long double>(i), big);
for (int exp = -1; exp >= std::numeric_limits<Float>::min_exponent; --exp)
test<Float>(std::ldexp(static_cast<long double>(i), exp), big);
}
#ifdef BOOST_HAS_FLOAT128
if (std::numeric_limits<Float>::max_exponent < std::numeric_limits<__float128>::digits)
{
test<Float>(static_cast<__float128>(i), big);
for (int exp = -1; exp >= std::numeric_limits<Float>::min_exponent; --exp)
test<Float>(ldexpq(static_cast<__float128>(i), exp), big);
}
#endif

--i;
}
}



int main()
{
using namespace boost::multiprecision;
typedef number<backends::cpp_bin_float<11, backends::digit_base_2, void, std::int16_t, -14, 15>, et_off> float16_t;
//typedef number<backends::cpp_bin_float<8, backends::digit_base_2, void, std::int16_t, -126, 127>, et_off> bfloat16_t;

test<float16_t>();
//test<bfloat16_t>();

return boost::report_errors();
}
Loading