Skip to content

Conversation

@orivej
Copy link
Contributor

@orivej orivej commented Oct 12, 2019

Depends on #1360

Fixes #1336

Copy link
Contributor

@vitaut vitaut left a comment

Choose a reason for hiding this comment

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

Thanks for another great PR! A few comments inline.

}

void compute_float_boundaries(fp& lower, fp& upper) const {
constexpr significand_type half_step =
Copy link
Contributor

Choose a reason for hiding this comment

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

Does step mean ulp here? If yes, I recommend using ulp instead of step since it's an established term.

Comment on lines 363 to 366
static FMT_CONSTEXPR_DECL const int double_significand_size =
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const int float_significand_size =
std::numeric_limits<float>::digits - 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

I suggest replacing these two with a single function to reduce duplication:

  template <typename T>
  static int float_significand_size() { return std::numeric_limits<T>::digits - 1; }

enable_if_t<(sizeof(Double) == sizeof(uint64_t)), int>>
FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
unsigned options, int& exp) {
FMT_API bool grisu_format(Double value, size_t sizeof_value, buffer<char>& buf,
Copy link
Contributor

Choose a reason for hiding this comment

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

Since sizeof_value is only used to pass one bit of information (whether argument is float or double) I suggest folding it into options.

@orivej orivej force-pushed the float2 branch 2 times, most recently from d6a0654 to a887908 Compare October 13, 2019 18:24
@vitaut
Copy link
Contributor

vitaut commented Oct 15, 2019

Looks good but let me double check the logic in compute_float_boundaries.

Copy link
Contributor Author

@orivej orivej left a comment

Choose a reason for hiding this comment

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

OK. I have left some notes.


void compute_float_boundaries(fp& lower, fp& upper) const {
constexpr int min_normal_e = std::numeric_limits<float>::min_exponent -
std::numeric_limits<double>::digits;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The smallest positive normal float is 2^-126. fp(2^-126) is fp(2^52, -178). float min_exponent is -125, double digits is 53: both of these numbers are off by one from what we need, but these 1s cancel each other out.

std::numeric_limits<double>::digits;
significand_type half_ulp = 1 << (std::numeric_limits<double>::digits -
std::numeric_limits<float>::digits - 1);
if (min_normal_e > e) half_ulp <<= min_normal_e - e;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When the float is normal, half_ulp is an obvious constant. When the float is subnormal, the cast to double shifts the significand to the left and effectively decreases the exponent by the number of shifted bits. Then we need to shift half_ulp to the left by the same amount.

std::numeric_limits<float>::digits - 1);
if (min_normal_e > e) half_ulp <<= min_normal_e - e;
upper = normalize<0>(fp(f + half_ulp, e));
lower = fp(f - (half_ulp >> (f == implicit_bit && e > min_normal_e)), e);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The condition is e > min_normal_e, not e >= min_normal_e, because the smallest positive normal float is the average of its neighbors. (The same is true for the smallest positive normal double but compute_boundaries does not account for that which may be either a bug or irrelevant.)

Copy link
Contributor

Choose a reason for hiding this comment

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

The same is true for the smallest positive normal double but compute_boundaries does not account for that which may be either a bug or irrelevant.

Great catch! It's definitely a bug which fortunately doesn't affect the output. Will be fixed in eb879a6.

@vitaut vitaut merged commit 599e0ae into fmtlib:master Oct 18, 2019
@vitaut
Copy link
Contributor

vitaut commented Oct 18, 2019

Thanks for the detailed explanation, merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support single precision floats in grisu formatting

2 participants