From 3219c40e18c20a0dbd41b106e1b05e3696bf4c85 Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Wed, 12 Oct 2011 10:22:26 +0200 Subject: [PATCH 1/4] [Optim] int.rs: reimplemented pow with fast exponentiation --- src/lib/int.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/lib/int.rs b/src/lib/int.rs index 0a6e69ceadc7e..fe7689e3e2872 100644 --- a/src/lib/int.rs +++ b/src/lib/int.rs @@ -73,16 +73,19 @@ fn to_str(n: int, radix: uint) -> str { fn str(i: int) -> str { ret to_str(i, 10u); } fn pow(base: int, exponent: uint) -> int { - ret if exponent == 0u { - 1 - } else if base == 0 { - 0 - } else { - let accum = base; - let count = exponent; - while count > 1u { accum *= base; count -= 1u; } - accum - }; + if exponent == 0u { ret 1; } //Not mathemtically true if [base == 0] + if base == 0 { ret 0; } + let my_pow = exponent; + let acc = 1; + let multiplier = base; + while(my_pow > 0u) { + if my_pow % 2u == 1u { + acc *= multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + ret acc; } // Local Variables: // mode: rust; From 7faed3d87ca4f20b2da41cf5410598b48438c04b Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Wed, 12 Oct 2011 10:24:43 +0200 Subject: [PATCH 2/4] [Fix] float.rs: str_to_float reimplemented --- src/lib/float.rs | 191 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 177 insertions(+), 14 deletions(-) diff --git a/src/lib/float.rs b/src/lib/float.rs index 9a241ba36cf36..e961c34e66082 100644 --- a/src/lib/float.rs +++ b/src/lib/float.rs @@ -1,3 +1,7 @@ +/** + * String conversions + */ + fn float_to_str(num: float, digits: uint) -> str { let accum = if num < 0.0 { num = -num; "-" } else { "" }; let trunc = num as uint; @@ -15,21 +19,180 @@ fn float_to_str(num: float, digits: uint) -> str { ret accum; } +/** + * Convert a string to a float + * + * This function accepts strings such as + * * "3.14" + * * "+3.14", equivalent to "3.14" + * * "-3.14" + * * "2.5E10", or equivalently, "2.5e10" + * * "2.5E-10" + * * "", or, equivalently, "." (understood as 0) + * * "5." + * * ".5", or, equivalently, "0.5" + * + * @param num A string, possibly empty. + * @return [NaN] if the string did not represent a valid number. + * @return Otherwise, the floating-point number represented [num]. + */ fn str_to_float(num: str) -> float { - let digits = str::split(num, '.' as u8); - let total = int::from_str(digits[0]) as float; - - fn dec_val(c: char) -> int { ret (c as int) - ('0' as int); } - - let right = digits[1]; - let len = str::char_len(digits[1]); - let i = 1u; - while (i < len) { - total += dec_val(str::pop_char(right)) as float / - (int::pow(10, i) as float); - i += 1u; - } - ret total; + let pos = 0u; //Current byte position in the string. + //Used to walk the string in O(n). + let len = str::byte_len(num); //Length of the string, in bytes. + + if len == 0u { ret 0.; } + let total = 0f; //Accumulated result + let c = 'z'; //Latest char. + + //Determine if first char is '-'/'+'. Set [pos] and [neg] accordingly. + let neg = false; //Sign of the result + alt str::char_at(num, 0u) { + '-' { + neg = true; + pos = 1u; + } + '+' { + pos = 1u; + } + _ {} + } + + //Examine the following chars until '.', 'e', 'E' + while(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + pos = char_range.next; + alt c { + '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' { + total = total * 10f; + total += ((c as int) - ('0' as int)) as float; + } + _ { + break; + } + } + } + + if c == '.' {//Examine decimal part + let decimal = 1.f; + while(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + pos = char_range.next; + alt c { + '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' { + decimal /= 10.f; + total += (((c as int) - ('0' as int)) as float)*decimal; + } + _ { + break; + } + } + } + } + + if (c == 'e') | (c == 'E') {//Examine exponent + let exponent = 0u; + let neg_exponent = false; + if(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + alt c { + '+' { + pos = char_range.next; + } + '-' { + pos = char_range.next; + neg_exponent = true; + } + _ {} + } + while(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + pos = char_range.next; + alt c { + '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' { + exponent *= 10u; + exponent += ((c as uint) - ('0' as uint)); + } + _ { + break; + } + } + } + let multiplier = pow_uint_to_uint_as_float(10u, exponent); + //Note: not [int::pow], otherwise, we'll quickly + //end up with a nice overflow + if neg_exponent { + total = total / multiplier; + } else { + total = total * multiplier; + } + } + } + + if(pos < len) { + ret NaN(); + } else { + if(neg) { + total *= -1f; + } + ret total; + } +} + +/** + * Arithmetics + */ + +/** + * Compute the exponentiation of an integer by another integer as a float. + * + * + * @param x The base. + * @param pow The exponent. + * @return [NaN] of both [x] and [pow] are [0u], otherwise [x^pow]. + */ +fn pow_uint_to_uint_as_float(x: uint, pow: uint) -> float { + if x == 0u { + if pow == 0u { + ret NaN(); + } + ret 0.; + } + let my_pow = pow; + let total = 1f; + let multiplier = x as float; + while (my_pow > 0u) { + if my_pow % 2u == 1u { + total = total * multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + ret total; +} + + +/** + * Constants + */ + +//TODO: Once this is possible, replace the body of these functions +//by an actual constant. + +fn NaN() -> float { + ret 0./0.; +} + +fn infinity() -> float { + ret 1./0.; +} + +fn neg_infinity() -> float { + ret -1./0.; } // From 8c9dd54dedecd0ba94b96c8a79f2e62582afdf77 Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Wed, 12 Oct 2011 10:25:39 +0200 Subject: [PATCH 3/4] [Renaming] str_to_float is now float::from_str, float_to_str is now float::to_str --- src/comp/driver/rustc.rs | 2 +- src/comp/middle/typeck.rs | 4 ++-- src/comp/util/common.rs | 6 +++--- src/lib/float.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index 9c7923a2ffe63..4dc58c04fe5af 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -94,7 +94,7 @@ fn time<@T>(do_it: bool, what: str, thunk: fn() -> T) -> T { let rv = thunk(); let end = std::time::precise_time_s(); log_err #fmt["time: %s took %s s", what, - std::float::float_to_str(end - start, 3u)]; + std::float::to_str(end - start, 3u)]; ret rv; } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index 94ac0cf3ed187..695000f7cf93f 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -1303,8 +1303,8 @@ fn valid_range_bounds(l1: @ast::lit, l2: @ast::lit) -> bool { alt l1.node { ast::lit_float(s1) | ast::lit_mach_float(_, s1) { let s2 = lit_as_float(l2); - let f1 = std::float::str_to_float(s1); - let f2 = std::float::str_to_float(s2); + let f1 = std::float::from_str(s1); + let f2 = std::float::from_str(s2); ret *util::common::min(f1, f2) == f1 } ast::lit_uint(_) | ast::lit_char(_) { diff --git a/src/comp/util/common.rs b/src/comp/util/common.rs index a0cd519641be2..60f35816d9097 100644 --- a/src/comp/util/common.rs +++ b/src/comp/util/common.rs @@ -156,8 +156,8 @@ fn lit_in_range(l: @ast::lit, m1: @ast::lit, m2: @ast::lit) -> bool { frange(f1, f2) { alt l.node { ast::lit_float(f3) | ast::lit_mach_float(_, f3) { - std::float::str_to_float(f3) >= *min(f1, f2) && - std::float::str_to_float(f3) <= *max(f1, f2) + std::float::from_str(f3) >= *min(f1, f2) && + std::float::from_str(f3) <= *max(f1, f2) } _ { fail } } @@ -232,7 +232,7 @@ fn lits_to_range(l: @ast::lit, r: @ast::lit) -> range { } ast::lit_float(f1) | ast::lit_mach_float(_, f1) { alt r.node { ast::lit_float(f2) | ast::lit_mach_float(_, f2) { - frange(std::float::str_to_float(f1), std::float::str_to_float(f2)) + frange(std::float::from_str(f1), std::float::from_str(f2)) } _ { fail } } } diff --git a/src/lib/float.rs b/src/lib/float.rs index e961c34e66082..928f33ecc0243 100644 --- a/src/lib/float.rs +++ b/src/lib/float.rs @@ -2,7 +2,7 @@ * String conversions */ -fn float_to_str(num: float, digits: uint) -> str { +fn to_str(num: float, digits: uint) -> str { let accum = if num < 0.0 { num = -num; "-" } else { "" }; let trunc = num as uint; let frac = num - (trunc as float); @@ -36,7 +36,7 @@ fn float_to_str(num: float, digits: uint) -> str { * @return [NaN] if the string did not represent a valid number. * @return Otherwise, the floating-point number represented [num]. */ -fn str_to_float(num: str) -> float { +fn from_str(num: str) -> float { let pos = 0u; //Current byte position in the string. //Used to walk the string in O(n). let len = str::byte_len(num); //Length of the string, in bytes. From 75bda422df0c7816d4b987a564d11935a0cdb8db Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Wed, 12 Oct 2011 10:25:58 +0200 Subject: [PATCH 4/4] [Tests] added float tests --- src/test/stdtest/float.rs | 19 +++++++++++++++++++ src/test/stdtest/stdtest.rc | 1 + 2 files changed, 20 insertions(+) create mode 100644 src/test/stdtest/float.rs diff --git a/src/test/stdtest/float.rs b/src/test/stdtest/float.rs new file mode 100644 index 0000000000000..b77d83eb75735 --- /dev/null +++ b/src/test/stdtest/float.rs @@ -0,0 +1,19 @@ +use std; +import std::float; + +#[test] +fn test_from_str() { + assert ( float::from_str("3.14") == 3.14 ); + assert ( float::from_str("+3.14") == 3.14 ); + assert ( float::from_str("-3.14") == -3.14 ); + assert ( float::from_str("2.5E10") == 25000000000. ); + assert ( float::from_str("2.5e10") == 25000000000. ); + assert ( float::from_str("25000000000.E-10") == 2.5 ); + assert ( float::from_str("") == 0. ); + assert ( float::from_str(" ") == 0. ); + assert ( float::from_str(".") == 0. ); + assert ( float::from_str("5.") == 5. ); + assert ( float::from_str(".5") == 0.5 ); + assert ( float::from_str("0.5") == 0.5 ); + +} diff --git a/src/test/stdtest/stdtest.rc b/src/test/stdtest/stdtest.rc index b94f18f77f4af..46292756a47e7 100644 --- a/src/test/stdtest/stdtest.rc +++ b/src/test/stdtest/stdtest.rc @@ -29,6 +29,7 @@ mod sys; mod task; mod test; mod uint; +mod float; // Local Variables: // mode: rust