Skip to content

Commit

Permalink
Fix integral constant issues with ldexp
Browse files Browse the repository at this point in the history
Signed-off-by: Ian <[email protected]>
  • Loading branch information
Rinzii committed Jan 13, 2025
1 parent ce3e691 commit 0d16ffe
Show file tree
Hide file tree
Showing 12 changed files with 57 additions and 42 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
-Wextra
-Wconversion
-Wpedantic
#-Wwrite-strings
-fpermissive # TODO: Figure out how to best remove this
-g3
# Define NOMINMAX only on Windows to avoid conflicts with min/max macros
$<$<BOOL:${WIN32}>:-DNOMINMAX>
)
Expand Down
2 changes: 1 addition & 1 deletion include/ccmath/internal/config/type_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
// actually support __int128.
#ifdef CCM_TYPES_HAS_INT128
#error CCM_TYPES_HAS_INT128 cannot be directly set
#elif defined(__SIZEOF_INT128__)
#elif defined(__SIZEOF_INT128__) && !defined(_WIN32)
#if (defined(__clang__) && !defined(_WIN32)) || (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
(defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
#define CCM_TYPES_HAS_INT128 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ namespace ccm::gen::impl
}

// Scale lower part of 2^(hi + mid)
DoubleDouble exp2_hi_mid_dd;
DoubleDouble exp2_hi_mid_dd{};
exp2_hi_mid_dd.lo =
(idx_y != 0) ? support::bit_cast<double>(exp_hi_i + support::bit_cast<int64_t>(support::constants::EXP2_MID1.at(idx_y).mid)) : 0.0;
exp2_hi_mid_dd.hi = exp2_hi_mid;
Expand Down
7 changes: 4 additions & 3 deletions include/ccmath/internal/support/bits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@

namespace ccm::support
{

template <typename To, typename From>
[[nodiscard]] constexpr std::enable_if_t<
inline constexpr std::enable_if_t<
sizeof(To) == sizeof(From) && std::is_trivially_constructible_v<To> && std::is_trivially_copyable_v<To> && std::is_trivially_copyable_v<From>, To>
// ReSharper disable once CppDFAUnreachableFunctionCall
bit_cast(const From & from) noexcept
bit_cast(const From & from)
{
return __builtin_bit_cast(To, from);
}
Expand All @@ -45,6 +45,7 @@ namespace ccm::support
}

// TODO: Have the below function replace all other top_bits func.
// TODO: Remove all usages of bit grabbing functions

template <typename T, std::size_t TopBitsToTake, std::enable_if_t<std::is_floating_point_v<T> && !std::is_same_v<T, long double>, bool> = true>
constexpr std::uint32_t top_bits(T x) noexcept
Expand Down
2 changes: 1 addition & 1 deletion include/ccmath/internal/support/fp/fp_bits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ namespace ccm::support::fp
constexpr FPBits() = default;

template <typename XType>
constexpr explicit FPBits(XType x)
inline constexpr explicit FPBits(XType x)
{
using UnQual = std::remove_cv_t<XType>;
if constexpr (std::is_same_v<UnQual, T>) { BASE::bits = support::bit_cast<storage_type>(x); }
Expand Down
4 changes: 2 additions & 2 deletions include/ccmath/internal/support/helpers/internal_ldexp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace ccm::support::helpers
fp::FPBits<T> bits(x);
if (CCM_UNLIKELY((exp == 0) || bits.is_zero() || bits.is_inf_or_nan())) { return x; }

constexpr int exponent_limit = fp::FPBits<T>::max_biased_exponent + support::fp::FPBits<T>::fraction_length + 1;
constexpr int exponent_limit = fp::FPBits<T>::max_biased_exponent + fp::FPBits<T>::fraction_length + 1;
static_assert(exponent_limit <= INT_MAX && -exponent_limit >= INT_MIN);

// Make sure that we can safely cast exp to int when not returning early.
Expand Down Expand Up @@ -70,6 +70,6 @@ namespace ccm::support::helpers
// For all other values, NormalFloat to T conversion handles it the right way.
types::DyadicFloat<fp::FPBits<T>::storage_length> normal(bits.get_val());
normal.exponent += static_cast<int>(exp);
return normal.template as<T, /*ShouldRaiseExceptions=*/true>();
return normal.template as<T, false>();
}
} // namespace ccm::support::helpers
20 changes: 14 additions & 6 deletions include/ccmath/internal/support/math_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@

#include <climits>

#include "ccmath/internal/predef/compiler_suppression/gcc_compiler_suppression.hpp"

// clang-format off
CCM_DISABLE_GCC_WARNING(-Wpedantic)
// clang-format on

namespace ccm::support
{

Expand Down Expand Up @@ -96,15 +102,15 @@ namespace ccm::support
}

