mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 19:24:48 +02:00
Implement round half to even
This commit is contained in:
@@ -946,20 +946,20 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Formats v using a variation of the Fixed-Precision Positive Floating-Point
|
// Formats value using a variation of the Fixed-Precision Positive
|
||||||
// Printout ((FPP)^2) algorithm by Steele & White:
|
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
||||||
// http://kurtstephens.com/files/p372-steele.pdf.
|
// http://kurtstephens.com/files/p372-steele.pdf.
|
||||||
template <typename Double>
|
template <typename Double>
|
||||||
FMT_FUNC void fallback_format(Double v, buffer<char>& buf, int& exp10) {
|
FMT_FUNC void fallback_format(Double value, buffer<char>& buf, int& exp10) {
|
||||||
fp fp_value(v);
|
fp fp_value(value);
|
||||||
// Shift to account for unequal gaps when lower boundary is 2 times closer.
|
// Shift to account for unequal gaps when lower boundary is 2 times closer.
|
||||||
// TODO: handle denormals
|
// TODO: handle denormals
|
||||||
int shift = 0; //fp_value.f == 1 ? 1 : 0;
|
int shift = 0; // fp_value.f == 1 ? 1 : 0;
|
||||||
bigint numerator; // 2 * R in (FPP)^2.
|
bigint numerator; // 2 * R in (FPP)^2.
|
||||||
bigint denominator; // 2 * S in (FPP)^2.
|
bigint denominator; // 2 * S in (FPP)^2.
|
||||||
bigint lower; // (M^- in (FPP)^2).
|
bigint lower; // (M^- in (FPP)^2).
|
||||||
bigint upper_store;
|
bigint upper_store;
|
||||||
bigint *upper = nullptr; // (M^+ in (FPP)^2).
|
bigint* upper = nullptr; // (M^+ in (FPP)^2).
|
||||||
// Shift numerator and denominator by an extra bit to make lower and upper
|
// Shift numerator and denominator by an extra bit to make lower and upper
|
||||||
// which are normally half ulp integers. This eliminates multiplication by 2
|
// which are normally half ulp integers. This eliminates multiplication by 2
|
||||||
// during later computations.
|
// during later computations.
|
||||||
@@ -998,27 +998,29 @@ FMT_FUNC void fallback_format(Double v, buffer<char>& buf, int& exp10) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!upper) upper = &lower;
|
if (!upper) upper = &lower;
|
||||||
// Invariant: fp_value == (numerator / denominator) * pow(10, exp10).
|
// Invariant: value == (numerator / denominator) * pow(10, exp10).
|
||||||
bool even = (fp_value.f & 1) == 0;
|
bool even = (fp_value.f & 1) == 0;
|
||||||
int num_digits = 0;
|
int num_digits = 0;
|
||||||
char* data = buf.data();
|
char* data = buf.data();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int digit = numerator.divmod_assign(denominator);
|
int digit = numerator.divmod_assign(denominator);
|
||||||
bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
|
bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
|
||||||
bool high = add_compare(numerator, *upper, denominator) + even >
|
// numerator + upper >[=] pow10:
|
||||||
0; // numerator + upper >[=] pow10.
|
bool high = add_compare(numerator, *upper, denominator) + even > 0;
|
||||||
|
data[num_digits++] = static_cast<char>('0' + digit);
|
||||||
if (low || high) {
|
if (low || high) {
|
||||||
if (!low) {
|
if (!low) {
|
||||||
++digit;
|
++data[num_digits - 1];
|
||||||
} else if (high) {
|
} else if (high) {
|
||||||
// TODO: round up if 2 * numerator >= denominator
|
int result = add_compare(numerator, numerator, denominator);
|
||||||
|
// Round half to even.
|
||||||
|
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
||||||
|
++data[num_digits - 1];
|
||||||
}
|
}
|
||||||
data[num_digits++] = static_cast<char>('0' + digit);
|
|
||||||
buf.resize(num_digits);
|
buf.resize(num_digits);
|
||||||
exp10 -= num_digits -1;
|
exp10 -= num_digits - 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data[num_digits++] = static_cast<char>('0' + digit);
|
|
||||||
numerator *= 10;
|
numerator *= 10;
|
||||||
lower *= 10;
|
lower *= 10;
|
||||||
if (upper != &lower) *upper *= 10;
|
if (upper != &lower) *upper *= 10;
|
||||||
|
@@ -1569,7 +1569,7 @@ template <typename Range> class basic_writer {
|
|||||||
decimal_point_(decimal_point) {
|
decimal_point_(decimal_point) {
|
||||||
int num_digits = static_cast<int>(digits.size());
|
int num_digits = static_cast<int>(digits.size());
|
||||||
int full_exp = num_digits + exp - 1;
|
int full_exp = num_digits + exp - 1;
|
||||||
int precision = params.num_digits > 0 ? params.num_digits : 11;
|
int precision = params.num_digits > 0 ? params.num_digits : 16;
|
||||||
params_.fixed |= full_exp >= -4 && full_exp < precision;
|
params_.fixed |= full_exp >= -4 && full_exp < precision;
|
||||||
auto it = internal::grisu_prettify<char>(
|
auto it = internal::grisu_prettify<char>(
|
||||||
digits.data(), num_digits, exp, internal::counting_iterator<char>(),
|
digits.data(), num_digits, exp, internal::counting_iterator<char>(),
|
||||||
|
@@ -48,7 +48,7 @@ TEST(GrisuTest, Prettify) {
|
|||||||
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
|
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
|
||||||
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
|
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
|
||||||
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
|
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
|
||||||
EXPECT_EQ("1e+11", fmt::format("{}", 1e11));
|
EXPECT_EQ("100000000000.0", fmt::format("{}", 1e11));
|
||||||
EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7));
|
EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7));
|
||||||
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
|
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
|
||||||
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
|
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
|
||||||
@@ -66,4 +66,7 @@ TEST(GrisuTest, Fallback) {
|
|||||||
EXPECT_EQ("1.372371880954233e-288",
|
EXPECT_EQ("1.372371880954233e-288",
|
||||||
fmt::format("{}", 1.372371880954233e-288));
|
fmt::format("{}", 1.372371880954233e-288));
|
||||||
EXPECT_EQ("55388492.622190244", fmt::format("{}", 55388492.622190244));
|
EXPECT_EQ("55388492.622190244", fmt::format("{}", 55388492.622190244));
|
||||||
|
EXPECT_EQ("2.2506787569811123e-253",
|
||||||
|
fmt::format("{}", 2.2506787569811123e-253));
|
||||||
|
EXPECT_EQ("1103618912042992.8", fmt::format("{}", 1103618912042992.8));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user