Support single precision floats in grisu formatting

Fixes #1336
This commit is contained in:
Orivej Desh
2019-10-12 12:17:27 +00:00
committed by Victor Zverovich
parent 91f7619cc9
commit 599e0aef45
4 changed files with 55 additions and 3 deletions

View File

@@ -422,6 +422,18 @@ class fp {
lower.f <<= lower.e - upper.e;
lower.e = upper.e;
}
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;
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;
upper = normalize<0>(fp(f + half_ulp, e));
lower = fp(f - (half_ulp >> (f == implicit_bit && e > min_normal_e)), e);
lower.f <<= lower.e - upper.e;
lower.e = upper.e;
}
};
// Returns an fp number representing x - y. Result may not be normalized.
@@ -1046,7 +1058,11 @@ bool grisu_format(Double value, buffer<char>& buf, int precision,
buf.resize(to_unsigned(handler.size));
} else {
fp lower, upper; // w^- and w^+ in the Grisu paper.
fp_value.compute_boundaries(lower, upper);
if ((options & grisu_options::binary32) != 0)
fp_value.compute_float_boundaries(lower, upper);
else
fp_value.compute_boundaries(lower, upper);
// Find a cached power of 10 such that multiplying upper by it will bring
// the exponent in the range [min_exp, -32].
const auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.

View File

@@ -1111,7 +1111,7 @@ It grisu_prettify(const char* digits, int size, int exp, It it,
}
namespace grisu_options {
enum { fixed = 1, grisu2 = 2 };
enum { fixed = 1, grisu2 = 2, binary32 = 4 };
}
// Formats value using the Grisu algorithm:
@@ -2809,12 +2809,16 @@ void internal::basic_writer<Range>::write_fp(T value,
memory_buffer buffer;
int exp = 0;
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
unsigned options = 0;
if (handler.fixed) options |= internal::grisu_options::fixed;
if (sizeof(value) == sizeof(float))
options |= internal::grisu_options::binary32;
bool use_grisu = USE_GRISU &&
(specs.type != 'a' && specs.type != 'A' &&
specs.type != 'e' && specs.type != 'E') &&
internal::grisu_format(
static_cast<double>(value), buffer, precision,
handler.fixed ? internal::grisu_options::fixed : 0, exp);
options, exp);
char* decimal_point_pos = nullptr;
if (!use_grisu)
decimal_point_pos = internal::sprintf_format(value, buffer, specs);