From 16f12e879ca1a1ae1a7026899ea49ea8463df655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sat, 19 Jan 2013 22:41:25 +0100 Subject: [PATCH 01/14] Moved all numeric modules in core into own directory Reason: Better grouping of related modules, future-proving for a more extensive math library. --- src/libcore/core.rc | 25 ++++++++++++--------- src/libcore/{ => num}/cmath.rs | 0 src/libcore/{ => num}/f32.rs | 0 src/libcore/{ => num}/f64.rs | 0 src/libcore/{ => num}/float.rs | 0 src/libcore/{ => num}/int-template.rs | 0 src/libcore/{ => num}/int-template/i16.rs | 0 src/libcore/{ => num}/int-template/i32.rs | 0 src/libcore/{ => num}/int-template/i64.rs | 0 src/libcore/{ => num}/int-template/i8.rs | 0 src/libcore/{ => num}/int-template/int.rs | 0 src/libcore/{ => num}/num.rs | 0 src/libcore/{ => num}/uint-template.rs | 0 src/libcore/{ => num}/uint-template/u16.rs | 0 src/libcore/{ => num}/uint-template/u32.rs | 0 src/libcore/{ => num}/uint-template/u64.rs | 0 src/libcore/{ => num}/uint-template/u8.rs | 0 src/libcore/{ => num}/uint-template/uint.rs | 0 18 files changed, 15 insertions(+), 10 deletions(-) rename src/libcore/{ => num}/cmath.rs (100%) rename src/libcore/{ => num}/f32.rs (100%) rename src/libcore/{ => num}/f64.rs (100%) rename src/libcore/{ => num}/float.rs (100%) rename src/libcore/{ => num}/int-template.rs (100%) rename src/libcore/{ => num}/int-template/i16.rs (100%) rename src/libcore/{ => num}/int-template/i32.rs (100%) rename src/libcore/{ => num}/int-template/i64.rs (100%) rename src/libcore/{ => num}/int-template/i8.rs (100%) rename src/libcore/{ => num}/int-template/int.rs (100%) rename src/libcore/{ => num}/num.rs (100%) rename src/libcore/{ => num}/uint-template.rs (100%) rename src/libcore/{ => num}/uint-template/u16.rs (100%) rename src/libcore/{ => num}/uint-template/u32.rs (100%) rename src/libcore/{ => num}/uint-template/u64.rs (100%) rename src/libcore/{ => num}/uint-template/u8.rs (100%) rename src/libcore/{ => num}/uint-template/uint.rs (100%) diff --git a/src/libcore/core.rc b/src/libcore/core.rc index 34804af70cae0..aece60652a21a 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -60,30 +60,33 @@ pub mod prelude; /* Primitive types */ -#[path = "int-template.rs"] #[merge = "int-template/int.rs"] +#[path = "num/int-template.rs"] #[merge = "num/int-template/int.rs"] pub mod int; -#[path = "int-template.rs"] #[merge = "int-template/i8.rs"] +#[path = "num/int-template.rs"] #[merge = "num/int-template/i8.rs"] pub mod i8; -#[path = "int-template.rs"] #[merge = "int-template/i16.rs"] +#[path = "num/int-template.rs"] #[merge = "num/int-template/i16.rs"] pub mod i16; -#[path = "int-template.rs"] #[merge = "int-template/i32.rs"] +#[path = "num/int-template.rs"] #[merge = "num/int-template/i32.rs"] pub mod i32; -#[path = "int-template.rs"] #[merge = "int-template/i64.rs"] +#[path = "num/int-template.rs"] #[merge = "num/int-template/i64.rs"] pub mod i64; -#[path = "uint-template.rs"] #[merge = "uint-template/uint.rs"] +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/uint.rs"] pub mod uint; -#[path = "uint-template.rs"] #[merge = "uint-template/u8.rs"] +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u8.rs"] pub mod u8; -#[path = "uint-template.rs"] #[merge = "uint-template/u16.rs"] +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u16.rs"] pub mod u16; -#[path = "uint-template.rs"] #[merge = "uint-template/u32.rs"] +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u32.rs"] pub mod u32; -#[path = "uint-template.rs"] #[merge = "uint-template/u64.rs"] +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u64.rs"] pub mod u64; +#[path = "num/float.rs"] pub mod float; +#[path = "num/f32.rs"] pub mod f32; +#[path = "num/f64.rs"] pub mod f64; pub mod nil; @@ -116,6 +119,7 @@ pub mod managed; /* Common traits */ pub mod from_str; +#[path = "num/num.rs"] pub mod num; pub mod iter; pub mod to_str; @@ -232,6 +236,7 @@ pub mod private; /* For internal use, not exported */ mod unicode; +#[path = "num/cmath.rs"] mod cmath; mod stackwalk; diff --git a/src/libcore/cmath.rs b/src/libcore/num/cmath.rs similarity index 100% rename from src/libcore/cmath.rs rename to src/libcore/num/cmath.rs diff --git a/src/libcore/f32.rs b/src/libcore/num/f32.rs similarity index 100% rename from src/libcore/f32.rs rename to src/libcore/num/f32.rs diff --git a/src/libcore/f64.rs b/src/libcore/num/f64.rs similarity index 100% rename from src/libcore/f64.rs rename to src/libcore/num/f64.rs diff --git a/src/libcore/float.rs b/src/libcore/num/float.rs similarity index 100% rename from src/libcore/float.rs rename to src/libcore/num/float.rs diff --git a/src/libcore/int-template.rs b/src/libcore/num/int-template.rs similarity index 100% rename from src/libcore/int-template.rs rename to src/libcore/num/int-template.rs diff --git a/src/libcore/int-template/i16.rs b/src/libcore/num/int-template/i16.rs similarity index 100% rename from src/libcore/int-template/i16.rs rename to src/libcore/num/int-template/i16.rs diff --git a/src/libcore/int-template/i32.rs b/src/libcore/num/int-template/i32.rs similarity index 100% rename from src/libcore/int-template/i32.rs rename to src/libcore/num/int-template/i32.rs diff --git a/src/libcore/int-template/i64.rs b/src/libcore/num/int-template/i64.rs similarity index 100% rename from src/libcore/int-template/i64.rs rename to src/libcore/num/int-template/i64.rs diff --git a/src/libcore/int-template/i8.rs b/src/libcore/num/int-template/i8.rs similarity index 100% rename from src/libcore/int-template/i8.rs rename to src/libcore/num/int-template/i8.rs diff --git a/src/libcore/int-template/int.rs b/src/libcore/num/int-template/int.rs similarity index 100% rename from src/libcore/int-template/int.rs rename to src/libcore/num/int-template/int.rs diff --git a/src/libcore/num.rs b/src/libcore/num/num.rs similarity index 100% rename from src/libcore/num.rs rename to src/libcore/num/num.rs diff --git a/src/libcore/uint-template.rs b/src/libcore/num/uint-template.rs similarity index 100% rename from src/libcore/uint-template.rs rename to src/libcore/num/uint-template.rs diff --git a/src/libcore/uint-template/u16.rs b/src/libcore/num/uint-template/u16.rs similarity index 100% rename from src/libcore/uint-template/u16.rs rename to src/libcore/num/uint-template/u16.rs diff --git a/src/libcore/uint-template/u32.rs b/src/libcore/num/uint-template/u32.rs similarity index 100% rename from src/libcore/uint-template/u32.rs rename to src/libcore/num/uint-template/u32.rs diff --git a/src/libcore/uint-template/u64.rs b/src/libcore/num/uint-template/u64.rs similarity index 100% rename from src/libcore/uint-template/u64.rs rename to src/libcore/num/uint-template/u64.rs diff --git a/src/libcore/uint-template/u8.rs b/src/libcore/num/uint-template/u8.rs similarity index 100% rename from src/libcore/uint-template/u8.rs rename to src/libcore/num/uint-template/u8.rs diff --git a/src/libcore/uint-template/uint.rs b/src/libcore/num/uint-template/uint.rs similarity index 100% rename from src/libcore/uint-template/uint.rs rename to src/libcore/num/uint-template/uint.rs From 76e8164cd0947103f605404be3a10cdb74f3c61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 27 Jan 2013 03:05:20 +0100 Subject: [PATCH 02/14] Added Round trait to core --- src/libcore/num/f32.rs | 27 ++++++++++++++++++++++++++ src/libcore/num/f64.rs | 27 ++++++++++++++++++++++++++ src/libcore/num/float.rs | 33 ++++++++++++++++++++++++++++++++ src/libcore/num/int-template.rs | 12 ++++++++++++ src/libcore/num/num.rs | 15 +++++++++++++++ src/libcore/num/uint-template.rs | 12 ++++++++++++ 6 files changed, 126 insertions(+) diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 795a9f9371c3c..43b68c5fc4f40 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -306,6 +306,33 @@ pub extern { fn floorf32(val: f32) -> f32; } +impl f32: num::Round { + #[inline(always)] + pure fn round(&self, mode: num::RoundMode) -> f32 { + match mode { + num::RoundDown => floor(*self), + num::RoundUp => ceil(*self), + num::RoundToZero if is_negative(*self) => ceil(*self), + num::RoundToZero => floor(*self), + num::RoundFromZero if is_negative(*self) => floor(*self), + num::RoundFromZero => ceil(*self) + } + } + + #[inline(always)] + pure fn floor(&self) -> f32 { floor(*self) } + #[inline(always)] + pure fn ceil(&self) -> f32 { ceil(*self) } + #[inline(always)] + pure fn fract(&self) -> f32 { + if is_negative(*self) { + (*self) - ceil(*self) + } else { + (*self) - floor(*self) + } + } +} + // // Local Variables: // mode: rust diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 8cd94c9357d61..851697012fc61 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -330,6 +330,33 @@ pub extern { fn floorf64(val: f64) -> f64; } +impl f64: num::Round { + #[inline(always)] + pure fn round(&self, mode: num::RoundMode) -> f64 { + match mode { + num::RoundDown => floor(*self), + num::RoundUp => ceil(*self), + num::RoundToZero if is_negative(*self) => ceil(*self), + num::RoundToZero => floor(*self), + num::RoundFromZero if is_negative(*self) => floor(*self), + num::RoundFromZero => ceil(*self) + } + } + + #[inline(always)] + pure fn floor(&self) -> f64 { floor(*self) } + #[inline(always)] + pure fn ceil(&self) -> f64 { ceil(*self) } + #[inline(always)] + pure fn fract(&self) -> f64 { + if is_negative(*self) { + (*self) - ceil(*self) + } else { + (*self) - floor(*self) + } + } +} + // // Local Variables: // mode: rust diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs index b90edec4d9612..1d1be6df87aba 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -488,6 +488,39 @@ impl float: num::One { static pure fn one() -> float { 1.0 } } +impl float: num::Round { + #[inline(always)] + pure fn round(&self, mode: num::RoundMode) -> float { + match mode { + num::RoundDown + => f64::floor(*self as f64) as float, + num::RoundUp + => f64::ceil(*self as f64) as float, + num::RoundToZero if is_negative(*self) + => f64::ceil(*self as f64) as float, + num::RoundToZero + => f64::floor(*self as f64) as float, + num::RoundFromZero if is_negative(*self) + => f64::floor(*self as f64) as float, + num::RoundFromZero + => f64::ceil(*self as f64) as float + } + } + + #[inline(always)] + pure fn floor(&self) -> float { f64::floor(*self as f64) as float} + #[inline(always)] + pure fn ceil(&self) -> float { f64::ceil(*self as f64) as float} + #[inline(always)] + pure fn fract(&self) -> float { + if is_negative(*self) { + (*self) - (f64::ceil(*self as f64) as float) + } else { + (*self) - (f64::floor(*self as f64) as float) + } + } +} + #[test] pub fn test_from_str() { assert from_str(~"3") == Some(3.); diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index 08508b09b70a7..7f3a15621acfb 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -200,6 +200,18 @@ impl T: num::One { static pure fn one() -> T { 1 } } +impl T: num::Round { + #[inline(always)] + pure fn round(&self, _: num::RoundMode) -> T { *self } + + #[inline(always)] + pure fn floor(&self) -> T { *self } + #[inline(always)] + pure fn ceil(&self) -> T { *self } + #[inline(always)] + pure fn fract(&self) -> T { 0 } +} + /** * Parse a buffer of bytes * diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index 5680e3116bfb6..db8a741f18587 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -35,3 +35,18 @@ pub trait Zero { pub trait One { static pure fn one() -> Self; } + +pub trait Round { + pure fn round(&self, mode: RoundMode) -> self; + + pure fn floor(&self) -> self; + pure fn ceil(&self) -> self; + pure fn fract(&self) -> self; +} + +pub enum RoundMode { + RoundDown, + RoundUp, + RoundToZero, + RoundFromZero +} diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs index 1cd447df00536..a59813b2df437 100644 --- a/src/libcore/num/uint-template.rs +++ b/src/libcore/num/uint-template.rs @@ -160,6 +160,18 @@ impl T: num::One { static pure fn one() -> T { 1 } } +impl T: num::Round { + #[inline(always)] + pure fn round(&self, _: num::RoundMode) -> T { *self } + + #[inline(always)] + pure fn floor(&self) -> T { *self } + #[inline(always)] + pure fn ceil(&self) -> T { *self } + #[inline(always)] + pure fn fract(&self) -> T { 0 } +} + /** * Parse a buffer of bytes * From 9d17da2ba8ef3e6c85e0053cafb50a57f0edd691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Thu, 24 Jan 2013 21:09:58 +0100 Subject: [PATCH 03/14] Added ToStrRadix and FromStrRadix traits --- src/libcore/num/num.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index db8a741f18587..5d26696f01877 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -9,6 +9,8 @@ // except according to those terms. //! An interface for numeric types +use cmp::Eq; +use option::{None, Option, Some}; pub trait Num { // FIXME: Trait composition. (#2616) @@ -50,3 +52,11 @@ pub enum RoundMode { RoundToZero, RoundFromZero } + +pub trait ToStrRadix { + pub pure fn to_str_radix(&self, radix: uint) -> ~str; +} + +pub trait FromStrRadix { + static pub pure fn from_str_radix(str: &str, radix: uint) -> Option; +} \ No newline at end of file From 19d78c3615ccca65cdd67ac3d250f8322c95addf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 20 Jan 2013 21:28:12 +0100 Subject: [PATCH 04/14] Added char::from_digit(), char::is_digit_radix() and an argument check to char::to_digit(). --- src/libcore/char.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/libcore/char.rs b/src/libcore/char.rs index b656879e7a422..8f1f0b3666b0a 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -118,6 +118,26 @@ pub pure fn is_digit(c: char) -> bool { unicode::general_category::No(c); } +/** + * Checks if a character parses as a numeric digit in the given radix. + * Compared to `is_digit()`, this function only recognizes the ascii + * characters `0-9`, `a-z` and `A-Z`. + * + * Returns `true` if `c` is a valid digit under `radix`, and `false` + * otherwise. + * + * Fails if given a `radix` > 36. + * + * Note: This just wraps `to_digit()`. + */ +#[inline(always)] +pub pure fn is_digit_radix(c: char, radix: uint) -> bool { + match to_digit(c, radix) { + Some(_) => true, + None => false + } +} + /** * Convert a char to the corresponding digit. * @@ -127,9 +147,15 @@ pub pure fn is_digit(c: char) -> bool { * between 0 and 9. If `c` is 'a' or 'A', 10. If `c` is * 'b' or 'B', 11, etc. Returns none if the char does not * refer to a digit in the given radix. + * + * # Failure + * Fails if given a `radix` outside the range `[0..36]`. */ #[inline] pub pure fn to_digit(c: char, radix: uint) -> Option { + if radix > 36 { + fail fmt!("to_digit: radix %? is to high (maximum 36)", radix) + } let val = match c { '0' .. '9' => c as uint - ('0' as uint), 'a' .. 'z' => c as uint + 10u - ('a' as uint), @@ -140,6 +166,30 @@ pub pure fn to_digit(c: char, radix: uint) -> Option { else { None } } +/** + * Converts a number to the ascii character representing it. + * + * Returns `Some(char)` if `num` represents one digit under `radix`, + * using one character of `0-9` or `a-z`, or `None` if it doesn't. + * + * Fails if given an `radix` > 36. + */ +#[inline] +pub pure fn from_digit(num: uint, radix: uint) -> Option { + if radix > 36 { + fail fmt!("from_digit: radix %? is to high (maximum 36)", num) + } + if num < radix { + if num < 10 { + Some(('0' as uint + num) as char) + } else { + Some(('a' as uint + num - 10u) as char) + } + } else { + None + } +} + /** * Return the hexadecimal unicode escape of a char. * From 197d25205a95a4c3c834d5d84d8a2a81489ecb1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Mon, 21 Jan 2013 00:40:02 +0100 Subject: [PATCH 05/14] Added some generic number functions to core::num Also fixes previous commit not compiling due to not finding Option. --- src/libcore/num/num.rs | 89 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index 5d26696f01877..15f9754873f3e 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -59,4 +59,93 @@ pub trait ToStrRadix { pub trait FromStrRadix { static pub pure fn from_str_radix(str: &str, radix: uint) -> Option; +} + +// Generic math functions: + +/// Dynamically calculates the value `inf` (`1/0`). +/// Can fail on integer types. +#[inline(always)] +pub pure fn infinity() -> T { + let _0: T = Zero::zero(); + let _1: T = One::one(); + _1 / _0 +} + +/// Dynamically calculates the value `-inf` (`-1/0`). +/// Can fail on integer types. +#[inline(always)] +pub pure fn neg_infinity() -> T { + let _0: T = Zero::zero(); + let _1: T = One::one(); + - _1 / _0 +} + +/// Dynamically calculates the value `NaN` (`0/0`). +/// Can fail on integer types. +#[inline(always)] +pub pure fn NaN() -> T { + let _0: T = Zero::zero(); + _0 / _0 +} + +/// Returns `true` if `num` has the value `inf` (`1/0`). +/// Can fail on integer types. +#[inline(always)] +pub pure fn is_infinity(num: &T) -> bool { + (*num) == (infinity::()) +} + +/// Returns `true` if `num` has the value `-inf` (`-1/0`). +/// Can fail on integer types. +#[inline(always)] +pub pure fn is_neg_infinity(num: &T) -> bool { + (*num) == (neg_infinity::()) +} + +/// Returns `true` if `num` has the value `NaN` (is not equal to itself). +#[inline(always)] +pub pure fn is_NaN(num: &T) -> bool { + (*num) != (*num) +} + +/// Returns `true` if `num` has the value `-0` (`1/num == -1/0`). +/// Can fail on integer types. +#[inline(always)] +pub pure fn is_neg_zero(num: &T) -> bool { + let _1: T = One::one(); + let _0: T = Zero::zero(); + *num == _0 && is_neg_infinity(&(_1 / *num)) +} + +/** + * Calculates a power to a given radix, optimized for uint `pow` and `radix`. + * + * Returns `radix^pow` as `T`. + * + * Note: + * Also returns `1` for `0^0`, despite that technically being an + * undefined number. The Reason for this is twofold: + * - If code written to use this function cares about that special case, it's + * probably going to catch it before making the call. + * - If code written to use this function doesn't care about it, it's + * probably assuming that `x^0` always equals `1`. + */ +pub pure fn pow_with_uint(radix: uint, pow: uint) -> T { + let _0: T = Zero::zero(); + let _1: T = One::one(); + + if pow == 0u { return _1; } + if radix == 0u { return _0; } + let mut my_pow = pow; + let mut total = _1; + let mut multiplier = Num::from_int(radix as int); + while (my_pow > 0u) { + if my_pow % 2u == 1u { + total *= multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + total } \ No newline at end of file From 882d8aea940da1c86f654d00e27f4ab234183ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 27 Jan 2013 03:13:33 +0100 Subject: [PATCH 06/14] Added generic string <-> number conversion functions to core::num. They unify the different implementations that exists in int-template.rs, uint-template.rs and float.rs into one pair of functions, which are also in principle usable for anything that implements the necessary numeric traits. Their usage is somewhat complex due to the large amount of arguments each one takes, but as they're not meant to be used directly that shouldn't be a problem. --- src/libcore/num/num.rs | 540 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 537 insertions(+), 3 deletions(-) diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index 15f9754873f3e..b466191352fd3 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -9,8 +9,12 @@ // except according to those terms. //! An interface for numeric types -use cmp::Eq; +use core::cmp::{Ord, Eq}; use option::{None, Option, Some}; +use char; +use str; +use kinds::Copy; +use vec; pub trait Num { // FIXME: Trait composition. (#2616) @@ -125,12 +129,12 @@ pub pure fn is_neg_zero(num: &T) -> bool { * * Note: * Also returns `1` for `0^0`, despite that technically being an - * undefined number. The Reason for this is twofold: + * undefined number. The reason for this is twofold: * - If code written to use this function cares about that special case, it's * probably going to catch it before making the call. * - If code written to use this function doesn't care about it, it's * probably assuming that `x^0` always equals `1`. - */ + */ pub pure fn pow_with_uint(radix: uint, pow: uint) -> T { let _0: T = Zero::zero(); let _1: T = One::one(); @@ -148,4 +152,534 @@ pub pure fn pow_with_uint(radix: uint, pow: uint) -> T { multiplier *= multiplier; } total +} + +pub enum ExponentFormat { + ExpNone, + ExpDec, + ExpBin +} + +pub enum SignificantDigits { + DigAll, + DigMax(uint), + DigExact(uint) +} + +pub enum SignFormat { + SignNone, + SignNeg, + SignAll +} + +/** + * Converts a number to its string representation as a byte vector. + * This is meant to be a common base implementation for all numeric string + * conversion functions like `to_str()` or `to_str_radix()`. + * + * # Arguments + * - `num` - The number to convert. Accepts any number that + * implements the numeric traits. + * - `radix` - Base to use. Accepts only the values 2-36. + * - `special` - Whether to attempt to compare to special values like + * `inf` or `NaN`. Also needed to detect negative 0. + * Can fail if it doesn't match `num`s type + * (see safety note). + * - `negative_zero` - Whether to treat the special value `-0` as + * `-0` or as `+0`. + * - `sign` - How to emit the sign. Options are: + * - `SignNone`: No sign at all. Basically emits `abs(num)`. + * - `SignNeg`: Only `-` on negative values. + * - `SignAll`: Both `+` on positive, and `-` on negative numbers. + * - `digits` - The amount of digits to use for emitting the + * fractional part, if any. Options are: + * - `DigAll`: All calculatable digits. Beware of bignums or + * fractions! + * - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros. + * - `DigExact(uint)`: Exactly N digits. + * + * # Return value + * A tuple containing the byte vector, and a boolean flag indicating + * whether it represents a special value like `inf`, `-inf`, `NaN` or not. + * It returns a tuple because there can be ambiguity between a special value + * and a number representation at higher bases. + * + * # Failure + * - Fails if `radix` < 2 or `radix` > 36. + * - Fails on wrong value for `special` (see safety note). + * + * # Safety note + * The function detects the special values `inf`, `-inf` and `NaN` by + * dynamically comparing `num` to `1 / 0`, `-1 / 0` and `0 / 0` + * (each of type T) if `special` is `true`. This will fail on integer types + * with a 'divide by zero'. Likewise, it will fail if `num` **is** one of + * those special values, and `special` is `false`, because then the + * algorithm just does normal calculations on them. + * + * # Possible improvements + * - Currently performs no rounding if truncating trailing digits. + * - Make function handle numbers with expensive copies better. + */ +pub pure fn to_str_bytes_common( + num: &T, radix: uint, special: bool, negative_zero: bool, + sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) { + if radix as int < 2 { + fail fmt!("to_str_bytes_common: radix %? to low, \ + must lie in the range [2, 36]", radix); + } else if radix as int > 36 { + fail fmt!("to_str_bytes_common: radix %? to high, \ + must lie in the range [2, 36]", radix); + } + + let _0: T = Zero::zero(); + let _1: T = One::one(); + + if special { + if is_NaN(num) { + return (str::to_bytes("NaN"), true); + } else if is_infinity(num){ + return match sign { + SignAll => (str::to_bytes("+inf"), true), + _ => (str::to_bytes("inf"), true) + } + } else if is_neg_infinity(num) { + return match sign { + SignNone => (str::to_bytes("inf"), true), + _ => (str::to_bytes("-inf"), true), + } + } + } + + let neg = *num < _0 || (negative_zero && *num == _0 + && special && is_neg_zero(num)); + let mut buf: ~[u8] = ~[]; + let radix_gen = Num::from_int::(radix as int); + + let mut deccum; + + // First emit the non-fractional part, looping at least once to make + // sure at least a `0` gets emitted. + deccum = num.round(RoundToZero); + loop { + // Calculate the absolute value of each digit instead of only + // doing it once for the whole number because a + // representable negative number doesn't necessary have an + // representable additive inverse of the same type + // (See twos complement). But we assume that for the + // numbers [-35 .. 0] we always have [0 .. 35]. + let current_digit_signed = deccum % radix_gen; + let current_digit = if current_digit_signed < _0 { + -current_digit_signed + } else { + current_digit_signed + }; + + // Decrease the deccumulator one digit at a time + deccum /= radix_gen; + deccum = deccum.round(RoundToZero); + + unsafe { // FIXME: Pureness workaround (#4568) + buf.push(char::from_digit(current_digit.to_int() as uint, radix) + .unwrap() as u8); + } + + // No more digits to calculate for the non-fractional part -> break + if deccum == _0 { break; } + } + + // If limited digits, calculate one digit more for rounding. + let (limit_digits, digit_count, exact) = match digits { + DigAll => (false, 0u, false), + DigMax(count) => (true, count+1, false), + DigExact(count) => (true, count+1, true) + }; + + // Decide what sign to put in front + match sign { + SignNeg | SignAll if neg => { + unsafe { // FIXME: Pureness workaround (#4568) + buf.push('-' as u8); + } + } + SignAll => { + unsafe { // FIXME: Pureness workaround (#4568) + buf.push('+' as u8); + } + } + _ => () + } + + unsafe { // FIXME: Pureness workaround (#4568) + vec::reverse(buf); + } + + // Remember start of the fractional digits. + // Points one beyond end of buf if none get generated, + // or at the '.' otherwise. + let start_fractional_digits = buf.len(); + + // Now emit the fractional part, if any + deccum = num.fract(); + if deccum != _0 || (limit_digits && exact && digit_count > 0) { + unsafe { // FIXME: Pureness workaround (#4568) + buf.push('.' as u8); + } + let mut dig = 0u; + + // calculate new digits while + // - there is no limit and there are digits left + // - or there is a limit, it's not reached yet and + // - it's exact + // - or it's a maximum, and there are still digits left + while (!limit_digits && deccum != _0) + || (limit_digits && dig < digit_count && ( + exact + || (!exact && deccum != _0) + ) + ) { + // Shift first fractional digit into the integer part + deccum *= radix_gen; + + // Calculate the absolute value of each digit. + // See note in first loop. + let current_digit_signed = deccum.round(RoundToZero); + let current_digit = if current_digit_signed < _0 { + -current_digit_signed + } else { + current_digit_signed + }; + + unsafe { // FIXME: Pureness workaround (#4568) + buf.push(char::from_digit( + current_digit.to_int() as uint, radix).unwrap() as u8); + } + + // Decrease the deccumulator one fractional digit at a time + deccum = deccum.fract(); + dig += 1u; + } + + // If digits are limited, and that limit has been reached, + // cut off the one extra digit, and depending on its value + // round the remaining ones. + if limit_digits && dig == digit_count { + let ascii2value = |chr: u8| { + char::to_digit(chr as char, radix).unwrap() as uint + }; + let value2ascii = |val: uint| { + char::from_digit(val, radix).unwrap() as u8 + }; + + unsafe { // FIXME: Pureness workaround (#4568) + let extra_digit = ascii2value(buf.pop()); + if extra_digit >= radix / 2 { // -> need to round + let mut i: int = buf.len() as int - 1; + loop { + // If reached left end of number, have to + // insert additional digit: + if i < 0 + || buf[i] == '-' as u8 + || buf[i] == '+' as u8 { + buf.insert((i + 1) as uint, value2ascii(1)); + break; + } + + // Skip the '.' + if buf[i] == '.' as u8 { i -= 1; loop; } + + // Either increment the digit, + // or set to 0 if max and carry the 1. + let current_digit = ascii2value(buf[i]); + if current_digit < (radix - 1) { + buf[i] = value2ascii(current_digit+1); + break; + } else { + buf[i] = value2ascii(0); + i -= 1; + } + } + } + } + } + } + + // if number of digits is not exact, remove all trailing '0's up to + // and including the '.' + if !exact { + let buf_max_i = buf.len() - 1; + + // index to truncate from + let mut i = buf_max_i; + + // discover trailing zeros of fractional part + while i > start_fractional_digits && buf[i] == '0' as u8 { + i -= 1; + } + + // Only attempt to truncate digits if buf has fractional digits + if i >= start_fractional_digits { + // If buf ends with '.', cut that too. + if buf[i] == '.' as u8 { i -= 1 } + + // only resize buf if we actually remove digits + if i < buf_max_i { + buf = buf.slice(0, i + 1); + } + } + } + + (buf, false) +} + +/** + * Converts a number to its string representation. This is a wrapper for + * `to_str_bytes_common()`, for details see there. + */ +#[inline(always)] +pub pure fn to_str_common( + num: &T, radix: uint, special: bool, negative_zero: bool, + sign: SignFormat, digits: SignificantDigits) -> (~str, bool) { + let (bytes, special) = to_str_bytes_common(num, radix, special, + negative_zero, sign, digits); + (str::from_bytes(bytes), special) +} + +// Some constants for from_str_bytes_common's input validation, +// they define minimum radix values for which the character is a valid digit. +priv const DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; +priv const DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; +priv const DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; + +/** + * Parses a byte slice as a number. This is meant to + * be a common base implementation for all numeric string conversion + * functions like `from_str()` or `from_str_radix()`. + * + * # Arguments + * - `buf` - The byte slice to parse. + * - `radix` - Which base to parse the number as. Accepts 2-36. + * - `negative` - Whether to accept negative numbers. + * - `fractional` - Whether to accept numbers with fractional parts. + * - `special` - Whether to accept special values like `inf` + * and `NaN`. Can conflict with `radix`, see Failure. + * - `exponent` - Which exponent format to accept. Options are: + * - `ExpNone`: No Exponent, accepts just plain numbers like `42` or + * `-8.2`. + * - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or + * `8.2E-2`. The exponent string itself is always base 10. + * Can conflict with `radix`, see Failure. + * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or + * `FFp128`. The exponent string itself is always base 10. + * Can conflict with `radix`, see Failure. + * - `empty_zero` - Whether to accept a empty `buf` as a 0 or not. + * + * # Return value + * Returns `Some(n)` if `buf` parses to a number n without overflowing, and + * `None` otherwise, depending on the constraints set by the remaining + * arguments. + * + * # Failure + * - Fails if `radix` < 2 or `radix` > 36. + * - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict + * between digit and exponent sign `'e'`. + * - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict + * between digit and exponent sign `'p'`. + * - Fails if `radix` > 18 and `special == true` due to conflict + * between digit and lowest first character in `inf` and `NaN`, the `'i'`. + * + * # Possible improvements + * - Could accept option to allow ignoring underscores, allowing for numbers + * formated like `FF_AE_FF_FF`. + */ +pub pure fn from_str_bytes_common( + buf: &[u8], radix: uint, negative: bool, fractional: bool, + special: bool, exponent: ExponentFormat, empty_zero: bool + ) -> Option { + match exponent { + ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' + => fail fmt!("from_str_bytes_common: radix %? incompatible with \ + use of 'e' as decimal exponent", radix), + ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' + => fail fmt!("from_str_bytes_common: radix %? incompatible with \ + use of 'p' as binary exponent", radix), + _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' + => fail fmt!("from_str_bytes_common: radix %? incompatible with \ + special values 'inf' and 'NaN'", radix), + _ if radix as int < 2 + => fail fmt!("from_str_bytes_common: radix %? to low, \ + must lie in the range [2, 36]", radix), + _ if radix as int > 36 + => fail fmt!("from_str_bytes_common: radix %? to high, \ + must lie in the range [2, 36]", radix), + _ => () + } + + let _0: T = Zero::zero(); + let _1: T = One::one(); + let radix_gen: T = Num::from_int(radix as int); + + let len = buf.len(); + + if len == 0 { + if empty_zero { + return Some(_0); + } else { + return None; + } + } + + if special { + if buf == str::to_bytes("inf") || buf == str::to_bytes("+inf") { + return Some(infinity()); + } else if buf == str::to_bytes("-inf") { + if negative { + return Some(neg_infinity()); + } else { + return None; + } + } else if buf == str::to_bytes("NaN") { + return Some(NaN()); + } + } + + let (start, accum_positive) = match buf[0] { + '-' as u8 if !negative => return None, + '-' as u8 => (1u, false), + '+' as u8 => (1u, true), + _ => (0u, true) + }; + + // Initialize accumulator with signed zero for floating point parsing to + // work + let mut accum = if accum_positive { _0 } else { -_1 * _0}; + let mut last_accum = accum; // Necessary to detect overflow + let mut i = start; + let mut exp_found = false; + + // Parse integer part of number + while i < len { + let c = buf[i] as char; + + match char::to_digit(c, radix) { + Some(digit) => { + // shift accum one digit left + accum *= radix_gen; + + // add/subtract current digit depending on sign + if accum_positive { + accum += Num::from_int(digit as int); + } else { + accum -= Num::from_int(digit as int); + } + + // Detect overflow by comparing to last value + if accum_positive && accum < last_accum { return None; } + if !accum_positive && accum > last_accum { return None; } + last_accum = accum; + } + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_found = true; + break; // start of exponent + } + '.' if fractional => { + i += 1u; // skip the '.' + break; // start of fractional part + } + _ => return None // invalid number + } + } + + i += 1u; + } + + // Parse fractional part of number + // Skip if already reached start of exponent + if !exp_found { + let mut power = _1; + + while i < len { + let c = buf[i] as char; + + match char::to_digit(c, radix) { + Some(digit) => { + // Decrease power one order of magnitude + power /= radix_gen; + + // add/subtract current digit depending on sign + if accum_positive { + accum += Num::from_int::(digit as int) * power; + } else { + accum -= Num::from_int::(digit as int) * power; + } + + // Detect overflow by comparing to last value + if accum_positive && accum < last_accum { return None; } + if !accum_positive && accum > last_accum { return None; } + last_accum = accum; + } + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_found = true; + break; // start of exponent + } + _ => return None // invalid number + } + } + + i += 1u; + } + } + + // Special case: buf not empty, but does not contain any digit in front + // of the exponent sign -> number is empty string + if i == start { + if empty_zero { + return Some(_0); + } else { + return None; + } + } + + let mut multiplier = _1; + + if exp_found { + let c = buf[i] as char; + let base = match (c, exponent) { + ('e', ExpDec) | ('E', ExpDec) => 10u, + ('p', ExpBin) | ('P', ExpBin) => 2u, + _ => return None // char doesn't fit given exponent format + }; + + // parse remaining bytes as decimal integer, + // skipping the exponent char + let exp: Option = from_str_bytes_common( + buf.view(i+1, len), 10, true, false, false, ExpNone, false); + + match exp { + Some(exp_pow) => { + multiplier = if exp_pow < 0 { + _1 / pow_with_uint::(base, (-exp_pow.to_int()) as uint) + } else { + pow_with_uint::(base, exp_pow.to_int() as uint) + } + } + None => return None // invalid exponent -> invalid number + } + } + + Some(accum * multiplier) +} + +/** + * Parses a string as a number. This is a wrapper for + * `from_str_bytes_common()`, for details see there. + */ +#[inline(always)] +pub pure fn from_str_common( + buf: &str, radix: uint, negative: bool, fractional: bool, + special: bool, exponent: ExponentFormat, empty_zero: bool + ) -> Option { + from_str_bytes_common(str::to_bytes(buf), radix, negative, + fractional, special, exponent, empty_zero) } \ No newline at end of file From a1ee81cf13412765230c95b9ae3bf0c92c3f89c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 27 Jan 2013 03:20:15 +0100 Subject: [PATCH 07/14] Converted libcore/int-template.rs to the new string functions. - Moved ToStr implementation of integers to int-template.rs. - Marked the `str()` function as deprecated. - Forwarded all conversion functions to `core::num::to_str_common()` and `core::num::from_str_common()`. - Fixed most places in the codebase where `to_str()` is being used. - Added int-template to_str and from_str overflow tests. --- src/libcore/num/int-template.rs | 178 +++++++++++++++++------- src/libcore/to_str.rs | 20 --- src/librustc/driver/driver.rs | 8 +- src/librustc/metadata/encoder.rs | 2 +- src/librustc/middle/trans/base.rs | 2 +- src/test/run-pass/reflect-visit-data.rs | 2 +- 6 files changed, 133 insertions(+), 79 deletions(-) diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index 7f3a15621acfb..27e3b0d04ea05 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -17,7 +17,9 @@ use T = self::inst::T; use char; use cmp::{Eq, Ord}; use cmp; +use to_str::ToStr; use from_str::FromStr; +use num::{ToStrRadix, FromStrRadix}; use num; use num::Num::from_int; use prelude::*; @@ -212,70 +214,87 @@ impl T: num::Round { pure fn fract(&self) -> T { 0 } } -/** - * Parse a buffer of bytes - * - * # Arguments - * - * * buf - A byte buffer - * * radix - The base of the number - */ -pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option { - if vec::len(buf) == 0u { return None; } - let mut i = vec::len(buf) - 1u; - let mut start = 0u; - let mut power = 1 as T; - - if buf[0] == ('-' as u8) { - power = -1 as T; - start = 1u; - } - let mut n = 0 as T; - loop { - match char::to_digit(buf[i] as char, radix) { - Some(d) => n += (d as T) * power, - None => return None - } - power *= radix as T; - if i <= start { return Some(n); } - i -= 1u; - }; +// String conversion functions and impl str -> num + +/// Parse a string as a number in base 10. +#[inline(always)] +pub pure fn from_str(s: &str) -> Option { + num::from_str_common(s, 10u, true, false, false, + num::ExpNone, false) } -/// Parse a string to an int +/// Parse a string as a number in the given base. #[inline(always)] -pub pure fn from_str(s: &str) -> Option -{ - parse_bytes(str::to_bytes(s), 10u) +pub pure fn from_str_radix(s: &str, radix: uint) -> Option { + num::from_str_common(s, radix, true, false, false, + num::ExpNone, false) +} + +/// Parse a byte slice as a number in the given base. +#[inline(always)] +pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option { + num::from_str_bytes_common(buf, radix, true, false, false, + num::ExpNone, false) } impl T : FromStr { #[inline(always)] - static pure fn from_str(s: &str) -> Option { from_str(s) } + static pure fn from_str(s: &str) -> Option { + from_str(s) + } } -/// Convert to a string in a given base -#[inline(always)] -pub pure fn to_str(n: T, radix: uint) -> ~str { - do to_str_bytes(n, radix) |slice| { - do vec::as_imm_buf(slice) |p, len| { - unsafe { str::raw::from_buf_len(p, len) } - } +impl T : FromStrRadix { + #[inline(always)] + static pure fn from_str_radix(&self, s: &str, radix: uint) -> Option { + from_str_radix(s, radix) } } +// String conversion functions and impl num -> str + +/// Convert to a string as a byte slice in a given base. #[inline(always)] pub pure fn to_str_bytes(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U { - if n < 0 as T { - uint::to_str_bytes(true, -n as uint, radix, f) - } else { - uint::to_str_bytes(false, n as uint, radix, f) - } + let (buf, _) = num::to_str_bytes_common(&n, radix, false, false, + num::SignNeg, num::DigAll); + f(buf) } -/// Convert to a string +/// Convert to a string in base 10. #[inline(always)] -pub pure fn str(i: T) -> ~str { return to_str(i, 10u); } +pub pure fn to_str(num: T) -> ~str { + let (buf, _) = num::to_str_common(&num, 10u, false, false, + num::SignNeg, num::DigAll); + buf +} + +/// Convert to a string in a given base. +#[inline(always)] +pub pure fn to_str_radix(num: T, radix: uint) -> ~str { + let (buf, _) = num::to_str_common(&num, radix, false, false, + num::SignNeg, num::DigAll); + buf +} + +/// Convert to a string. +/// *Deprecated*, use to_str() instead. +#[inline(always)] +pub pure fn str(i: T) -> ~str { to_str(i) } + +impl T : ToStr { + #[inline(always)] + pure fn to_str() -> ~str { + to_str(self) + } +} + +impl T : ToStrRadix { + #[inline(always)] + pure fn to_str_radix(&self, radix: uint) -> ~str { + to_str_radix(*self, radix) + } +} #[test] fn test_from_str() { @@ -322,11 +341,66 @@ fn test_parse_bytes() { #[test] fn test_to_str() { - assert (to_str(0 as T, 10u) == ~"0"); - assert (to_str(1 as T, 10u) == ~"1"); - assert (to_str(-1 as T, 10u) == ~"-1"); - assert (to_str(127 as T, 16u) == ~"7f"); - assert (to_str(100 as T, 10u) == ~"100"); + assert (to_str_radix(0 as T, 10u) == ~"0"); + assert (to_str_radix(1 as T, 10u) == ~"1"); + assert (to_str_radix(-1 as T, 10u) == ~"-1"); + assert (to_str_radix(127 as T, 16u) == ~"7f"); + assert (to_str_radix(100 as T, 10u) == ~"100"); + +} + +#[test] +fn test_int_to_str_overflow() { + let mut i8_val: i8 = 127_i8; + assert (i8::to_str(i8_val) == ~"127"); + + i8_val += 1 as i8; + assert (i8::to_str(i8_val) == ~"-128"); + + let mut i16_val: i16 = 32_767_i16; + assert (i16::to_str(i16_val) == ~"32767"); + + i16_val += 1 as i16; + assert (i16::to_str(i16_val) == ~"-32768"); + + let mut i32_val: i32 = 2_147_483_647_i32; + assert (i32::to_str(i32_val) == ~"2147483647"); + + i32_val += 1 as i32; + assert (i32::to_str(i32_val) == ~"-2147483648"); + + let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; + assert (i64::to_str(i64_val) == ~"9223372036854775807"); + + i64_val += 1 as i64; + assert (i64::to_str(i64_val) == ~"-9223372036854775808"); +} + +#[test] +fn test_int_from_str_overflow() { + let mut i8_val: i8 = 127_i8; + assert (i8::from_str(~"127") == Some(i8_val)); + + i8_val += 1 as i8; + assert (i8::from_str(~"-128") == Some(i8_val)); + + let mut i16_val: i16 = 32_767_i16; + assert (i16::from_str(~"32767") == Some(i16_val)); + + i16_val += 1 as i16; + assert (i16::from_str(~"-32768") == Some(i16_val)); + + let mut i32_val: i32 = 2_147_483_647_i32; + assert (i32::from_str(~"2147483647") == Some(i32_val)); + + i32_val += 1 as i32; + assert (i32::from_str(~"-2147483648") == Some(i32_val)); + + let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; + assert (i64::from_str(~"9223372036854775807") == Some(i64_val)); + + i64_val += 1 as i64; + assert (i64::from_str(~"-9223372036854775808") == Some(i64_val)); } #[test] diff --git a/src/libcore/to_str.rs b/src/libcore/to_str.rs index b1fb1fdd483c4..054512811be17 100644 --- a/src/libcore/to_str.rs +++ b/src/libcore/to_str.rs @@ -24,26 +24,6 @@ use vec; pub trait ToStr { pub pure fn to_str() -> ~str; } -impl int: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::int::str(self) } -} -impl i8: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::i8::str(self) } -} -impl i16: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::i16::str(self) } -} -impl i32: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::i32::str(self) } -} -impl i64: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::i64::str(self) } -} impl uint: ToStr { #[inline(always)] pure fn to_str() -> ~str { ::uint::str(self) } diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 7f7c01149888b..3dd49f661eefd 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -382,21 +382,21 @@ pub fn pretty_print_input(sess: Session, +cfg: ast::crate_cfg, input: input, match node { pprust::node_item(s, item) => { pp::space(s.s); - pprust::synth_comment(s, int::to_str(item.id, 10u)); + pprust::synth_comment(s, int::to_str(item.id)); } pprust::node_block(s, ref blk) => { pp::space(s.s); pprust::synth_comment( - s, ~"block " + int::to_str((*blk).node.id, 10u)); + s, ~"block " + int::to_str((*blk).node.id)); } pprust::node_expr(s, expr) => { pp::space(s.s); - pprust::synth_comment(s, int::to_str(expr.id, 10u)); + pprust::synth_comment(s, int::to_str(expr.id)); pprust::pclose(s); } pprust::node_pat(s, pat) => { pp::space(s.s); - pprust::synth_comment(s, ~"pat " + int::to_str(pat.id, 10u)); + pprust::synth_comment(s, ~"pat " + int::to_str(pat.id)); } } } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 33879237ffb49..b8a1b942453f0 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -245,7 +245,7 @@ fn encode_discriminant(ecx: @encode_ctxt, ebml_w: writer::Encoder, fn encode_disr_val(_ecx: @encode_ctxt, ebml_w: writer::Encoder, disr_val: int) { ebml_w.start_tag(tag_disr_val); - ebml_w.writer.write(str::to_bytes(int::to_str(disr_val,10u))); + ebml_w.writer.write(str::to_bytes(int::to_str(disr_val))); ebml_w.end_tag(); } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index f394c98ab6f4b..5a8fe94a4390a 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -679,7 +679,7 @@ pub fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t, let variant_cx = sub_block(cx, ~"enum-iter-variant-" + - int::to_str(variant.disr_val, 10u)); + int::to_str(variant.disr_val)); AddCase(llswitch, C_int(ccx, variant.disr_val), variant_cx.llbb); let variant_cx = iter_variant(variant_cx, llunion_a_ptr, *variant, diff --git a/src/test/run-pass/reflect-visit-data.rs b/src/test/run-pass/reflect-visit-data.rs index 3ea8ef23ea928..96bddf7099b8e 100644 --- a/src/test/run-pass/reflect-visit-data.rs +++ b/src/test/run-pass/reflect-visit-data.rs @@ -517,7 +517,7 @@ impl my_visitor: TyVisitor { } fn visit_int(&self) -> bool { do self.get::() |i| { - self.vals += ~[int::to_str(i, 10u)]; + self.vals += ~[int::to_str(i)]; }; true } From 3f5a70e2b97ad0aef2330eebaaa8e2605c9c805d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Thu, 24 Jan 2013 21:47:57 +0100 Subject: [PATCH 08/14] Converted libcore/uint-template.rs to the new string functions. - Moved ToStr implementation of unsigned integers to uint-template.rs. - Marked the `str()` function as deprecated. - Forwarded all conversion functions to `core::num::to_str_common()` and `core::num::from_str_common()`. - Fixed most places in the codebase where `to_str()` is being used. - Added uint-template to_str and from_str overflow tests. --- src/libcore/char.rs | 2 +- src/libcore/extfmt.rs | 2 +- src/libcore/hash.rs | 4 +- src/libcore/io.rs | 2 +- src/libcore/num/int-template.rs | 16 +- src/libcore/num/uint-template.rs | 238 +++++++++++---------- src/libcore/to_str.rs | 20 -- src/librustc/metadata/tyencode.rs | 4 +- src/librustc/middle/typeck/infer/to_str.rs | 2 +- src/libstd/bigint.rs | 2 +- src/libstd/md4.rs | 2 +- src/libstd/sha1.rs | 2 +- src/libsyntax/parse/lexer.rs | 4 +- src/libsyntax/print/pprust.rs | 10 +- 14 files changed, 155 insertions(+), 155 deletions(-) diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 8f1f0b3666b0a..2fcd233203288 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -200,7 +200,7 @@ pub pure fn from_digit(num: uint, radix: uint) -> Option { * - chars above 0x10000 get 8-digit escapes: `\\UNNNNNNNN` */ pub pure fn escape_unicode(c: char) -> ~str { - let s = u32::to_str(c as u32, 16u); + let s = u32::to_str_radix(c as u32, 16u); let (c, pad) = (if c <= '\xff' { ('x', 2u) } else if c <= '\uffff' { ('u', 4u) } else { ('U', 8u) }); diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index e452735777ece..4627c04801539 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -596,7 +596,7 @@ pub mod rt { return if prec == 0u && num == 0u { ~"" } else { - let s = uint::to_str(num, radix); + let s = uint::to_str_radix(num, radix); let len = str::char_len(s); if len < prec { let diff = prec - len; diff --git a/src/libcore/hash.rs b/src/libcore/hash.rs index d9e53811b65c4..d676570e1e6cb 100644 --- a/src/libcore/hash.rs +++ b/src/libcore/hash.rs @@ -354,7 +354,7 @@ impl &SipState : Streaming { let r = self.result_bytes(); let mut s = ~""; for vec::each(r) |b| { - s += uint::to_str(*b as uint, 16u); + s += uint::to_str_radix(*b as uint, 16u); } move s } @@ -449,7 +449,7 @@ pub fn test_siphash() { fn to_hex_str(r: &[u8 * 8]) -> ~str { let mut s = ~""; for vec::each(*r) |b| { - s += uint::to_str(*b as uint, 16u); + s += uint::to_str_radix(*b as uint, 16u); } move s } diff --git a/src/libcore/io.rs b/src/libcore/io.rs index d2ddbc40885e3..b726cf62cfe52 100644 --- a/src/libcore/io.rs +++ b/src/libcore/io.rs @@ -910,7 +910,7 @@ impl T : WriterUtil { int::to_str_bytes(n, 10u, |bytes| self.write(bytes)) } fn write_uint(&self, n: uint) { - uint::to_str_bytes(false, n, 10u, |bytes| self.write(bytes)) + uint::to_str_bytes(n, 10u, |bytes| self.write(bytes)) } fn write_le_uint(&self, n: uint) { u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v)) diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index 27e3b0d04ea05..6263a9d887bb2 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -353,25 +353,25 @@ fn test_to_str() { fn test_int_to_str_overflow() { let mut i8_val: i8 = 127_i8; assert (i8::to_str(i8_val) == ~"127"); - + i8_val += 1 as i8; assert (i8::to_str(i8_val) == ~"-128"); let mut i16_val: i16 = 32_767_i16; assert (i16::to_str(i16_val) == ~"32767"); - + i16_val += 1 as i16; assert (i16::to_str(i16_val) == ~"-32768"); let mut i32_val: i32 = 2_147_483_647_i32; assert (i32::to_str(i32_val) == ~"2147483647"); - + i32_val += 1 as i32; assert (i32::to_str(i32_val) == ~"-2147483648"); let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; assert (i64::to_str(i64_val) == ~"9223372036854775807"); - + i64_val += 1 as i64; assert (i64::to_str(i64_val) == ~"-9223372036854775808"); } @@ -380,25 +380,25 @@ fn test_int_to_str_overflow() { fn test_int_from_str_overflow() { let mut i8_val: i8 = 127_i8; assert (i8::from_str(~"127") == Some(i8_val)); - + i8_val += 1 as i8; assert (i8::from_str(~"-128") == Some(i8_val)); let mut i16_val: i16 = 32_767_i16; assert (i16::from_str(~"32767") == Some(i16_val)); - + i16_val += 1 as i16; assert (i16::from_str(~"-32768") == Some(i16_val)); let mut i32_val: i32 = 2_147_483_647_i32; assert (i32::from_str(~"2147483647") == Some(i32_val)); - + i32_val += 1 as i32; assert (i32::from_str(~"-2147483648") == Some(i32_val)); let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; assert (i64::from_str(~"9223372036854775807") == Some(i64_val)); - + i64_val += 1 as i64; assert (i64::from_str(~"-9223372036854775808") == Some(i64_val)); } diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs index a59813b2df437..0c39186824a72 100644 --- a/src/libcore/num/uint-template.rs +++ b/src/libcore/num/uint-template.rs @@ -17,9 +17,13 @@ use T_SIGNED = self::inst::T_SIGNED; use char; use cmp::{Eq, Ord}; +use cmp; +use to_str::ToStr; use from_str::FromStr; +use num::{ToStrRadix, FromStrRadix}; use num; use option::{None, Option, Some}; +use prelude::*; use str; use uint; use vec; @@ -172,135 +176,97 @@ impl T: num::Round { pure fn fract(&self) -> T { 0 } } -/** - * Parse a buffer of bytes - * - * # Arguments - * - * * buf - A byte buffer - * * radix - The base of the number - * - * # Failure - * - * `buf` must not be empty - */ -pub pure fn parse_bytes(buf: &[const u8], radix: uint) -> Option { - if vec::len(buf) == 0u { return None; } - let mut i = vec::len(buf) - 1u; - let mut power = 1u as T; - let mut n = 0u as T; - loop { - match char::to_digit(buf[i] as char, radix) { - Some(d) => n += d as T * power, - None => return None - } - power *= radix as T; - if i == 0u { return Some(n); } - i -= 1u; - }; -} +// String conversion functions and impl str -> num -/// Parse a string to an int +/// Parse a string as a number in base 10. #[inline(always)] -pub pure fn from_str(s: &str) -> Option -{ - parse_bytes(str::to_bytes(s), 10u) +pub pure fn from_str(s: &str) -> Option { + num::from_str_common(s, 10u, false, false, false, + num::ExpNone, false) } -impl T : FromStr { - #[inline(always)] - static pure fn from_str(s: &str) -> Option { from_str(s) } +/// Parse a string as a number in the given base. +#[inline(always)] +pub pure fn from_str_radix(s: &str, radix: uint) -> Option { + num::from_str_common(s, radix, false, false, false, + num::ExpNone, false) } -/// Parse a string as an unsigned integer. -pub fn from_str_radix(buf: &str, radix: u64) -> Option { - if str::len(buf) == 0u { return None; } - let mut i = str::len(buf) - 1u; - let mut power = 1u64, n = 0u64; - loop { - match char::to_digit(buf[i] as char, radix as uint) { - Some(d) => n += d as u64 * power, - None => return None - } - power *= radix; - if i == 0u { return Some(n); } - i -= 1u; - }; +/// Parse a byte slice as a number in the given base. +#[inline(always)] +pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option { + num::from_str_bytes_common(buf, radix, false, false, false, + num::ExpNone, false) } -/** - * Convert to a string in a given base - * - * # Failure - * - * Fails if `radix` < 2 or `radix` > 16 - */ -#[inline(always)] -pub pure fn to_str(num: T, radix: uint) -> ~str { - do to_str_bytes(false, num, radix) |slice| { - do vec::as_imm_buf(slice) |p, len| { - unsafe { str::raw::from_buf_len(p, len) } - } +impl T : FromStr { + #[inline(always)] + static pure fn from_str(s: &str) -> Option { + from_str(s) } } -/// Low-level helper routine for string conversion. -pub pure fn to_str_bytes(neg: bool, num: T, radix: uint, - f: fn(v: &[u8]) -> U) -> U { - +impl T : FromStrRadix { #[inline(always)] - pure fn digit(n: T) -> u8 { - if n <= 9u as T { - n as u8 + '0' as u8 - } else if n <= 15u as T { - (n - 10 as T) as u8 + 'a' as u8 - } else { - die!(); - } + static pure fn from_str_radix(&self, s: &str, radix: uint) -> Option { + from_str_radix(s, radix) } +} - assert (1u < radix && radix <= 16u); - - // Enough room to hold any number in any radix. - // Worst case: 64-bit number, binary-radix, with - // a leading negative sign = 65 bytes. - let buf : [mut u8 * 65] = [mut 0u8, ..65]; - let len = buf.len(); - - let mut i = len; - let mut n = num; - let radix = radix as T; - loop { - i -= 1u; - assert 0u < i && i < len; - buf[i] = digit(n % radix); - n /= radix; - if n == 0 as T { break; } - } +// String conversion functions and impl num -> str - assert 0u < i && i < len; +/// Convert to a string as a byte slice in a given base. +#[inline(always)] +pub pure fn to_str_bytes(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U { + let (buf, _) = num::to_str_bytes_common(&n, radix, false, false, + num::SignNeg, num::DigAll); + f(buf) +} - if neg { - i -= 1u; - buf[i] = '-' as u8; - } +/// Convert to a string in base 10. +#[inline(always)] +pub pure fn to_str(num: T) -> ~str { + let (buf, _) = num::to_str_common(&num, 10u, false, false, + num::SignNeg, num::DigAll); + buf +} - f(vec::view(buf, i, len)) +/// Convert to a string in a given base. +#[inline(always)] +pub pure fn to_str_radix(num: T, radix: uint) -> ~str { + let (buf, _) = num::to_str_common(&num, radix, false, false, + num::SignNeg, num::DigAll); + buf } -/// Convert to a string +/// Convert to a string. +/// *Deprecated*, use to_str() instead. #[inline(always)] -pub pure fn str(i: T) -> ~str { return to_str(i, 10u); } +pub pure fn str(i: T) -> ~str { to_str(i) } + +impl T : ToStr { + #[inline(always)] + pure fn to_str() -> ~str { + to_str(self) + } +} + +impl T : ToStrRadix { + #[inline(always)] + pure fn to_str_radix(&self, radix: uint) -> ~str { + to_str_radix(*self, radix) + } +} #[test] pub fn test_to_str() { - assert to_str(0 as T, 10u) == ~"0"; - assert to_str(1 as T, 10u) == ~"1"; - assert to_str(2 as T, 10u) == ~"2"; - assert to_str(11 as T, 10u) == ~"11"; - assert to_str(11 as T, 16u) == ~"b"; - assert to_str(255 as T, 16u) == ~"ff"; - assert to_str(0xff as T, 10u) == ~"255"; + assert to_str_radix(0 as T, 10u) == ~"0"; + assert to_str_radix(1 as T, 10u) == ~"1"; + assert to_str_radix(2 as T, 10u) == ~"2"; + assert to_str_radix(11 as T, 10u) == ~"11"; + assert to_str_radix(11 as T, 16u) == ~"b"; + assert to_str_radix(255 as T, 16u) == ~"ff"; + assert to_str_radix(0xff as T, 10u) == ~"255"; } #[test] @@ -330,18 +296,72 @@ pub fn test_parse_bytes() { assert parse_bytes(to_bytes(~"_"), 2u).is_none(); } +#[test] +fn test_uint_to_str_overflow() { + let mut u8_val: u8 = 255_u8; + assert (u8::to_str(u8_val) == ~"255"); + + u8_val += 1 as u8; + assert (u8::to_str(u8_val) == ~"0"); + + let mut u16_val: u16 = 65_535_u16; + assert (u16::to_str(u16_val) == ~"65535"); + + u16_val += 1 as u16; + assert (u16::to_str(u16_val) == ~"0"); + + let mut u32_val: u32 = 4_294_967_295_u32; + assert (u32::to_str(u32_val) == ~"4294967295"); + + u32_val += 1 as u32; + assert (u32::to_str(u32_val) == ~"0"); + + let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; + assert (u64::to_str(u64_val) == ~"18446744073709551615"); + + u64_val += 1 as u64; + assert (u64::to_str(u64_val) == ~"0"); +} + +#[test] +fn test_uint_from_str_overflow() { + let mut u8_val: u8 = 255_u8; + assert (u8::from_str(~"255") == Some(u8_val)); + + u8_val += 1 as u8; + assert (u8::from_str(~"0") == Some(u8_val)); + + let mut u16_val: u16 = 65_535_u16; + assert (u16::from_str(~"65535") == Some(u16_val)); + + u16_val += 1 as u16; + assert (u16::from_str(~"0") == Some(u16_val)); + + let mut u32_val: u32 = 4_294_967_295_u32; + assert (u32::from_str(~"4294967295") == Some(u32_val)); + + u32_val += 1 as u32; + assert (u32::from_str(~"0") == Some(u32_val)); + + let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; + assert (u64::from_str(~"18446744073709551615") == Some(u64_val)); + + u64_val += 1 as u64; + assert (u64::from_str(~"0") == Some(u64_val)); +} + #[test] #[should_fail] #[ignore(cfg(windows))] pub fn to_str_radix1() { - uint::to_str(100u, 1u); + uint::to_str_radix(100u, 1u); } #[test] #[should_fail] #[ignore(cfg(windows))] -pub fn to_str_radix17() { - uint::to_str(100u, 17u); +pub fn to_str_radix37() { + uint::to_str_radix(100u, 37u); } use io; diff --git a/src/libcore/to_str.rs b/src/libcore/to_str.rs index 054512811be17..13b09590b4480 100644 --- a/src/libcore/to_str.rs +++ b/src/libcore/to_str.rs @@ -24,26 +24,6 @@ use vec; pub trait ToStr { pub pure fn to_str() -> ~str; } -impl uint: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::uint::str(self) } -} -impl u8: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::u8::str(self) } -} -impl u16: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::u16::str(self) } -} -impl u32: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::u32::str(self) } -} -impl u64: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::u64::str(self) } -} impl float: ToStr { #[inline(always)] pure fn to_str() -> ~str { ::float::to_str(self, 4u) } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 69d69537f2ad9..f02f187c629a0 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -98,8 +98,8 @@ pub fn enc_ty(w: io::Writer, cx: @ctxt, t: ty::t) { let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len); if abbrev_len < len { // I.e. it's actually an abbreviation. - let s = ~"#" + uint::to_str(pos, 16u) + ~":" + - uint::to_str(len, 16u) + ~"#"; + let s = ~"#" + uint::to_str_radix(pos, 16u) + ~":" + + uint::to_str_radix(len, 16u) + ~"#"; let a = {pos: pos, len: len, s: @s}; abbrevs.insert(t, a); } diff --git a/src/librustc/middle/typeck/infer/to_str.rs b/src/librustc/middle/typeck/infer/to_str.rs index e100392bba9ad..57e05926173d1 100644 --- a/src/librustc/middle/typeck/infer/to_str.rs +++ b/src/librustc/middle/typeck/infer/to_str.rs @@ -88,7 +88,7 @@ pub impl VarValue : InferStr { match *self { Redirect(ref vid) => fmt!("Redirect(%s)", vid.to_str()), Root(ref pt, rk) => fmt!("Root(%s, %s)", pt.inf_str(cx), - uint::to_str(rk, 10u)) + uint::to_str_radix(rk, 10u)) } } } diff --git a/src/libstd/bigint.rs b/src/libstd/bigint.rs index 0104e72764f69..64126ea918fc0 100644 --- a/src/libstd/bigint.rs +++ b/src/libstd/bigint.rs @@ -456,7 +456,7 @@ pub impl BigUint { pure fn fill_concat(v: &[BigDigit], radix: uint, l: uint) -> ~str { if v.is_empty() { return ~"0" } str::trim_left_chars(str::concat(vec::reversed(v).map(|n| { - let s = uint::to_str(*n as uint, radix); + let s = uint::to_str_radix(*n as uint, radix); str::from_chars(vec::from_elem(l - s.len(), '0')) + s })), ['0']) } diff --git a/src/libstd/md4.rs b/src/libstd/md4.rs index 06b6aca6895d6..e1e2bb2697f8c 100644 --- a/src/libstd/md4.rs +++ b/src/libstd/md4.rs @@ -109,7 +109,7 @@ pub pure fn md4_str(msg: &[u8]) -> ~str { while i < 4u32 { let byte = (u >> (i * 8u32)) as u8; if byte <= 16u8 { result += ~"0"; } - result += uint::to_str(byte as uint, 16u); + result += uint::to_str_radix(byte as uint, 16u); i += 1u32; } } diff --git a/src/libstd/sha1.rs b/src/libstd/sha1.rs index 608d071d90e5c..a5c740c343cfc 100644 --- a/src/libstd/sha1.rs +++ b/src/libstd/sha1.rs @@ -253,7 +253,7 @@ pub fn sha1() -> Sha1 { let rr = mk_result(&self); let mut s = ~""; for vec::each(rr) |b| { - s += uint::to_str(*b as uint, 16u); + s += uint::to_str_radix(*b as uint, 16u); } return s; } diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index 71e96699c3d54..381183e736cf7 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -417,7 +417,7 @@ fn scan_number(c: char, rdr: string_reader) -> token::Token { if str::len(num_str) == 0u { rdr.fatal(~"no valid digits found for number"); } - let parsed = u64::from_str_radix(num_str, base as u64).get(); + let parsed = u64::from_str_radix(num_str, base as uint).get(); match tp { either::Left(t) => return token::LIT_INT(parsed as i64, t), either::Right(t) => return token::LIT_UINT(parsed, t) @@ -471,7 +471,7 @@ fn scan_number(c: char, rdr: string_reader) -> token::Token { if str::len(num_str) == 0u { rdr.fatal(~"no valid digits found for number"); } - let parsed = u64::from_str_radix(num_str, base as u64).get(); + let parsed = u64::from_str_radix(num_str, base as uint).get(); debug!("lexing %s as an unsuffixed integer literal", num_str); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 527b036a46c01..5079766239b00 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2006,24 +2006,24 @@ pub fn print_literal(s: ps, &&lit: @ast::lit) { ast::lit_int(i, t) => { if i < 0_i64 { word(s.s, - ~"-" + u64::to_str(-i as u64, 10u) + ~"-" + u64::to_str_radix(-i as u64, 10u) + ast_util::int_ty_to_str(t)); } else { word(s.s, - u64::to_str(i as u64, 10u) + u64::to_str_radix(i as u64, 10u) + ast_util::int_ty_to_str(t)); } } ast::lit_uint(u, t) => { word(s.s, - u64::to_str(u, 10u) + u64::to_str_radix(u, 10u) + ast_util::uint_ty_to_str(t)); } ast::lit_int_unsuffixed(i) => { if i < 0_i64 { - word(s.s, ~"-" + u64::to_str(-i as u64, 10u)); + word(s.s, ~"-" + u64::to_str_radix(-i as u64, 10u)); } else { - word(s.s, u64::to_str(i as u64, 10u)); + word(s.s, u64::to_str_radix(i as u64, 10u)); } } ast::lit_float(f, t) => { From 3bca159fb26b701d67aac291052f3815080098cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 27 Jan 2013 03:21:37 +0100 Subject: [PATCH 09/14] Fixed tests still using old integer to_str Fixed integer tests --- src/libcore/num/int-template.rs | 27 +++++++++++++++++++-------- src/libcore/num/uint-template.rs | 17 ++++++++++++++--- src/libstd/map.rs | 29 ++++++++++++++++------------- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index 6263a9d887bb2..06d11e23967e8 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -26,6 +26,9 @@ use prelude::*; use str; use uint; use vec; +use i8; +use i16; +use i32; pub const bits : uint = inst::bits; pub const bytes : uint = (inst::bits / 8); @@ -301,13 +304,13 @@ fn test_from_str() { assert from_str(~"0") == Some(0 as T); assert from_str(~"3") == Some(3 as T); assert from_str(~"10") == Some(10 as T); - assert from_str(~"123456789") == Some(123456789 as T); + assert i32::from_str(~"123456789") == Some(123456789 as i32); assert from_str(~"00100") == Some(100 as T); assert from_str(~"-1") == Some(-1 as T); assert from_str(~"-3") == Some(-3 as T); assert from_str(~"-10") == Some(-10 as T); - assert from_str(~"-123456789") == Some(-123456789 as T); + assert i32::from_str(~"-123456789") == Some(-123456789 as i32); assert from_str(~"-00100") == Some(-100 as T); assert from_str(~" ").is_none(); @@ -320,18 +323,18 @@ fn test_parse_bytes() { assert parse_bytes(to_bytes(~"123"), 10u) == Some(123 as T); assert parse_bytes(to_bytes(~"1001"), 2u) == Some(9 as T); assert parse_bytes(to_bytes(~"123"), 8u) == Some(83 as T); - assert parse_bytes(to_bytes(~"123"), 16u) == Some(291 as T); - assert parse_bytes(to_bytes(~"ffff"), 16u) == Some(65535 as T); - assert parse_bytes(to_bytes(~"FFFF"), 16u) == Some(65535 as T); + assert i32::parse_bytes(to_bytes(~"123"), 16u) == Some(291 as i32); + assert i32::parse_bytes(to_bytes(~"ffff"), 16u) == Some(65535 as i32); + assert i32::parse_bytes(to_bytes(~"FFFF"), 16u) == Some(65535 as i32); assert parse_bytes(to_bytes(~"z"), 36u) == Some(35 as T); assert parse_bytes(to_bytes(~"Z"), 36u) == Some(35 as T); assert parse_bytes(to_bytes(~"-123"), 10u) == Some(-123 as T); assert parse_bytes(to_bytes(~"-1001"), 2u) == Some(-9 as T); assert parse_bytes(to_bytes(~"-123"), 8u) == Some(-83 as T); - assert parse_bytes(to_bytes(~"-123"), 16u) == Some(-291 as T); - assert parse_bytes(to_bytes(~"-ffff"), 16u) == Some(-65535 as T); - assert parse_bytes(to_bytes(~"-FFFF"), 16u) == Some(-65535 as T); + assert i32::parse_bytes(to_bytes(~"-123"), 16u) == Some(-291 as i32); + assert i32::parse_bytes(to_bytes(~"-ffff"), 16u) == Some(-65535 as i32); + assert i32::parse_bytes(to_bytes(~"-FFFF"), 16u) == Some(-65535 as i32); assert parse_bytes(to_bytes(~"-z"), 36u) == Some(-35 as T); assert parse_bytes(to_bytes(~"-Z"), 36u) == Some(-35 as T); @@ -380,27 +383,35 @@ fn test_int_to_str_overflow() { fn test_int_from_str_overflow() { let mut i8_val: i8 = 127_i8; assert (i8::from_str(~"127") == Some(i8_val)); + assert (i8::from_str(~"128").is_none()); i8_val += 1 as i8; assert (i8::from_str(~"-128") == Some(i8_val)); + assert (i8::from_str(~"-129").is_none()); let mut i16_val: i16 = 32_767_i16; assert (i16::from_str(~"32767") == Some(i16_val)); + assert (i16::from_str(~"32768").is_none()); i16_val += 1 as i16; assert (i16::from_str(~"-32768") == Some(i16_val)); + assert (i16::from_str(~"-32769").is_none()); let mut i32_val: i32 = 2_147_483_647_i32; assert (i32::from_str(~"2147483647") == Some(i32_val)); + assert (i32::from_str(~"2147483648").is_none()); i32_val += 1 as i32; assert (i32::from_str(~"-2147483648") == Some(i32_val)); + assert (i32::from_str(~"-2147483649").is_none()); let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; assert (i64::from_str(~"9223372036854775807") == Some(i64_val)); + assert (i64::from_str(~"9223372036854775808").is_none()); i64_val += 1 as i64; assert (i64::from_str(~"-9223372036854775808") == Some(i64_val)); + assert (i64::from_str(~"-9223372036854775809").is_none()); } #[test] diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs index 0c39186824a72..44ed9816cf94a 100644 --- a/src/libcore/num/uint-template.rs +++ b/src/libcore/num/uint-template.rs @@ -27,6 +27,9 @@ use prelude::*; use str; use uint; use vec; +use u8; +use u16; +use u32; pub const bits : uint = inst::bits; pub const bytes : uint = (inst::bits / 8); @@ -274,7 +277,7 @@ pub fn test_from_str() { assert from_str(~"0") == Some(0u as T); assert from_str(~"3") == Some(3u as T); assert from_str(~"10") == Some(10u as T); - assert from_str(~"123456789") == Some(123456789u as T); + assert u32::from_str(~"123456789") == Some(123456789 as u32); assert from_str(~"00100") == Some(100u as T); assert from_str(~"").is_none(); @@ -288,8 +291,8 @@ pub fn test_parse_bytes() { assert parse_bytes(to_bytes(~"123"), 10u) == Some(123u as T); assert parse_bytes(to_bytes(~"1001"), 2u) == Some(9u as T); assert parse_bytes(to_bytes(~"123"), 8u) == Some(83u as T); - assert parse_bytes(to_bytes(~"123"), 16u) == Some(291u as T); - assert parse_bytes(to_bytes(~"ffff"), 16u) == Some(65535u as T); + assert u16::parse_bytes(to_bytes(~"123"), 16u) == Some(291u as u16); + assert u16::parse_bytes(to_bytes(~"ffff"), 16u) == Some(65535u as u16); assert parse_bytes(to_bytes(~"z"), 36u) == Some(35u as T); assert parse_bytes(to_bytes(~"Z"), 10u).is_none(); @@ -327,27 +330,35 @@ fn test_uint_to_str_overflow() { fn test_uint_from_str_overflow() { let mut u8_val: u8 = 255_u8; assert (u8::from_str(~"255") == Some(u8_val)); + assert (u8::from_str(~"256").is_none()); u8_val += 1 as u8; assert (u8::from_str(~"0") == Some(u8_val)); + assert (u8::from_str(~"-1").is_none()); let mut u16_val: u16 = 65_535_u16; assert (u16::from_str(~"65535") == Some(u16_val)); + assert (u16::from_str(~"65536").is_none()); u16_val += 1 as u16; assert (u16::from_str(~"0") == Some(u16_val)); + assert (u16::from_str(~"-1").is_none()); let mut u32_val: u32 = 4_294_967_295_u32; assert (u32::from_str(~"4294967295") == Some(u32_val)); + assert (u32::from_str(~"4294967296").is_none()); u32_val += 1 as u32; assert (u32::from_str(~"0") == Some(u32_val)); + assert (u32::from_str(~"-1").is_none()); let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; assert (u64::from_str(~"18446744073709551615") == Some(u64_val)); + assert (u64::from_str(~"18446744073709551616").is_none()); u64_val += 1 as u64; assert (u64::from_str(~"0") == Some(u64_val)); + assert (u64::from_str(~"-1").is_none()); } #[test] diff --git a/src/libstd/map.rs b/src/libstd/map.rs index 380e23b23a5e9..d557a5a06da1a 100644 --- a/src/libstd/map.rs +++ b/src/libstd/map.rs @@ -619,32 +619,35 @@ mod tests { map::HashMap::<~str, ~str>(); i = 0u; while i < num_to_insert { - assert hm_ss.insert(uint::to_str(i, 2u), uint::to_str(i * i, 2u)); + assert hm_ss.insert(uint::to_str_radix(i, 2u), + uint::to_str_radix(i * i, 2u)); debug!("inserting \"%s\" -> \"%s\"", - uint::to_str(i, 2u), - uint::to_str(i*i, 2u)); + uint::to_str_radix(i, 2u), + uint::to_str_radix(i*i, 2u)); i += 1u; } debug!("-----"); i = 0u; while i < num_to_insert { debug!("get(\"%s\") = \"%s\"", - uint::to_str(i, 2u), - hm_ss.get(uint::to_str(i, 2u))); - assert hm_ss.get(uint::to_str(i, 2u)) == uint::to_str(i * i, 2u); + uint::to_str_radix(i, 2u), + hm_ss.get(uint::to_str_radix(i, 2u))); + assert hm_ss.get(uint::to_str_radix(i, 2u)) == + uint::to_str_radix(i * i, 2u); i += 1u; } - assert (hm_ss.insert(uint::to_str(num_to_insert, 2u), - uint::to_str(17u, 2u))); - assert hm_ss.get(uint::to_str(num_to_insert, 2u)) == - uint::to_str(17u, 2u); + assert (hm_ss.insert(uint::to_str_radix(num_to_insert, 2u), + uint::to_str_radix(17u, 2u))); + assert hm_ss.get(uint::to_str_radix(num_to_insert, 2u)) == + uint::to_str_radix(17u, 2u); debug!("-----"); i = 0u; while i < num_to_insert { debug!("get(\"%s\") = \"%s\"", - uint::to_str(i, 2u), - hm_ss.get(uint::to_str(i, 2u))); - assert hm_ss.get(uint::to_str(i, 2u)) == uint::to_str(i * i, 2u); + uint::to_str_radix(i, 2u), + hm_ss.get(uint::to_str_radix(i, 2u))); + assert hm_ss.get(uint::to_str_radix(i, 2u)) == + uint::to_str_radix(i * i, 2u); i += 1u; } debug!("*** finished test_growth"); From 652d6bc59b91195bd81d3c90ade5668b1ed53fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 27 Jan 2013 03:28:39 +0100 Subject: [PATCH 10/14] Converted the floating point types to the new string conversion functions. Also fixed all conflicting calls of the old functions in the rest of the codebase. The set of string conversion functions for each float type now consists of those items: - to_str(), converts to number in base 10 - to_str_hex(), converts to number in base 16 - to_str_radix(), converts to number in given radix - to_str_exact(), converts to number in base 10 with a exact number of trailing digits - to_str_digits(), converts to number in base 10 with a maximum number of trailing digits - implementations for to_str::ToStr and num::ToStrRadix - from_str(), parses a string as number in base 10 including decimal exponent and special values - from_str_hex(), parses a string as a number in base 16 including binary exponent and special values - from_str_radix(), parses a string as a number in a given base excluding any exponent and special values - implementations for from_str::FromStr and num::FromStrRadix --- src/libcore/extfmt.rs | 2 +- src/libcore/num/f32.rs | 194 ++++++++++++++++++ src/libcore/num/f64.rs | 194 ++++++++++++++++++ src/libcore/num/float.rs | 427 ++++++++++++++++++++------------------- src/libcore/repr.rs | 8 +- src/libcore/to_str.rs | 12 -- src/libstd/json.rs | 4 +- 7 files changed, 616 insertions(+), 225 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 4627c04801539..ed0625624d80a 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -563,7 +563,7 @@ pub mod rt { pub pure fn conv_float(cv: Conv, f: float) -> ~str { let (to_str, digits) = match cv.precision { CountIs(c) => (float::to_str_exact, c as uint), - CountImplied => (float::to_str, 6u) + CountImplied => (float::to_str_digits, 6u) }; let mut s = unsafe { to_str(f, digits) }; if 0.0 <= f { diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 43b68c5fc4f40..a33a46192fdfd 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -18,6 +18,9 @@ use cmath; use cmp; use libc::{c_float, c_int}; use num; +use option::Option; +use from_str; +use to_str; pub use cmath::c_float_targ_consts::*; @@ -333,6 +336,197 @@ impl f32: num::Round { } } +/** + * Section: String Conversions + */ + +/** + * Converts a float to a string + * + * # Arguments + * + * * num - The float value + */ +#[inline(always)] +pub pure fn to_str(num: f32) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigAll); + r +} + +/** + * Converts a float to a string in hexadecimal format + * + * # Arguments + * + * * num - The float value + */ +#[inline(always)] +pub pure fn to_str_hex(num: f32) -> ~str { + let (r, _) = num::to_str_common( + &num, 16u, true, true, num::SignNeg, num::DigAll); + r +} + +/** + * Converts a float to a string in a given radix + * + * # Arguments + * + * * num - The float value + * * radix - The base to use + */ +#[inline(always)] +pub pure fn to_str_radix(num: f32, rdx: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, rdx, true, true, num::SignNeg, num::DigAll); + r +} + +/** + * Converts a float to a string with exactly the number of + * provided significant digits + * + * # Arguments + * + * * num - The float value + * * digits - The number of significant digits + */ +#[inline(always)] +pub pure fn to_str_exact(num: f32, dig: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigExact(dig)); + r +} + +/** + * Converts a float to a string with a maximum number of + * significant digits + * + * # Arguments + * + * * num - The float value + * * digits - The number of significant digits + */ +#[inline(always)] +pub pure fn to_str_digits(num: f32, dig: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigMax(dig)); + r +} + +impl f32: to_str::ToStr { + #[inline(always)] + pure fn to_str() -> ~str { to_str_digits(self, 8) } +} + +impl f32: num::ToStrRadix { + #[inline(always)] + pure fn to_str_radix(&self, rdx: uint) -> ~str { + to_str_radix(*self, rdx) + } +} + +/** + * Convert a string in base 10 to a float. + * Accepts a optional decimal exponent. + * + * 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' + * * '.' (understood as 0) + * * '5.' + * * '.5', or, equivalently, '0.5' + * * '+inf', 'inf', '-inf', 'NaN' + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `num`. + */ +#[inline(always)] +pub pure fn from_str(num: &str) -> Option { + num::from_str_common(num, 10u, true, true, true, num::ExpDec, false) +} + +/** + * Convert a string in base 16 to a float. + * Accepts a optional binary exponent. + * + * This function accepts strings such as + * + * * 'a4.fe' + * * '+a4.fe', equivalent to 'a4.fe' + * * '-a4.fe' + * * '2b.aP128', or equivalently, '2b.ap128' + * * '2b.aP-128' + * * '.' (understood as 0) + * * 'c.' + * * '.c', or, equivalently, '0.c' + * * '+inf', 'inf', '-inf', 'NaN' + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `[num]`. + */ +#[inline(always)] +pub pure fn from_str_hex(num: &str) -> Option { + num::from_str_common(num, 16u, true, true, true, num::ExpBin, false) +} + +/** + * Convert a string in an given base to a float. + * + * Due to possible conflicts, this function does **not** accept + * the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** + * does it recognize exponents of any kind. + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * * radix - The base to use. Must lie in the range [2 .. 36] + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `num`. + */ +#[inline(always)] +pub pure fn from_str_radix(num: &str, rdx: uint) -> Option { + num::from_str_common(num, rdx, true, true, false, num::ExpNone, false) +} + +impl f32: from_str::FromStr { + #[inline(always)] + static pure fn from_str(val: &str) -> Option { from_str(val) } +} + +impl f32: num::FromStrRadix { + #[inline(always)] + static pure fn from_str_radix(val: &str, rdx: uint) -> Option { + from_str_radix(val, rdx) + } +} + // // Local Variables: // mode: rust diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 851697012fc61..276aa13da71f8 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -19,6 +19,9 @@ use cmp; use libc::{c_double, c_int}; use libc; use num; +use option::Option; +use to_str; +use from_str; pub use cmath::c_double_targ_consts::*; @@ -357,6 +360,197 @@ impl f64: num::Round { } } +/** + * Section: String Conversions + */ + +/** + * Converts a float to a string + * + * # Arguments + * + * * num - The float value + */ +#[inline(always)] +pub pure fn to_str(num: f64) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigAll); + r +} + +/** + * Converts a float to a string in hexadecimal format + * + * # Arguments + * + * * num - The float value + */ +#[inline(always)] +pub pure fn to_str_hex(num: f64) -> ~str { + let (r, _) = num::to_str_common( + &num, 16u, true, true, num::SignNeg, num::DigAll); + r +} + +/** + * Converts a float to a string in a given radix + * + * # Arguments + * + * * num - The float value + * * radix - The base to use + */ +#[inline(always)] +pub pure fn to_str_radix(num: f64, rdx: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, rdx, true, true, num::SignNeg, num::DigAll); + r +} + +/** + * Converts a float to a string with exactly the number of + * provided significant digits + * + * # Arguments + * + * * num - The float value + * * digits - The number of significant digits + */ +#[inline(always)] +pub pure fn to_str_exact(num: f64, dig: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigExact(dig)); + r +} + +/** + * Converts a float to a string with a maximum number of + * significant digits + * + * # Arguments + * + * * num - The float value + * * digits - The number of significant digits + */ +#[inline(always)] +pub pure fn to_str_digits(num: f64, dig: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigMax(dig)); + r +} + +impl f64: to_str::ToStr { + #[inline(always)] + pure fn to_str() -> ~str { to_str_digits(self, 8) } +} + +impl f64: num::ToStrRadix { + #[inline(always)] + pure fn to_str_radix(&self, rdx: uint) -> ~str { + to_str_radix(*self, rdx) + } +} + +/** + * Convert a string in base 10 to a float. + * Accepts a optional decimal exponent. + * + * 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' + * * '.' (understood as 0) + * * '5.' + * * '.5', or, equivalently, '0.5' + * * '+inf', 'inf', '-inf', 'NaN' + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `num`. + */ +#[inline(always)] +pub pure fn from_str(num: &str) -> Option { + num::from_str_common(num, 10u, true, true, true, num::ExpDec, false) +} + +/** + * Convert a string in base 16 to a float. + * Accepts a optional binary exponent. + * + * This function accepts strings such as + * + * * 'a4.fe' + * * '+a4.fe', equivalent to 'a4.fe' + * * '-a4.fe' + * * '2b.aP128', or equivalently, '2b.ap128' + * * '2b.aP-128' + * * '.' (understood as 0) + * * 'c.' + * * '.c', or, equivalently, '0.c' + * * '+inf', 'inf', '-inf', 'NaN' + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `[num]`. + */ +#[inline(always)] +pub pure fn from_str_hex(num: &str) -> Option { + num::from_str_common(num, 16u, true, true, true, num::ExpBin, false) +} + +/** + * Convert a string in an given base to a float. + * + * Due to possible conflicts, this function does **not** accept + * the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** + * does it recognize exponents of any kind. + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * * radix - The base to use. Must lie in the range [2 .. 36] + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `num`. + */ +#[inline(always)] +pub pure fn from_str_radix(num: &str, rdx: uint) -> Option { + num::from_str_common(num, rdx, true, true, false, num::ExpNone, false) +} + +impl f64: from_str::FromStr { + #[inline(always)] + static pure fn from_str(val: &str) -> Option { from_str(val) } +} + +impl f64: num::FromStrRadix { + #[inline(always)] + static pure fn from_str_radix(val: &str, rdx: uint) -> Option { + from_str_radix(val, rdx) + } +} + // // Local Variables: // mode: rust diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs index 1d1be6df87aba..4f683ed14f76f 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -34,6 +34,8 @@ use num::Num::from_int; use option::{None, Option, Some}; use str; use uint; +use to_str; +use from_str; pub use f64::{add, sub, mul, div, rem, lt, le, eq, ne, ge, gt}; pub use f64::logarithm; @@ -95,7 +97,7 @@ pub mod consts { pub const ln_10: float = 2.30258509299404568401799145468436421; } -/** +/* * Section: String Conversions */ @@ -105,85 +107,41 @@ pub mod consts { * # Arguments * * * num - The float value - * * digits - The number of significant digits - * * exact - Whether to enforce the exact number of significant digits */ -pub pure fn to_str_common(num: float, digits: uint, exact: bool) -> ~str { - if is_NaN(num) { return ~"NaN"; } - if num == infinity { return ~"inf"; } - if num == neg_infinity { return ~"-inf"; } - - 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: (#2608) - // 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 { return 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 > 0 && (frac >= epsilon_prime || exact) { - // store the next digit - frac *= 10.0; - let digit = frac as uint; - // Bleh: not really unsafe. - unsafe { fractionalParts.push(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 >= 5 { 1 } else { 0 }; - - // turn digits into string - // using stack of digits - while !fractionalParts.is_empty() { - // Bleh; shouldn't need to be unsafe - let mut adjusted_digit = carry + unsafe { fractionalParts.pop() }; - - if adjusted_digit == 10 { - carry = 1; - adjusted_digit %= 10 - } else { - carry = 0; - }; - - racc = uint::str(adjusted_digit) + racc; - } +#[inline(always)] +pub pure fn to_str(num: float) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigAll); + r +} - // pad decimals with trailing zeroes - while racc.len() < digits && exact { - racc += ~"0" - } +/** + * Converts a float to a string in hexadecimal format + * + * # Arguments + * + * * num - The float value + */ +#[inline(always)] +pub pure fn to_str_hex(num: float) -> ~str { + let (r, _) = num::to_str_common( + &num, 16u, true, true, num::SignNeg, num::DigAll); + r +} - // combine ints and decimals - let mut ones = uint::str(trunc + carry); - if racc == ~"" { - acc = sign + ones; - } else { - acc = sign + ones + ~"." + racc; - } - move acc +/** + * Converts a float to a string in a given radix + * + * # Arguments + * + * * num - The float value + * * radix - The base to use + */ +#[inline(always)] +pub pure fn to_str_radix(num: float, radix: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, radix, true, true, num::SignNeg, num::DigAll); + r } /** @@ -197,7 +155,9 @@ pub pure fn to_str_common(num: float, digits: uint, exact: bool) -> ~str { */ #[inline(always)] pub pure fn to_str_exact(num: float, digits: uint) -> ~str { - to_str_common(num, digits, true) + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigExact(digits)); + r } #[test] @@ -206,7 +166,6 @@ pub fn test_to_str_exact_do_decimal() { assert s == ~"5.0000"; } - /** * Converts a float to a string with a maximum number of * significant digits @@ -217,12 +176,27 @@ pub fn test_to_str_exact_do_decimal() { * * digits - The number of significant digits */ #[inline(always)] -pub pure fn to_str(num: float, digits: uint) -> ~str { - to_str_common(num, digits, false) +pub pure fn to_str_digits(num: float, digits: uint) -> ~str { + let (r, _) = num::to_str_common( + &num, 10u, true, true, num::SignNeg, num::DigMax(digits)); + r +} + +impl float: to_str::ToStr { + #[inline(always)] + pure fn to_str() -> ~str { to_str_digits(self, 8) } +} + +impl float: num::ToStrRadix { + #[inline(always)] + pure fn to_str_radix(&self, radix: uint) -> ~str { + to_str_radix(*self, radix) + } } /** - * Convert a string to a float + * Convert a string in base 10 to a float. + * Accepts a optional decimal exponent. * * This function accepts strings such as * @@ -231,12 +205,12 @@ pub pure fn to_str(num: float, digits: uint) -> ~str { * * '-3.14' * * '2.5E10', or equivalently, '2.5e10' * * '2.5E-10' - * * '', or, equivalently, '.' (understood as 0) + * * '.' (understood as 0) * * '5.' * * '.5', or, equivalently, '0.5' - * * 'inf', '-inf', 'NaN' + * * '+inf', 'inf', '-inf', 'NaN' * - * Leading and trailing whitespace are ignored. + * Leading and trailing whitespace represent an error. * * # Arguments * @@ -245,125 +219,79 @@ pub pure fn to_str(num: float, digits: uint) -> ~str { * # Return value * * `none` if the string did not represent a valid number. Otherwise, - * `Some(n)` where `n` is the floating-point number represented by `[num]`. + * `Some(n)` where `n` is the floating-point number represented by `num`. */ +#[inline(always)] pub pure fn from_str(num: &str) -> Option { - if num == "inf" { - return Some(infinity as float); - } else if num == "-inf" { - return Some(neg_infinity as float); - } else if num == "NaN" { - return Some(NaN as float); - } - - let mut pos = 0u; //Current byte position in the string. - //Used to walk the string in O(n). - let len = str::len(num); //Length of the string, in bytes. - - if len == 0u { return None; } - let mut total = 0f; //Accumulated result - let mut c = 'z'; //Latest char. - - //The string must start with one of the following characters. - match str::char_at(num, 0u) { - '-' | '+' | '0' .. '9' | '.' => (), - _ => return None - } - - //Determine if first char is '-'/'+'. Set [pos] and [neg] accordingly. - let mut neg = false; //Sign of the result - match str::char_at(num, 0u) { - '-' => { - neg = true; - pos = 1u; - } - '+' => { - pos = 1u; - } - _ => () - } + num::from_str_common(num, 10u, true, true, true, num::ExpDec, false) +} - //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; - match c { - '0' .. '9' => { - total = total * 10f; - total += ((c as int) - ('0' as int)) as float; - } - '.' | 'e' | 'E' => break, - _ => return None - } - } +/** + * Convert a string in base 16 to a float. + * Accepts a optional binary exponent. + * + * This function accepts strings such as + * + * * 'a4.fe' + * * '+a4.fe', equivalent to 'a4.fe' + * * '-a4.fe' + * * '2b.aP128', or equivalently, '2b.ap128' + * * '2b.aP-128' + * * '.' (understood as 0) + * * 'c.' + * * '.c', or, equivalently, '0.c' + * * '+inf', 'inf', '-inf', 'NaN' + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `[num]`. + */ +#[inline(always)] +pub pure fn from_str_hex(num: &str) -> Option { + num::from_str_common(num, 16u, true, true, true, num::ExpBin, false) +} - if c == '.' {//Examine decimal part - let mut decimal = 1f; - while(pos < len) { - let char_range = str::char_range_at(num, pos); - c = char_range.ch; - pos = char_range.next; - match c { - '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' => { - decimal /= 10f; - total += (((c as int) - ('0' as int)) as float)*decimal; - } - 'e' | 'E' => break, - _ => return None - } - } - } +/** + * Convert a string in an given base to a float. + * + * Due to possible conflicts, this function does **not** accept + * the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** + * does it recognize exponents of any kind. + * + * Leading and trailing whitespace represent an error. + * + * # Arguments + * + * * num - A string + * * radix - The base to use. Must lie in the range [2 .. 36] + * + * # Return value + * + * `none` if the string did not represent a valid number. Otherwise, + * `Some(n)` where `n` is the floating-point number represented by `num`. + */ +#[inline(always)] +pub pure fn from_str_radix(num: &str, radix: uint) -> Option { + num::from_str_common(num, radix, true, true, false, num::ExpNone, false) +} - if (c == 'e') || (c == 'E') { //Examine exponent - let mut exponent = 0u; - let mut neg_exponent = false; - if(pos < len) { - let char_range = str::char_range_at(num, pos); - c = char_range.ch; - match 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; - match c { - '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' => { - exponent *= 10u; - exponent += ((c as uint) - ('0' as uint)); - } - _ => break - } - pos = char_range.next; - } - let multiplier = pow_with_uint(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; - } - } else { - return None; - } - } +impl float: from_str::FromStr { + #[inline(always)] + static pure fn from_str(val: &str) -> Option { from_str(val) } +} - if(pos < len) { - return None; - } else { - if(neg) { - total *= -1f; - } - return Some(total); - } +impl float: num::FromStrRadix { + #[inline(always)] + static pure fn from_str_radix(val: &str, radix: uint) -> Option { + from_str_radix(val, radix) + } } /** @@ -523,7 +451,6 @@ impl float: num::Round { #[test] pub fn test_from_str() { - assert from_str(~"3") == Some(3.); assert from_str(~"3") == Some(3.); assert from_str(~"3.14") == Some(3.14); assert from_str(~"+3.14") == Some(3.14); @@ -537,20 +464,25 @@ pub fn test_from_str() { assert from_str(~"5.") == Some(5.); assert from_str(~".5") == Some(0.5); assert from_str(~"0.5") == Some(0.5); - assert from_str(~"0.5") == Some(0.5); - assert from_str(~"0.5") == Some(0.5); - assert from_str(~"-.5") == Some(-0.5); assert from_str(~"-.5") == Some(-0.5); assert from_str(~"-5") == Some(-5.); - assert from_str(~"-0") == Some(-0.); - assert from_str(~"0") == Some(0.); assert from_str(~"inf") == Some(infinity); + assert from_str(~"+inf") == Some(infinity); assert from_str(~"-inf") == Some(neg_infinity); // note: NaN != NaN, hence this slightly complex test match from_str(~"NaN") { Some(f) => assert is_NaN(f), None => die!() } + // note: -0 == 0, hence these slightly more complex tests + match from_str(~"-0") { + Some(v) if is_zero(v) => assert is_negative(v), + _ => fail + } + match from_str(~"0") { + Some(v) if is_zero(v) => assert is_positive(v), + _ => fail + } assert from_str(~"").is_none(); assert from_str(~"x").is_none(); @@ -564,6 +496,89 @@ pub fn test_from_str() { assert from_str(~"1e1-1").is_none(); } +#[test] +pub fn test_from_str_hex() { + assert from_str_hex(~"a4") == Some(164.); + assert from_str_hex(~"a4.fe") == Some(164.9921875); + assert from_str_hex(~"-a4.fe") == Some(-164.9921875); + assert from_str_hex(~"+a4.fe") == Some(164.9921875); + assert from_str_hex(~"ff0P4") == Some(0xff00 as float); + assert from_str_hex(~"ff0p4") == Some(0xff00 as float); + assert from_str_hex(~"ff0p-4") == Some(0xff as float); + assert from_str_hex(~".") == Some(0.); + assert from_str_hex(~".p1") == Some(0.); + assert from_str_hex(~".p-1") == Some(0.); + assert from_str_hex(~"f.") == Some(15.); + assert from_str_hex(~".f") == Some(0.9375); + assert from_str_hex(~"0.f") == Some(0.9375); + assert from_str_hex(~"-.f") == Some(-0.9375); + assert from_str_hex(~"-f") == Some(-15.); + assert from_str_hex(~"inf") == Some(infinity); + assert from_str_hex(~"+inf") == Some(infinity); + assert from_str_hex(~"-inf") == Some(neg_infinity); + // note: NaN != NaN, hence this slightly complex test + match from_str_hex(~"NaN") { + Some(f) => assert is_NaN(f), + None => fail + } + // note: -0 == 0, hence these slightly more complex tests + match from_str_hex(~"-0") { + Some(v) if is_zero(v) => assert is_negative(v), + _ => fail + } + match from_str_hex(~"0") { + Some(v) if is_zero(v) => assert is_positive(v), + _ => fail + } + assert from_str_hex(~"e") == Some(14.); + assert from_str_hex(~"E") == Some(14.); + assert from_str_hex(~"E1") == Some(225.); + assert from_str_hex(~"1e1e1") == Some(123361.); + assert from_str_hex(~"1e1.1") == Some(481.0625); + + assert from_str_hex(~"").is_none(); + assert from_str_hex(~"x").is_none(); + assert from_str_hex(~" ").is_none(); + assert from_str_hex(~" ").is_none(); + assert from_str_hex(~"p").is_none(); + assert from_str_hex(~"P").is_none(); + assert from_str_hex(~"P1").is_none(); + assert from_str_hex(~"1p1p1").is_none(); + assert from_str_hex(~"1p1.1").is_none(); + assert from_str_hex(~"1p1-1").is_none(); +} + +#[test] +pub fn test_to_str_hex() { + assert to_str_hex(164.) == ~"a4"; + assert to_str_hex(164.9921875) == ~"a4.fe"; + assert to_str_hex(-164.9921875) == ~"-a4.fe"; + assert to_str_hex(0xff00 as float) == ~"ff00"; + assert to_str_hex(-(0xff00 as float)) == ~"-ff00"; + assert to_str_hex(0.) == ~"0"; + assert to_str_hex(15.) == ~"f"; + assert to_str_hex(-15.) == ~"-f"; + assert to_str_hex(0.9375) == ~"0.f"; + assert to_str_hex(-0.9375) == ~"-0.f"; + assert to_str_hex(infinity) == ~"inf"; + assert to_str_hex(neg_infinity) == ~"-inf"; + assert to_str_hex(NaN) == ~"NaN"; + assert to_str_hex(0.) == ~"0"; + assert to_str_hex(-0.) == ~"-0"; +} + +#[test] +pub fn test_to_str_radix() { + assert to_str_radix(36., 36u) == ~"10"; + assert to_str_radix(8.125, 2u) == ~"1000.001"; +} + +#[test] +pub fn test_from_str_radix() { + assert from_str_radix(~"10", 36u) == Some(36.); + assert from_str_radix(~"1000.001", 2u) == Some(8.125); +} + #[test] pub fn test_positive() { assert(is_positive(infinity)); @@ -610,8 +625,8 @@ pub fn test_nonnegative() { #[test] pub fn test_to_str_inf() { - assert to_str(infinity, 10u) == ~"inf"; - assert to_str(-infinity, 10u) == ~"-inf"; + assert to_str_digits(infinity, 10u) == ~"inf"; + assert to_str_digits(-infinity, 10u) == ~"-inf"; } #[test] diff --git a/src/libcore/repr.rs b/src/libcore/repr.rs index a5c3afcc87f5e..2f77e71bef005 100644 --- a/src/libcore/repr.rs +++ b/src/libcore/repr.rs @@ -893,7 +893,7 @@ fn test_repr() { exact_test(&10, "10"); exact_test(&true, "true"); exact_test(&false, "false"); - exact_test(&1.234, "1.2340"); + exact_test(&1.234, "1.234"); exact_test(&(&"hello"), "\"hello\""); exact_test(&(@"hello"), "@\"hello\""); exact_test(&(~"he\u10f3llo"), "~\"he\\u10f3llo\""); @@ -918,11 +918,11 @@ fn test_repr() { exact_test(&(&["hi", "there"]), "&[\"hi\", \"there\"]"); exact_test(&(P{a:10, b:1.234}), - "{a: 10, b: 1.2340}"); + "{a: 10, b: 1.234}"); exact_test(&(@P{a:10, b:1.234}), - "@{a: 10, b: 1.2340}"); + "@{a: 10, b: 1.234}"); exact_test(&(~P{a:10, b:1.234}), - "~{a: 10, b: 1.2340}"); + "~{a: 10, b: 1.234}"); exact_test(&(10_u8, ~"hello"), "(10, ~\"hello\")"); exact_test(&(10_u16, ~"hello"), diff --git a/src/libcore/to_str.rs b/src/libcore/to_str.rs index 13b09590b4480..51205b0c647fd 100644 --- a/src/libcore/to_str.rs +++ b/src/libcore/to_str.rs @@ -24,18 +24,6 @@ use vec; pub trait ToStr { pub pure fn to_str() -> ~str; } -impl float: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::float::to_str(self, 4u) } -} -impl f32: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::float::to_str(self as float, 4u) } -} -impl f64: ToStr { - #[inline(always)] - pure fn to_str() -> ~str { ::float::to_str(self as float, 4u) } -} impl bool: ToStr { #[inline(always)] pure fn to_str() -> ~str { ::bool::to_str(self) } diff --git a/src/libstd/json.rs b/src/libstd/json.rs index f0929c3dba047..b1cdeef4e6650 100644 --- a/src/libstd/json.rs +++ b/src/libstd/json.rs @@ -109,7 +109,7 @@ pub impl Encoder: serialize::Encoder { fn emit_f64(&self, v: f64) { self.emit_float(v as float); } fn emit_f32(&self, v: f32) { self.emit_float(v as float); } fn emit_float(&self, v: float) { - self.wr.write_str(float::to_str(v, 6u)); + self.wr.write_str(float::to_str_digits(v, 6u)); } fn emit_char(&self, v: char) { self.emit_borrowed_str(str::from_char(v)) } @@ -213,7 +213,7 @@ pub impl PrettyEncoder: serialize::Encoder { fn emit_f64(&self, v: f64) { self.emit_float(v as float); } fn emit_f32(&self, v: f32) { self.emit_float(v as float); } fn emit_float(&self, v: float) { - self.wr.write_str(float::to_str(v, 6u)); + self.wr.write_str(float::to_str_digits(v, 6u)); } fn emit_char(&self, v: char) { self.emit_borrowed_str(str::from_char(v)) } From df9ddc6725b74b68e375855652996cb695edde93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Mon, 28 Jan 2013 13:16:25 +0100 Subject: [PATCH 11/14] Removed wrong/wip doc --- src/libcore/num/num.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index b466191352fd3..9a138de3cf35d 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -215,10 +215,6 @@ pub enum SignFormat { * with a 'divide by zero'. Likewise, it will fail if `num` **is** one of * those special values, and `special` is `false`, because then the * algorithm just does normal calculations on them. - * - * # Possible improvements - * - Currently performs no rounding if truncating trailing digits. - * - Make function handle numbers with expensive copies better. */ pub pure fn to_str_bytes_common( num: &T, radix: uint, special: bool, negative_zero: bool, From cf4a9aeed94a12ae70fef8c4de74f911dce6efd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sat, 2 Feb 2013 16:40:42 +0100 Subject: [PATCH 12/14] Fixed `fmt!`, tests, doc-tests. --- doc/rust.md | 8 +++--- doc/tutorial-tasks.md | 4 +-- src/libcore/num/num.rs | 8 +++++- src/libstd/time.rs | 2 +- src/test/bench/core-map.rs | 30 +++++++++++------------ src/test/bench/core-uint-to-str.rs | 2 +- src/test/run-pass/core-export-f64-sqrt.rs | 2 +- 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/doc/rust.md b/doc/rust.md index 7d4ec54aff5ce..34fd7cf25d60d 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -844,7 +844,7 @@ mod quux { pub fn bar() { } pub fn baz() { } } - + pub use quux::foo::*; } ~~~~ @@ -1242,7 +1242,7 @@ trait Num { impl float: Num { static pure fn from_int(n: int) -> float { n as float } } -let x: float = Num::from_int(42); +let x: float = Num::from_int(42); ~~~~ Traits may inherit from other traits. For example, in @@ -1615,7 +1615,7 @@ The following are examples of structure expressions: ~~~~ # struct Point { x: float, y: float } # struct TuplePoint(float, float); -# mod game { pub struct User { name: &str, age: uint, mut score: uint } } +# mod game { pub struct User { name: &str, age: uint, mut score: uint } } # use game; Point {x: 10f, y: 20f}; TuplePoint(10f, 20f); @@ -2812,7 +2812,7 @@ trait Printable { } impl int: Printable { - fn to_str() -> ~str { int::to_str(self, 10) } + fn to_str() -> ~str { int::to_str(self) } } fn print(a: @Printable) { diff --git a/doc/tutorial-tasks.md b/doc/tutorial-tasks.md index 3434ef022da5e..f814970375a7d 100644 --- a/doc/tutorial-tasks.md +++ b/doc/tutorial-tasks.md @@ -473,7 +473,7 @@ fn stringifier(channel: &DuplexStream<~str, uint>) { let mut value: uint; loop { value = channel.recv(); - channel.send(uint::to_str(value, 10)); + channel.send(uint::to_str(value)); if value == 0 { break; } } } @@ -497,7 +497,7 @@ Here is the code for the parent task: # let mut value: uint; # loop { # value = channel.recv(); -# channel.send(uint::to_str(value, 10u)); +# channel.send(uint::to_str(value)); # if value == 0u { break; } # } # } diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index 9a138de3cf35d..30f6a727f0abc 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -422,6 +422,12 @@ pub pure fn to_str_bytes_common( buf = buf.slice(0, i + 1); } } + } // If exact and trailing '.', just cut that + else { + let max_i = buf.len() - 1; + if buf[max_i] == '.' as u8 { + buf = buf.slice(0, max_i); + } } (buf, false) @@ -678,4 +684,4 @@ pub pure fn from_str_common( ) -> Option { from_str_bytes_common(str::to_bytes(buf), radix, negative, fractional, special, exponent, empty_zero) -} \ No newline at end of file +} diff --git a/src/libstd/time.rs b/src/libstd/time.rs index 165c2a3d9bcb9..dd25fc36d2ddf 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -926,7 +926,7 @@ mod tests { let s0 = precise_time_s(); let ns1 = precise_time_ns(); - log(debug, ~"s0=" + float::to_str(s0, 9u) + ~" sec"); + log(debug, ~"s0=" + float::to_str_digits(s0, 9u) + ~" sec"); assert s0 > 0.; let ns0 = (s0 * 1000000000.) as u64; log(debug, ~"ns0=" + u64::str(ns0) + ~" ns"); diff --git a/src/test/bench/core-map.rs b/src/test/bench/core-map.rs index d401b594c4c06..a61d8f01ab785 100644 --- a/src/test/bench/core-map.rs +++ b/src/test/bench/core-map.rs @@ -75,12 +75,12 @@ fn old_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) { let map = map::HashMap(); do timed(&mut results.sequential_strings) { for uint::range(0, num_keys) |i| { - let s = uint::to_str(i, 10); + let s = uint::to_str(i); map.insert(s, i); } for uint::range(0, num_keys) |i| { - let s = uint::to_str(i, 10); + let s = uint::to_str(i); assert map.get(s) == i; } } @@ -90,7 +90,7 @@ fn old_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) { let map = map::HashMap(); do timed(&mut results.random_strings) { for uint::range(0, num_keys) |i| { - let s = uint::to_str(rng.next() as uint, 10); + let s = uint::to_str(rng.next() as uint); map.insert(s, i); } } @@ -99,11 +99,11 @@ fn old_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) { { let map = map::HashMap(); for uint::range(0, num_keys) |i| { - map.insert(uint::to_str(i, 10), i); + map.insert(uint::to_str(i), i); } do timed(&mut results.delete_strings) { for uint::range(0, num_keys) |i| { - assert map.remove(uint::to_str(i, 10)); + assert map.remove(uint::to_str(i)); } } } @@ -151,12 +151,12 @@ fn linear_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) let mut map = LinearMap::new(); do timed(&mut results.sequential_strings) { for uint::range(0, num_keys) |i| { - let s = uint::to_str(i, 10); + let s = uint::to_str(i); map.insert(s, i); } for uint::range(0, num_keys) |i| { - let s = uint::to_str(i, 10); + let s = uint::to_str(i); assert map.find(&s).unwrap() == &i; } } @@ -166,7 +166,7 @@ fn linear_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) let mut map = LinearMap::new(); do timed(&mut results.random_strings) { for uint::range(0, num_keys) |i| { - let s = uint::to_str(rng.next() as uint, 10); + let s = uint::to_str(rng.next() as uint); map.insert(s, i); } } @@ -175,11 +175,11 @@ fn linear_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) { let mut map = LinearMap::new(); for uint::range(0, num_keys) |i| { - map.insert(uint::to_str(i, 10), i); + map.insert(uint::to_str(i), i); } do timed(&mut results.delete_strings) { for uint::range(0, num_keys) |i| { - assert map.remove(&uint::to_str(i, 10)); + assert map.remove(&uint::to_str(i)); } } } @@ -227,12 +227,12 @@ fn tree_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) { let mut map = TreeMap::new(); do timed(&mut results.sequential_strings) { for uint::range(0, num_keys) |i| { - let s = uint::to_str(i, 10); + let s = uint::to_str(i); map.insert(s, i); } for uint::range(0, num_keys) |i| { - let s = uint::to_str(i, 10); + let s = uint::to_str(i); assert map.find(&s).unwrap() == &i; } } @@ -242,7 +242,7 @@ fn tree_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) { let mut map = TreeMap::new(); do timed(&mut results.random_strings) { for uint::range(0, num_keys) |i| { - let s = uint::to_str(rng.next() as uint, 10); + let s = uint::to_str(rng.next() as uint); map.insert(s, i); } } @@ -251,11 +251,11 @@ fn tree_str_benchmarks(rng: @rand::Rng, num_keys: uint, results: &mut Results) { { let mut map = TreeMap::new(); for uint::range(0, num_keys) |i| { - map.insert(uint::to_str(i, 10), i); + map.insert(uint::to_str(i), i); } do timed(&mut results.delete_strings) { for uint::range(0, num_keys) |i| { - assert map.remove(&uint::to_str(i, 10)); + assert map.remove(&uint::to_str(i)); } } } diff --git a/src/test/bench/core-uint-to-str.rs b/src/test/bench/core-uint-to-str.rs index df4962fb44ccb..56f616c6f2815 100644 --- a/src/test/bench/core-uint-to-str.rs +++ b/src/test/bench/core-uint-to-str.rs @@ -21,7 +21,7 @@ fn main() { let n = uint::from_str(args[1]).get(); for uint::range(0u, n) |i| { - let x = uint::to_str(i, 10u); + let x = uint::to_str(i); log(debug, x); } } diff --git a/src/test/run-pass/core-export-f64-sqrt.rs b/src/test/run-pass/core-export-f64-sqrt.rs index 37f605d932002..d7ac91fe75c32 100644 --- a/src/test/run-pass/core-export-f64-sqrt.rs +++ b/src/test/run-pass/core-export-f64-sqrt.rs @@ -14,5 +14,5 @@ pub fn main() { let digits: uint = 10 as uint; - ::core::io::println(float::to_str(f64::sqrt(42.0f64) as float, digits)); + ::core::io::println(float::to_str_digits(f64::sqrt(42.0f64) as float, digits)); } From 98a1eb2b8b5f896fbf60870d8e2db6efbe4bb641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 3 Feb 2013 14:13:06 +0100 Subject: [PATCH 13/14] Fixed errors resulting from rebase. --- src/libcore/char.rs | 4 ++-- src/libcore/num/float.rs | 10 +++++----- src/libcore/num/num.rs | 41 ++++++++++++++++++++-------------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 2fcd233203288..deecdc98ffa83 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -154,7 +154,7 @@ pub pure fn is_digit_radix(c: char, radix: uint) -> bool { #[inline] pub pure fn to_digit(c: char, radix: uint) -> Option { if radix > 36 { - fail fmt!("to_digit: radix %? is to high (maximum 36)", radix) + die!(fmt!("to_digit: radix %? is to high (maximum 36)", radix)); } let val = match c { '0' .. '9' => c as uint - ('0' as uint), @@ -177,7 +177,7 @@ pub pure fn to_digit(c: char, radix: uint) -> Option { #[inline] pub pure fn from_digit(num: uint, radix: uint) -> Option { if radix > 36 { - fail fmt!("from_digit: radix %? is to high (maximum 36)", num) + die!(fmt!("from_digit: radix %? is to high (maximum 36)", num)); } if num < radix { if num < 10 { diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs index 4f683ed14f76f..a73b5b9236c23 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -477,11 +477,11 @@ pub fn test_from_str() { // note: -0 == 0, hence these slightly more complex tests match from_str(~"-0") { Some(v) if is_zero(v) => assert is_negative(v), - _ => fail + _ => die!() } match from_str(~"0") { Some(v) if is_zero(v) => assert is_positive(v), - _ => fail + _ => die!() } assert from_str(~"").is_none(); @@ -519,16 +519,16 @@ pub fn test_from_str_hex() { // note: NaN != NaN, hence this slightly complex test match from_str_hex(~"NaN") { Some(f) => assert is_NaN(f), - None => fail + None => die!() } // note: -0 == 0, hence these slightly more complex tests match from_str_hex(~"-0") { Some(v) if is_zero(v) => assert is_negative(v), - _ => fail + _ => die!() } match from_str_hex(~"0") { Some(v) if is_zero(v) => assert is_positive(v), - _ => fail + _ => die!() } assert from_str_hex(~"e") == Some(14.); assert from_str_hex(~"E") == Some(14.); diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index 30f6a727f0abc..05b03a53dcdea 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -43,11 +43,11 @@ pub trait One { } pub trait Round { - pure fn round(&self, mode: RoundMode) -> self; + pure fn round(&self, mode: RoundMode) -> Self; - pure fn floor(&self) -> self; - pure fn ceil(&self) -> self; - pure fn fract(&self) -> self; + pure fn floor(&self) -> Self; + pure fn ceil(&self) -> Self; + pure fn fract(&self) -> Self; } pub enum RoundMode { @@ -62,7 +62,7 @@ pub trait ToStrRadix { } pub trait FromStrRadix { - static pub pure fn from_str_radix(str: &str, radix: uint) -> Option; + static pub pure fn from_str_radix(str: &str, radix: uint) -> Option; } // Generic math functions: @@ -135,7 +135,8 @@ pub pure fn is_neg_zero(num: &T) -> bool { * - If code written to use this function doesn't care about it, it's * probably assuming that `x^0` always equals `1`. */ -pub pure fn pow_with_uint(radix: uint, pow: uint) -> T { +pub pure fn pow_with_uint(radix: uint, + pow: uint) -> T { let _0: T = Zero::zero(); let _1: T = One::one(); @@ -220,11 +221,11 @@ pub pure fn to_str_bytes_common( num: &T, radix: uint, special: bool, negative_zero: bool, sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) { if radix as int < 2 { - fail fmt!("to_str_bytes_common: radix %? to low, \ - must lie in the range [2, 36]", radix); + die!(fmt!("to_str_bytes_common: radix %? to low, \ + must lie in the range [2, 36]", radix)); } else if radix as int > 36 { - fail fmt!("to_str_bytes_common: radix %? to high, \ - must lie in the range [2, 36]", radix); + die!(fmt!("to_str_bytes_common: radix %? to high, \ + must lie in the range [2, 36]", radix)); } let _0: T = Zero::zero(); @@ -499,20 +500,20 @@ pub pure fn from_str_bytes_common( ) -> Option { match exponent { ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' - => fail fmt!("from_str_bytes_common: radix %? incompatible with \ - use of 'e' as decimal exponent", radix), + => die!(fmt!("from_str_bytes_common: radix %? incompatible with \ + use of 'e' as decimal exponent", radix)), ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' - => fail fmt!("from_str_bytes_common: radix %? incompatible with \ - use of 'p' as binary exponent", radix), + => die!(fmt!("from_str_bytes_common: radix %? incompatible with \ + use of 'p' as binary exponent", radix)), _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' - => fail fmt!("from_str_bytes_common: radix %? incompatible with \ - special values 'inf' and 'NaN'", radix), + => die!(fmt!("from_str_bytes_common: radix %? incompatible with \ + special values 'inf' and 'NaN'", radix)), _ if radix as int < 2 - => fail fmt!("from_str_bytes_common: radix %? to low, \ - must lie in the range [2, 36]", radix), + => die!(fmt!("from_str_bytes_common: radix %? to low, \ + must lie in the range [2, 36]", radix)), _ if radix as int > 36 - => fail fmt!("from_str_bytes_common: radix %? to high, \ - must lie in the range [2, 36]", radix), + => die!(fmt!("from_str_bytes_common: radix %? to high, \ + must lie in the range [2, 36]", radix)), _ => () } From 0dc843b1fedd13bfca043e6afba5c0b56166d686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 3 Feb 2013 17:27:01 +0100 Subject: [PATCH 14/14] Solved float, f32 and f64 `to_str_radix()` special value ambiguity. Calling it on a special value now causes a failure, however `to_str_radix_special()` is provided which can be used if those values are expected, and which returns a tupel to allow differentating them. --- src/libcore/num/f32.rs | 24 +++++++++++++++++++++++- src/libcore/num/f64.rs | 24 +++++++++++++++++++++++- src/libcore/num/float.rs | 24 +++++++++++++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index a33a46192fdfd..68e7c3c9df26e 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -375,14 +375,36 @@ pub pure fn to_str_hex(num: f32) -> ~str { * * * num - The float value * * radix - The base to use + * + * # Failure + * + * Fails if called on a special value like `inf`, `-inf` or `NaN` due to + * possible misinterpretation of the result at higher bases. If those values + * are expected, use `to_str_radix_special()` instead. */ #[inline(always)] pub pure fn to_str_radix(num: f32, rdx: uint) -> ~str { - let (r, _) = num::to_str_common( + let (r, special) = num::to_str_common( &num, rdx, true, true, num::SignNeg, num::DigAll); + if special { die!(~"number has a special value, \ + try to_str_radix_special() if those are expected") } r } +/** + * Converts a float to a string in a given radix, and a flag indicating + * whether it's a special value + * + * # Arguments + * + * * num - The float value + * * radix - The base to use + */ +#[inline(always)] +pub pure fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) { + num::to_str_common(&num, rdx, true, true, num::SignNeg, num::DigAll) +} + /** * Converts a float to a string with exactly the number of * provided significant digits diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 276aa13da71f8..85f44d1b94f8d 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -399,14 +399,36 @@ pub pure fn to_str_hex(num: f64) -> ~str { * * * num - The float value * * radix - The base to use + * + * # Failure + * + * Fails if called on a special value like `inf`, `-inf` or `NaN` due to + * possible misinterpretation of the result at higher bases. If those values + * are expected, use `to_str_radix_special()` instead. */ #[inline(always)] pub pure fn to_str_radix(num: f64, rdx: uint) -> ~str { - let (r, _) = num::to_str_common( + let (r, special) = num::to_str_common( &num, rdx, true, true, num::SignNeg, num::DigAll); + if special { die!(~"number has a special value, \ + try to_str_radix_special() if those are expected") } r } +/** + * Converts a float to a string in a given radix, and a flag indicating + * whether it's a special value + * + * # Arguments + * + * * num - The float value + * * radix - The base to use + */ +#[inline(always)] +pub pure fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) { + num::to_str_common(&num, rdx, true, true, num::SignNeg, num::DigAll) +} + /** * Converts a float to a string with exactly the number of * provided significant digits diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs index a73b5b9236c23..32c7717422135 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -136,14 +136,36 @@ pub pure fn to_str_hex(num: float) -> ~str { * * * num - The float value * * radix - The base to use + * + * # Failure + * + * Fails if called on a special value like `inf`, `-inf` or `NaN` due to + * possible misinterpretation of the result at higher bases. If those values + * are expected, use `to_str_radix_special()` instead. */ #[inline(always)] pub pure fn to_str_radix(num: float, radix: uint) -> ~str { - let (r, _) = num::to_str_common( + let (r, special) = num::to_str_common( &num, radix, true, true, num::SignNeg, num::DigAll); + if special { die!(~"number has a special value, \ + try to_str_radix_special() if those are expected") } r } +/** + * Converts a float to a string in a given radix, and a flag indicating + * whether it's a special value + * + * # Arguments + * + * * num - The float value + * * radix - The base to use + */ +#[inline(always)] +pub pure fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) { + num::to_str_common(&num, radix, true, true, num::SignNeg, num::DigAll) +} + /** * Converts a float to a string with exactly the number of * provided significant digits