Reuse num_significand_bits

This commit is contained in:
Victor Zverovich 2022-03-14 11:19:13 -07:00
parent 9a1beab574
commit f91f61cd13
3 changed files with 42 additions and 52 deletions

View File

@ -215,18 +215,6 @@ template <typename T> struct bits {
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
};
template <typename Float> constexpr bool has_implicit_bit() {
return std::numeric_limits<Float>::digits != 64;
}
// Returns the number of significand bits in Float excluding the implicit bit.
template <typename Float> constexpr int num_significand_bits() {
// Subtract 1 to account for an implicit most significant bit in the
// normalized form.
return std::numeric_limits<Float>::digits -
(has_implicit_bit<Float>() ? 1 : 0);
}
// A floating-point number f * pow(2, e).
template <typename F> struct basic_fp {
F f;
@ -259,9 +247,8 @@ template <typename F> struct basic_fp {
const carrier_uint significand_mask = implicit_bit - 1;
auto u = bit_cast<carrier_uint>(n);
f = static_cast<uint64_t>(u & significand_mask);
int biased_e =
static_cast<int>((u & exponent_mask<Float>()) >>
dragonbox::float_info<Float>::significand_bits);
int biased_e = static_cast<int>((u & exponent_mask<Float>()) >>
detail::num_significand_bits<Float>());
// The predecessor is closer if n is a normalized power of 2 (f == 0) other
// than the smallest normalized number (biased_e > 1).
bool is_predecessor_closer = f == 0 && biased_e > 1;
@ -1021,22 +1008,21 @@ template <> struct cache_accessor<float> {
static carrier_uint compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
return static_cast<carrier_uint>(
(cache - (cache >> (float_info<float>::significand_bits + 2))) >>
(64 - float_info<float>::significand_bits - 1 - beta));
(cache - (cache >> (num_significand_bits<float>() + 2))) >>
(64 - num_significand_bits<float>() - 1 - beta));
}
static carrier_uint compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
return static_cast<carrier_uint>(
(cache + (cache >> (float_info<float>::significand_bits + 1))) >>
(64 - float_info<float>::significand_bits - 1 - beta));
(cache + (cache >> (num_significand_bits<float>() + 1))) >>
(64 - num_significand_bits<float>() - 1 - beta));
}
static carrier_uint compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
return (static_cast<carrier_uint>(
cache >>
(64 - float_info<float>::significand_bits - 2 - beta)) +
cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
1) /
2;
}
@ -1780,21 +1766,20 @@ template <> struct cache_accessor<double> {
static carrier_uint compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
return (cache.high() -
(cache.high() >> (float_info<double>::significand_bits + 2))) >>
(64 - float_info<double>::significand_bits - 1 - beta);
(cache.high() >> (num_significand_bits<double>() + 2))) >>
(64 - num_significand_bits<double>() - 1 - beta);
}
static carrier_uint compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
return (cache.high() +
(cache.high() >> (float_info<double>::significand_bits + 1))) >>
(64 - float_info<double>::significand_bits - 1 - beta);
(cache.high() >> (num_significand_bits<double>() + 1))) >>
(64 - num_significand_bits<double>() - 1 - beta);
}
static carrier_uint compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
return ((cache.high() >>
(64 - float_info<double>::significand_bits - 2 - beta)) +
return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
1) /
2;
}
@ -1942,25 +1927,24 @@ template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
// Extract significand bits and exponent bits.
const carrier_uint significand_mask =
(static_cast<carrier_uint>(1) << float_info<T>::significand_bits) - 1;
(static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
carrier_uint significand = (br & significand_mask);
int exponent = static_cast<int>((br & exponent_mask<T>()) >>
float_info<T>::significand_bits);
int exponent =
static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
if (exponent != 0) { // Check if normal.
exponent += float_info<T>::exponent_bias - float_info<T>::significand_bits;
exponent += float_info<T>::exponent_bias - num_significand_bits<T>();
// Shorter interval case; proceed like Schubfach.
// In fact, when exponent == 1 and significand == 0, the interval is
// regular. However, it can be shown that the end-results are anyway same.
if (significand == 0) return shorter_interval_case<T>(exponent);
significand |=
(static_cast<carrier_uint>(1) << float_info<T>::significand_bits);
significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
} else {
// Subnormal case; the interval is always regular.
if (significand == 0) return {0, 0};
exponent = float_info<T>::min_exponent - float_info<T>::significand_bits;
exponent = float_info<T>::min_exponent - num_significand_bits<T>();
}
const bool include_left_endpoint = (significand % 2 == 0);

View File

@ -1226,7 +1226,6 @@ template <typename T, typename Enable = void> struct float_info;
template <> struct float_info<float> {
using carrier_uint = uint32_t;
static const int significand_bits = 23;
static const int exponent_bits = 8;
static const int min_exponent = -126;
static const int max_exponent = 127;
@ -1249,7 +1248,6 @@ template <> struct float_info<float> {
template <> struct float_info<double> {
using carrier_uint = uint64_t;
static const int significand_bits = 52;
static const int exponent_bits = 11;
static const int min_exponent = -1022;
static const int max_exponent = 1023;
@ -1275,7 +1273,6 @@ template <typename T>
struct float_info<T, enable_if_t<std::is_same<T, long double>::value &&
std::numeric_limits<T>::digits == 64>> {
using carrier_uint = detail::uint128_t;
static const int significand_bits = 64;
static const int exponent_bits = 15;
};
@ -1288,12 +1285,22 @@ template <typename T> struct decimal_fp {
template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
} // namespace dragonbox
template <typename T>
template <typename Float> constexpr bool has_implicit_bit() {
return std::numeric_limits<Float>::digits != 64;
}
// Returns the number of significand bits in Float excluding the implicit bit.
template <typename Float> constexpr int num_significand_bits() {
return std::numeric_limits<Float>::digits -
(has_implicit_bit<Float>() ? 1 : 0);
}
template <typename Float>
constexpr auto exponent_mask() ->
typename dragonbox::float_info<T>::carrier_uint {
using uint = typename dragonbox::float_info<T>::carrier_uint;
return ((uint(1) << dragonbox::float_info<T>::exponent_bits) - 1)
<< dragonbox::float_info<T>::significand_bits;
typename dragonbox::float_info<Float>::carrier_uint {
using uint = typename dragonbox::float_info<Float>::carrier_uint;
return ((uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
<< num_significand_bits<Float>();
}
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
@ -2178,11 +2185,9 @@ FMT_CONSTEXPR20 bool isinf(T value) {
if (is_constant_evaluated()) {
#if defined(__cpp_if_constexpr)
if constexpr (std::numeric_limits<double>::is_iec559) {
auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
constexpr auto significand_bits =
dragonbox::float_info<double>::significand_bits;
auto bits = bit_cast<uint64_t>(static_cast<double>(value));
return (bits & exponent_mask<double>()) &&
!(bits & ((uint64_t(1) << significand_bits) - 1));
!(bits & ((uint64_t(1) << num_significand_bits<double>()) - 1));
}
#endif
}

View File

@ -227,13 +227,14 @@ TEST(fp_test, dragonbox_max_k) {
using fmt::detail::dragonbox::floor_log10_pow2;
using float_info = fmt::detail::dragonbox::float_info<float>;
EXPECT_EQ(fmt::detail::const_check(float_info::max_k),
float_info::kappa - floor_log10_pow2(float_info::min_exponent -
float_info::significand_bits));
float_info::kappa -
floor_log10_pow2(float_info::min_exponent -
fmt::detail::num_significand_bits<float>()));
using double_info = fmt::detail::dragonbox::float_info<double>;
EXPECT_EQ(
fmt::detail::const_check(double_info::max_k),
double_info::kappa - floor_log10_pow2(double_info::min_exponent -
double_info::significand_bits));
EXPECT_EQ(fmt::detail::const_check(double_info::max_k),
double_info::kappa -
floor_log10_pow2(double_info::min_exponent -
fmt::detail::num_significand_bits<double>()));
}
TEST(fp_test, get_round_direction) {