Skip to content

Commit 7faed3d

Browse files
author
David Rajchenbach-Teller
committed
[Fix] float.rs: str_to_float reimplemented
1 parent 3219c40 commit 7faed3d

File tree

1 file changed

+177
-14
lines changed

1 file changed

+177
-14
lines changed

src/lib/float.rs

Lines changed: 177 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* String conversions
3+
*/
4+
15
fn float_to_str(num: float, digits: uint) -> str {
26
let accum = if num < 0.0 { num = -num; "-" } else { "" };
37
let trunc = num as uint;
@@ -15,21 +19,180 @@ fn float_to_str(num: float, digits: uint) -> str {
1519
ret accum;
1620
}
1721

22+
/**
23+
* Convert a string to a float
24+
*
25+
* This function accepts strings such as
26+
* * "3.14"
27+
* * "+3.14", equivalent to "3.14"
28+
* * "-3.14"
29+
* * "2.5E10", or equivalently, "2.5e10"
30+
* * "2.5E-10"
31+
* * "", or, equivalently, "." (understood as 0)
32+
* * "5."
33+
* * ".5", or, equivalently, "0.5"
34+
*
35+
* @param num A string, possibly empty.
36+
* @return [NaN] if the string did not represent a valid number.
37+
* @return Otherwise, the floating-point number represented [num].
38+
*/
1839
fn str_to_float(num: str) -> float {
19-
let digits = str::split(num, '.' as u8);
20-
let total = int::from_str(digits[0]) as float;
21-
22-
fn dec_val(c: char) -> int { ret (c as int) - ('0' as int); }
23-
24-
let right = digits[1];
25-
let len = str::char_len(digits[1]);
26-
let i = 1u;
27-
while (i < len) {
28-
total += dec_val(str::pop_char(right)) as float /
29-
(int::pow(10, i) as float);
30-
i += 1u;
31-
}
32-
ret total;
40+
let pos = 0u; //Current byte position in the string.
41+
//Used to walk the string in O(n).
42+
let len = str::byte_len(num); //Length of the string, in bytes.
43+
44+
if len == 0u { ret 0.; }
45+
let total = 0f; //Accumulated result
46+
let c = 'z'; //Latest char.
47+
48+
//Determine if first char is '-'/'+'. Set [pos] and [neg] accordingly.
49+
let neg = false; //Sign of the result
50+
alt str::char_at(num, 0u) {
51+
'-' {
52+
neg = true;
53+
pos = 1u;
54+
}
55+
'+' {
56+
pos = 1u;
57+
}
58+
_ {}
59+
}
60+
61+
//Examine the following chars until '.', 'e', 'E'
62+
while(pos < len) {
63+
let char_range = str::char_range_at(num, pos);
64+
c = char_range.ch;
65+
pos = char_range.next;
66+
alt c {
67+
'0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' {
68+
total = total * 10f;
69+
total += ((c as int) - ('0' as int)) as float;
70+
}
71+
_ {
72+
break;
73+
}
74+
}
75+
}
76+
77+
if c == '.' {//Examine decimal part
78+
let decimal = 1.f;
79+
while(pos < len) {
80+
let char_range = str::char_range_at(num, pos);
81+
c = char_range.ch;
82+
pos = char_range.next;
83+
alt c {
84+
'0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' {
85+
decimal /= 10.f;
86+
total += (((c as int) - ('0' as int)) as float)*decimal;
87+
}
88+
_ {
89+
break;
90+
}
91+
}
92+
}
93+
}
94+
95+
if (c == 'e') | (c == 'E') {//Examine exponent
96+
let exponent = 0u;
97+
let neg_exponent = false;
98+
if(pos < len) {
99+
let char_range = str::char_range_at(num, pos);
100+
c = char_range.ch;
101+
alt c {
102+
'+' {
103+
pos = char_range.next;
104+
}
105+
'-' {
106+
pos = char_range.next;
107+
neg_exponent = true;
108+
}
109+
_ {}
110+
}
111+
while(pos < len) {
112+
let char_range = str::char_range_at(num, pos);
113+
c = char_range.ch;
114+
pos = char_range.next;
115+
alt c {
116+
'0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' {
117+
exponent *= 10u;
118+
exponent += ((c as uint) - ('0' as uint));
119+
}
120+
_ {
121+
break;
122+
}
123+
}
124+
}
125+
let multiplier = pow_uint_to_uint_as_float(10u, exponent);
126+
//Note: not [int::pow], otherwise, we'll quickly
127+
//end up with a nice overflow
128+
if neg_exponent {
129+
total = total / multiplier;
130+
} else {
131+
total = total * multiplier;
132+
}
133+
}
134+
}
135+
136+
if(pos < len) {
137+
ret NaN();
138+
} else {
139+
if(neg) {
140+
total *= -1f;
141+
}
142+
ret total;
143+
}
144+
}
145+
146+
/**
147+
* Arithmetics
148+
*/
149+
150+
/**
151+
* Compute the exponentiation of an integer by another integer as a float.
152+
*
153+
*
154+
* @param x The base.
155+
* @param pow The exponent.
156+
* @return [NaN] of both [x] and [pow] are [0u], otherwise [x^pow].
157+
*/
158+
fn pow_uint_to_uint_as_float(x: uint, pow: uint) -> float {
159+
if x == 0u {
160+
if pow == 0u {
161+
ret NaN();
162+
}
163+
ret 0.;
164+
}
165+
let my_pow = pow;
166+
let total = 1f;
167+
let multiplier = x as float;
168+
while (my_pow > 0u) {
169+
if my_pow % 2u == 1u {
170+
total = total * multiplier;
171+
}
172+
my_pow /= 2u;
173+
multiplier *= multiplier;
174+
}
175+
ret total;
176+
}
177+
178+
179+
/**
180+
* Constants
181+
*/
182+
183+
//TODO: Once this is possible, replace the body of these functions
184+
//by an actual constant.
185+
186+
fn NaN() -> float {
187+
ret 0./0.;
188+
}
189+
190+
fn infinity() -> float {
191+
ret 1./0.;
192+
}
193+
194+
fn neg_infinity() -> float {
195+
ret -1./0.;
33196
}
34197

35198
//

0 commit comments

Comments
 (0)