parent
91f7619cc9
commit
599e0aef45
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -221,6 +221,36 @@ TEST(FPTest, ComputeBoundaries) {
|
||||
EXPECT_EQ(31, upper.e);
|
||||
}
|
||||
|
||||
TEST(FPTest, ComputeFloatBoundaries) {
|
||||
struct {
|
||||
double x, lower, upper;
|
||||
} tests[] = {
|
||||
// regular
|
||||
{1.5f, 1.4999999403953552, 1.5000000596046448},
|
||||
// boundary
|
||||
{1.0f, 0.9999999701976776, 1.0000000596046448},
|
||||
// min normal
|
||||
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
|
||||
// max subnormal
|
||||
{1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
|
||||
// min subnormal
|
||||
{1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
|
||||
};
|
||||
for (auto test : tests) {
|
||||
auto v = fp(test.x);
|
||||
fp vlower = normalize(fp(test.lower));
|
||||
fp vupper = normalize(fp(test.upper));
|
||||
vlower.f >>= vupper.e - vlower.e;
|
||||
vlower.e = vupper.e;
|
||||
fp lower, upper;
|
||||
v.compute_float_boundaries(lower, upper);
|
||||
EXPECT_EQ(vlower.f, lower.f);
|
||||
EXPECT_EQ(vlower.e, lower.e);
|
||||
EXPECT_EQ(vupper.f, upper.f);
|
||||
EXPECT_EQ(vupper.e, upper.e);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FPTest, Subtract) {
|
||||
auto v = fp(123, 1) - fp(102, 1);
|
||||
EXPECT_EQ(v.f, 21u);
|
||||
|
@ -52,6 +52,8 @@ TEST(GrisuTest, Prettify) {
|
||||
EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7));
|
||||
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
|
||||
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
|
||||
EXPECT_EQ("0.1", fmt::format("{}", 0.1f));
|
||||
EXPECT_EQ("0.10000000149011612", fmt::format("{}", double(0.1f)));
|
||||
}
|
||||
|
||||
TEST(GrisuTest, ZeroPrecision) { EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); }
|
||||
|
Loading…
Reference in New Issue
Block a user