mirror of
https://github.com/fmtlib/fmt.git
synced 2025-04-10 12:44:48 +00:00
Round close to zero fixed precision
This commit is contained in:
parent
49d244c065
commit
8407f4cb24
@ -460,7 +460,10 @@ int grisu2_gen_digits(char* buf, fp value, uint64_t error_ulp, int& exp,
|
|||||||
uint64_t fractional = value.f & (one.f - 1);
|
uint64_t fractional = value.f & (one.f - 1);
|
||||||
exp = count_digits(integral); // kappa in Grisu.
|
exp = count_digits(integral); // kappa in Grisu.
|
||||||
int size = 0;
|
int size = 0;
|
||||||
if (stop.on_exp(exp)) return size;
|
if (stop.init(buf, size, value.f, data::POWERS_OF_10_64[exp] << -one.e,
|
||||||
|
error_ulp, exp)) {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
// Generate digits for the integral part. This can produce up to 10 digits.
|
// Generate digits for the integral part. This can produce up to 10 digits.
|
||||||
do {
|
do {
|
||||||
uint32_t digit = 0;
|
uint32_t digit = 0;
|
||||||
@ -541,17 +544,44 @@ struct fixed_stop {
|
|||||||
return full_exp <= 0 && -full_exp >= precision;
|
return full_exp <= 0 && -full_exp >= precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool on_exp(int exp) {
|
enum round_direction { unknown, up, down };
|
||||||
|
|
||||||
|
static round_direction round(uint64_t remainder, uint64_t divisor,
|
||||||
|
uint64_t error) {
|
||||||
|
assert(error < divisor && error < divisor - error);
|
||||||
|
// Round down if (remainder + error) * 2 <= divisor.
|
||||||
|
if (remainder < divisor - remainder && error * 2 <= divisor - remainder * 2)
|
||||||
|
return down;
|
||||||
|
// Round up if (remainder - error) * 2 >= divisor.
|
||||||
|
if (remainder >= error &&
|
||||||
|
remainder - error >= divisor - (remainder - error)) {
|
||||||
|
return up;
|
||||||
|
}
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init(char* buf, int& size, uint64_t remainder, uint64_t divisor,
|
||||||
|
uint64_t error, int& exp) {
|
||||||
if (!fixed) return false;
|
if (!fixed) return false;
|
||||||
exp += exp10;
|
int full_exp = exp + exp10;
|
||||||
if (exp >= 0) precision += exp;
|
if (full_exp >= 0) precision += full_exp;
|
||||||
// TODO: round
|
if (!enough_precision(full_exp)) return false;
|
||||||
return enough_precision(exp);
|
switch (round(remainder, divisor, error)) {
|
||||||
|
case unknown:
|
||||||
|
size = -1;
|
||||||
|
break;
|
||||||
|
case up:
|
||||||
|
buf[size++] = '1';
|
||||||
|
break;
|
||||||
|
case down:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test
|
// TODO: test
|
||||||
bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor,
|
bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor,
|
||||||
uint64_t error, int& exp, bool integral) {
|
uint64_t error, int& exp, bool integral) const {
|
||||||
assert(remainder < divisor);
|
assert(remainder < divisor);
|
||||||
if (size != precision && !enough_precision(exp + exp10)) return false;
|
if (size != precision && !enough_precision(exp + exp10)) return false;
|
||||||
if (!integral) {
|
if (!integral) {
|
||||||
@ -565,12 +595,11 @@ struct fixed_stop {
|
|||||||
} else {
|
} else {
|
||||||
assert(error == 1 && divisor > 2);
|
assert(error == 1 && divisor > 2);
|
||||||
}
|
}
|
||||||
// Round down if (remainder + error) * 2 <= divisor.
|
switch (round(remainder, divisor, error)) {
|
||||||
if (remainder < divisor - remainder && error * 2 <= divisor - remainder * 2)
|
case unknown:
|
||||||
return true;
|
size = -1;
|
||||||
// Round up if (remainder - error) * 2 >= divisor.
|
break;
|
||||||
if (remainder >= error &&
|
case up:
|
||||||
remainder - error >= divisor - (remainder - error)) {
|
|
||||||
++buf[size - 1];
|
++buf[size - 1];
|
||||||
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
||||||
buf[i] = '0';
|
buf[i] = '0';
|
||||||
@ -580,9 +609,10 @@ struct fixed_stop {
|
|||||||
buf[0] = '1';
|
buf[0] = '1';
|
||||||
++exp;
|
++exp;
|
||||||
}
|
}
|
||||||
return true;
|
break;
|
||||||
|
case down:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
size = -1;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -591,7 +621,7 @@ struct fixed_stop {
|
|||||||
struct shortest_stop {
|
struct shortest_stop {
|
||||||
fp diff; // wp_w in Grisu.
|
fp diff; // wp_w in Grisu.
|
||||||
|
|
||||||
bool on_exp(int) { return false; }
|
bool init(char*, int&, uint64_t, uint64_t, uint64_t, int&) { return false; }
|
||||||
|
|
||||||
bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor,
|
bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor,
|
||||||
uint64_t error, int& exp, bool integral) {
|
uint64_t error, int& exp, bool integral) {
|
||||||
|
@ -1472,6 +1472,7 @@ TEST(FormatterTest, FormatDouble) {
|
|||||||
|
|
||||||
TEST(FormatterTest, PrecisionRounding) {
|
TEST(FormatterTest, PrecisionRounding) {
|
||||||
EXPECT_EQ("0.000", format("{:.3f}", 0.00049));
|
EXPECT_EQ("0.000", format("{:.3f}", 0.00049));
|
||||||
|
EXPECT_EQ("0.001", format("{:.3f}", 0.0005));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatNaN) {
|
TEST(FormatterTest, FormatNaN) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user