(float) fix some rounding errors when showing as str

This seems to fix issue #1876, and some of the superficial parts of
issue #1375.  The #fmt macro and the to_str functions will round,
rather than truncate, floats as strings.

Other issues remain, and I wrote more code here than intended, but the
following should pass now.

```
fn x() {
   assert "3.1416"      == #fmt["%.4f", 3.14159];
   assert "3"           == #fmt["%.0f", 3.14159];
   assert "99"          == #fmt["%.0f", 98.5];
   assert "7.0000"      == #fmt["%.4f", 6.999999999];
   assert "3.141590000" == #fmt["%.9f", 3.14159];
}
```
This commit is contained in:
Kevin Cantu 2012-06-02 00:15:29 -07:00 committed by Brian Anderson
parent 7d0755529e
commit a7359f5b3b
2 changed files with 72 additions and 19 deletions

View File

@ -44,24 +44,77 @@ fn to_str_common(num: float, digits: uint, exact: bool) -> str {
if is_NaN(num) { ret "NaN"; }
if num == infinity { ret "inf"; }
if num == neg_infinity { ret "-inf"; }
let mut (num, accum) = if num < 0.0 { (-num, "-") } else { (num, "") };
let trunc = num as uint;
let mut frac = num - (trunc as float);
accum += uint::str(trunc);
if (frac < epsilon && !exact) || digits == 0u { ret accum; }
accum += ".";
let mut i = digits;
let mut epsilon_prime = 1. / pow_with_uint(10u, i);
while i > 0u && (frac >= epsilon_prime || exact) {
frac *= 10.0;
epsilon_prime *= 10.0;
let digit = frac as uint;
accum += uint::str(digit);
frac -= digit as float;
i -= 1u;
}
ret accum;
let mut (num, sign) = if num < 0.0 { (-num, "-") } else { (num, "") };
// truncated integer
let trunc = num as uint;
// decimal remainder
let mut frac = num - (trunc as float);
// stack of digits
let mut fractionalParts = [];
// FIXME:
// This used to return right away without rounding, as "[-]num",
// but given epsilon like in f64.rs, I don't see how the comparison
// to epsilon did much when only used there.
// if (frac < epsilon && !exact) || digits == 0u { ret accum; }
//
// With something better, possibly weird results like this can be avoided:
// assert "3.14158999999999988262" == my_to_str_exact(3.14159, 20u);
let mut ii = digits;
let mut epsilon_prime = 1.0 / pow_with_uint(10u, ii);
// while we still need digits
// build stack of digits
while ii > 0u && (frac >= epsilon_prime || exact) {
// store the next digit
frac *= 10.0;
let digit = frac as uint;
vec::push(fractionalParts, digit);
// calculate the next frac
frac -= digit as float;
epsilon_prime *= 10.0;
ii -= 1u;
}
let mut acc;
let mut racc = "";
let mut carry = if frac * 10.0 as uint >= 5u { 1u } else { 0u };
// turn digits into string
// using stack of digits
while vec::len(fractionalParts) > 0u {
let mut adjusted_digit = carry + vec::pop(fractionalParts);
if adjusted_digit == 10u {
carry = 1u;
adjusted_digit %= 10u
} else {
carry = 0u
};
racc = uint::str(adjusted_digit) + racc;
}
// pad decimals with trailing zeroes
while str::len(racc) < digits && exact {
racc += "0"
}
// combine ints and decimals
let mut ones = uint::str(trunc + carry);
if racc == "" {
acc = sign + ones;
} else {
acc = sign + ones + "." + racc;
}
ret acc;
}
#[doc = "

View File

@ -94,7 +94,7 @@ fn part3() {
test(#fmt["%.o", 10u], "12");
test(#fmt["%.t", 3u], "11");
test(#fmt["%.c", 'A'], "A");
test(#fmt["%.f", 5.82], "5");
test(#fmt["%.f", 5.82], "6");
test(#fmt["%.0d", 0], "");
test(#fmt["%.0u", 0u], "");
test(#fmt["%.0x", 0u], "");
@ -107,7 +107,7 @@ fn part3() {
test(#fmt["%.0o", 10u], "12");
test(#fmt["%.0t", 3u], "11");
test(#fmt["%.0c", 'A'], "A");
test(#fmt["%.0f", 5.892], "5");
test(#fmt["%.0f", 5.892], "6");
test(#fmt["%.1d", 0], "0");
test(#fmt["%.1u", 0u], "0");
test(#fmt["%.1x", 0u], "0");