Skip to content

Commit 6fb42b3

Browse files
committed
(float) fix some rounding errors when showing as str
This seems to fix issue rust-lang#1876, and some of the superficial parts of issue rust-lang#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]; } ```
1 parent c5d168c commit 6fb42b3

File tree

2 files changed

+66
-13
lines changed

2 files changed

+66
-13
lines changed

src/libcore/float.rs

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,77 @@ fn to_str_common(num: float, digits: uint, exact: bool) -> str {
4444
if is_NaN(num) { ret "NaN"; }
4545
if num == infinity { ret "inf"; }
4646
if num == neg_infinity { ret "-inf"; }
47-
let mut (num, accum) = if num < 0.0 { (-num, "-") } else { (num, "") };
47+
48+
let mut (num, sign) = if num < 0.0 { (-num, "-") } else { (num, "") };
49+
50+
// truncated integer
4851
let trunc = num as uint;
52+
53+
// decimal remainder
4954
let mut frac = num - (trunc as float);
50-
accum += uint::str(trunc);
51-
if (frac < epsilon && !exact) || digits == 0u { ret accum; }
52-
accum += ".";
53-
let mut i = digits;
54-
let mut epsilon_prime = 1. / pow_with_uint(10u, i);
55-
while i > 0u && (frac >= epsilon_prime || exact) {
55+
56+
// stack of digits
57+
let mut fractionalParts = [];
58+
59+
// FIXME:
60+
// This used to return right away without rounding, as "[-]num",
61+
// but given epsilon like in f64.rs, I don't see how the comparison
62+
// to epsilon did much when only used there.
63+
// if (frac < epsilon && !exact) || digits == 0u { ret accum; }
64+
//
65+
// With something better, possibly weird results like this can be avoided:
66+
// assert "3.14158999999999988262" == my_to_str_exact(3.14159, 20u);
67+
68+
let mut ii = digits;
69+
let mut epsilon_prime = 1.0 / pow_with_uint(10u, ii);
70+
71+
// while we still need digits
72+
// build stack of digits
73+
while ii > 0u && (frac >= epsilon_prime || exact) {
74+
// store the next digit
5675
frac *= 10.0;
57-
epsilon_prime *= 10.0;
5876
let digit = frac as uint;
59-
accum += uint::str(digit);
77+
vec::push(fractionalParts, digit);
78+
79+
// calculate the next frac
6080
frac -= digit as float;
61-
i -= 1u;
81+
epsilon_prime *= 10.0;
82+
ii -= 1u;
83+
}
84+
85+
let mut acc;
86+
let mut racc = "";
87+
let mut carry = if frac * 10.0 as uint >= 5u { 1u } else { 0u };
88+
89+
// turn digits into string
90+
// using stack of digits
91+
while vec::len(fractionalParts) > 0u {
92+
let mut adjusted_digit = carry + vec::pop(fractionalParts);
93+
94+
if adjusted_digit == 10u {
95+
carry = 1u;
96+
adjusted_digit %= 10u
97+
} else {
98+
carry = 0u
99+
};
100+
101+
racc = uint::str(adjusted_digit) + racc;
102+
}
103+
104+
// pad decimals with trailing zeroes
105+
while str::len(racc) < digits && exact {
106+
racc += "0"
107+
}
108+
109+
// combine ints and decimals
110+
let mut ones = uint::str(trunc + carry);
111+
if racc == "" {
112+
acc = sign + ones;
113+
} else {
114+
acc = sign + ones + "." + racc;
62115
}
63-
ret accum;
64116

117+
ret acc;
65118
}
66119

67120
#[doc = "

src/test/run-pass/syntax-extension-fmt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ fn part3() {
9494
test(#fmt["%.o", 10u], "12");
9595
test(#fmt["%.t", 3u], "11");
9696
test(#fmt["%.c", 'A'], "A");
97-
test(#fmt["%.f", 5.82], "5");
97+
test(#fmt["%.f", 5.82], "6");
9898
test(#fmt["%.0d", 0], "");
9999
test(#fmt["%.0u", 0u], "");
100100
test(#fmt["%.0x", 0u], "");
@@ -107,7 +107,7 @@ fn part3() {
107107
test(#fmt["%.0o", 10u], "12");
108108
test(#fmt["%.0t", 3u], "11");
109109
test(#fmt["%.0c", 'A'], "A");
110-
test(#fmt["%.0f", 5.892], "5");
110+
test(#fmt["%.0f", 5.892], "6");
111111
test(#fmt["%.1d", 0], "0");
112112
test(#fmt["%.1u", 0u], "0");
113113
test(#fmt["%.1x", 0u], "0");

0 commit comments

Comments
 (0)