#define RETURN_IF(TYPE, BUILTIN) \
if constexpr (std::is_same_v<T, TYPE>) return BUILTIN(a, b, carry_in, carry_out);
if constexpr (std::is_same_v<T, TYPE>) { return BUILTIN(a, b, carry_in, &carry_out); }

// Returns the result of 'a + b' taking into account 'carry_in'.
// The carry out is stored in 'carry_out' it not 'nullptr', dropped otherwise.
// We keep the pass by pointer interface for consistency with the intrinsic.
template <typename T>
[[nodiscard]] constexpr std::enable_if_t<traits::ccm_is_unsigned_v<T>, T> add_with_carry(T a, T b, T carry_in, T & carry_out)
{
if (!is_constant_evaluated())
if constexpr (!is_constant_evaluated())
{
#if CCM_HAS_BUILTIN(__builtin_addcb)
RETURN_IF(unsigned char, __builtin_addcb)
Expand All @@ -126,10 +132,10 @@ namespace ccm::support
}

// Returns the result of 'a - b' taking into account 'carry_in'.
// The carry out is stored in 'carry_out' it not 'nullptr', dropped otherwise.
// The carry out is stored in 'carry_out' if not 'nullptr', dropped otherwise.
// We keep the pass by pointer interface for consistency with the intrinsic.
template <typename T>
[[nodiscard]] constexpr std::enable_if_t<traits::ccm_is_unsigned_v<T>, T> sub_with_borrow(T a, T b, T carry_in, T & carry_out)
[[nodiscard]] inline constexpr std::enable_if_t<traits::ccm_is_unsigned_v<T>, T> sub_with_borrow(T a, T b, T carry_in, T & carry_out)
{
if (!is_constant_evaluated())
{
Expand Down Expand Up @@ -217,7 +223,7 @@ namespace ccm::support
* @param b
*/

template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
template <typename T, std::enable_if_t<traits::ccm_is_floating_point_v<T>, bool> = true>
static constexpr void fast_two_sum(T & hi, T & lo, T a, T b)
{
hi = a + b;
Expand All @@ -226,7 +232,7 @@ namespace ccm::support
}

/* Algorithm 2 from https://hal.science/hal-01351529 */
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
template <typename T, std::enable_if_t<traits::ccm_is_floating_point_v<T>, bool> = true>
static constexpr void two_sum(T & s, T & t, T a, T b)
{
s = a + b;
Expand All @@ -251,3 +257,5 @@ namespace ccm::support
}

} // namespace ccm::support

CCM_RESTORE_GCC_WARNING()
13 changes: 11 additions & 2 deletions include/ccmath/internal/support/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,17 @@ namespace ccm::support::traits

/// is_unsigned

template <typename T> struct ccm_is_unsigned : and_<ccm_is_arithmetic<T>, not_<ccm_is_signed<T>>>::type {};
template <typename T> constexpr bool ccm_is_unsigned_v = ccm_is_unsigned<T>::value;
//template <typename T> struct ccm_is_unsigned : and_<ccm_is_arithmetic<T>, not_<ccm_is_signed<T>>>::type {};
//template <typename T> constexpr bool ccm_is_unsigned_v = ccm_is_unsigned<T>::value;

template <typename T>
struct ccm_is_unsigned : std::bool_constant<(ccm_is_arithmetic_v<T> && (T(-1) > T(0)))> {
constexpr operator bool() const { return ccm_is_unsigned::value; }
constexpr bool operator()() const { return ccm_is_unsigned::value; }
};
template <typename T>
constexpr bool ccm_is_unsigned_v = ccm_is_unsigned<T>::value;



/// is_unsigned_integer custom trait
Expand Down
15 changes: 9 additions & 6 deletions include/ccmath/internal/types/big_int.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include "ccmath/internal/support/math_support.hpp"
#include "ccmath/internal/support/type_traits.hpp"

#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -794,10 +793,13 @@ namespace ccm::types
*
* @return True if all parts of the BigInt are zero, false otherwise.
*/
[[nodiscard]] constexpr bool is_zero() const
constexpr bool is_zero() const
{
// If at any point this operation see's a value that is not zero, it will return false.
return std::none_of(val.begin(), val.end(), [](auto part) { return part != 0; });
for (auto part : val)
{
if (part != 0) { return false; }
}
return true;
}

/**
Expand Down Expand Up @@ -1483,14 +1485,15 @@ namespace ccm::types
/// Specialization of the bits.hpp header for BigInt types.
namespace ccm::support
{

template <typename To, typename From>
constexpr std::enable_if_t<
(sizeof(To) == sizeof(From)) && std::is_trivially_copyable_v<To> && std::is_trivially_copyable_v<From> && ccm::types::is_big_int<To>::value, To>
(sizeof(To) == sizeof(From)) && std::is_trivially_copyable_v<To> && std::is_trivially_copyable_v<From> && types::is_big_int<To>::value, To>
bit_cast(const From & from)
{
To out;
using Storage = decltype(out.val);
out.val = ccm::support::bit_cast<Storage>(from);
out.val = support::bit_cast<Storage>(from);
return out;
}

Expand Down
23 changes: 6 additions & 17 deletions include/ccmath/internal/types/dyadic_float.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ namespace ccm::types

constexpr DyadicFloat() = default;

template <typename T, std::enable_if_t<ccm::support::traits::ccm_is_floating_point_v<T>, bool> = true>
constexpr explicit DyadicFloat(T x)
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
constexpr DyadicFloat(T x)
{
static_assert(support::fp::FPBits<T>::fraction_length < Bits);
support::fp::FPBits<T> x_bits(x);
Expand All @@ -107,7 +107,7 @@ namespace ccm::types
{
if (!mantissa.is_zero())
{
int shift_length = support::countl_zero(mantissa);
const int shift_length = support::countl_zero(mantissa);
exponent -= shift_length;
mantissa <<= static_cast<std::size_t>(shift_length);
}
Expand Down Expand Up @@ -194,7 +194,7 @@ namespace ccm::types
*/

template <typename T, bool ShouldSignalExceptions,
typename = std::enable_if_t<ccm::support::traits::ccm_is_floating_point_v<T> && (support::fp::FPBits<T>::fraction_length < Bits), void>>
typename = std::enable_if_t<std::is_floating_point_v<T> && (support::fp::FPBits<T>::fraction_length < Bits), void>>
constexpr T fast_as() const
{
if (CCM_UNLIKELY(mantissa.is_zero())) { return support::fp::FPBits<T>::zero(sign).get_val(); }
Expand Down Expand Up @@ -251,7 +251,7 @@ namespace ccm::types
const bool sticky_bit = !(mantissa & sticky_mask).is_zero();
int round_and_sticky = (static_cast<int>(round_bit) * 2) + static_cast<int>(sticky_bit);

T d_lo;
T d_lo{}; // GCC will lose its mind if {} is not used here.

if (CCM_UNLIKELY(exp_lo <= 0))
{
Expand Down Expand Up @@ -346,18 +346,7 @@ namespace ccm::types
return as<T, /*ShouldSignalExceptions=*/false>();
}

/**
* @brief Converts this DyadicFloat to its underlying mantissa type with exponent adjustments.
*
* Interprets the DyadicFloat as an integer-like value by applying the exponent shift
* and sign bit. In other words, it shifts the mantissa left (or right) by \c exponent,
* and if the sign is negative, it converts the result to its two's complement form.
* This can be used to retrieve the raw integer representation implied by the DyadicFloat.
*
* @return The \c mantissa_type value derived from the floating-point-like representation.
* Returns 0 if the internal mantissa is zero.
*/
explicit constexpr operator mantissa_type() const
constexpr mantissa_type as_mantissa_type() const
{
if (mantissa.is_zero()) { return 0; }

Expand Down
4 changes: 2 additions & 2 deletions include/ccmath/math/fmanip/ldexp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ namespace ccm
* @see https://en.cppreference.com/w/cpp/numeric/math/ldexp
*/
template <typename T, std::enable_if_t<!std::is_integral_v<T>, bool> = true>
constexpr T ldexp(T num, int exp) noexcept
constexpr T ldexp(T num, int exp)
{
if constexpr (ccm::builtin::has_constexpr_ldexp<T>) { return builtin::ldexp(num, exp); }
else { return support::helpers::internal_ldexp(num, exp); }
Expand Down Expand Up @@ -129,7 +129,7 @@ namespace ccm
* @see https://en.cppreference.com/w/cpp/numeric/math/ldexp
*/
template <typename Integer, std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
constexpr double ldexp(Integer num, int exp) noexcept
constexpr double ldexp(Integer num, int exp)
{
return ccm::ldexp<double>(static_cast<double>(num), exp);
}
Expand Down
4 changes: 3 additions & 1 deletion test/fmanip/ldexp_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

// NOLINTBEGIN

static_assert(ccm::support::helpers::internal_ldexp(static_cast<float>(1.0), 2) == ccm::support::helpers::internal_ldexp(static_cast<float>(1.0), 2),
"ldexp failed static_assert test");

template <typename T>
class CcmathFmanipTests : public ::testing::Test
{
Expand All @@ -28,7 +31,6 @@ TYPED_TEST(CcmathFmanipTests, LdexpStaticAssert)
{
// TODO: IanP: Figure out why this static_assert fails
// EXPECT_EQ(ccm::ldexp(static_cast<TypeParam>(1.0), 0), 3.0);
// static_assert(ccm::ldexp(static_cast<TypeParam>(1.0), 0) == ccm::ldexp(static_cast<TypeParam>(1.0), 0), "ldexp failed static_assert test");
}

TYPED_TEST(CcmathFmanipTests, LdexpBasic)
Expand Down

0 comments on commit 0d16ffe

Please sign in to comment.