auto merge of #11611 : SiegeLord/rust/exp_printing, r=alexcrichton
Fixes #6593 Currently, Rust provides no way to print very large or very small floating point values which come up routinely in scientific and modeling work. The classical solution to this is to use the scientific/exponential notation, which not-coincidentally, corresponds to how floating point values are encoded in memory. Given this, there are two solutions to the problem. One is what, as far as I understand it, Python does. I.e. for floating point numbers in a certain range it does what we do today with the `'f'` formatting flag, otherwise it switches to exponential notation. The other way is to provide a set of formatting flags to explicitly choose the exponential notation, like it is done in C. I've chosen the second way as I think its important to provide that kind of control to the user. This pull request changes the `std::num::strconv::float_to_str_common` function to optionally format floating point numbers using the exponential (scientific) notation. The base of the significant can be varied between 2 and 25, while the base of the exponent can be 2 or 10. Additionally this adds two new formatting specifiers to `format!` and friends: `'e'` and `'E'` which switch between outputs like `1.0e5` and `1.0E5`. Mostly parroting C stdlib in this sense, although I wasn't going for an exact output match.
This commit is contained in:
commit
52ba3b6414
@ -147,6 +147,8 @@ The current mapping of types to traits is:
|
||||
* `p` ⇒ `Pointer`
|
||||
* `t` ⇒ `Binary`
|
||||
* `f` ⇒ `Float`
|
||||
* `e` ⇒ `LowerExp`
|
||||
* `E` ⇒ `UpperExp`
|
||||
* *nothing* ⇒ `Default`
|
||||
|
||||
What this means is that any type of argument which implements the
|
||||
@ -578,6 +580,12 @@ pub trait Pointer { fn fmt(&Self, &mut Formatter); }
|
||||
/// Format trait for the `f` character
|
||||
#[allow(missing_doc)]
|
||||
pub trait Float { fn fmt(&Self, &mut Formatter); }
|
||||
/// Format trait for the `e` character
|
||||
#[allow(missing_doc)]
|
||||
pub trait LowerExp { fn fmt(&Self, &mut Formatter); }
|
||||
/// Format trait for the `E` character
|
||||
#[allow(missing_doc)]
|
||||
pub trait UpperExp { fn fmt(&Self, &mut Formatter); }
|
||||
|
||||
/// The `write` function takes an output stream, a precompiled format string,
|
||||
/// and a list of arguments. The arguments will be formatted according to the
|
||||
@ -1085,6 +1093,28 @@ macro_rules! floating(($ty:ident) => {
|
||||
fmt.pad_integral(s.as_bytes(), "", *f >= 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerExp for $ty {
|
||||
fn fmt(f: &$ty, fmt: &mut Formatter) {
|
||||
// XXX: this shouldn't perform an allocation
|
||||
let s = match fmt.precision {
|
||||
Some(i) => ::$ty::to_str_exp_exact(f.abs(), i, false),
|
||||
None => ::$ty::to_str_exp_digits(f.abs(), 6, false)
|
||||
};
|
||||
fmt.pad_integral(s.as_bytes(), "", *f >= 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl UpperExp for $ty {
|
||||
fn fmt(f: &$ty, fmt: &mut Formatter) {
|
||||
// XXX: this shouldn't perform an allocation
|
||||
let s = match fmt.precision {
|
||||
Some(i) => ::$ty::to_str_exp_exact(f.abs(), i, true),
|
||||
None => ::$ty::to_str_exp_digits(f.abs(), 6, true)
|
||||
};
|
||||
fmt.pad_integral(s.as_bytes(), "", *f >= 0.0);
|
||||
}
|
||||
}
|
||||
})
|
||||
floating!(f32)
|
||||
floating!(f64)
|
||||
|
@ -285,20 +285,16 @@ impl Signed for f32 {
|
||||
#[inline]
|
||||
fn abs(&self) -> f32 { abs(*self) }
|
||||
|
||||
///
|
||||
/// The positive difference of two numbers. Returns `0.0` if the number is less than or
|
||||
/// equal to `other`, otherwise the difference between`self` and `other` is returned.
|
||||
///
|
||||
#[inline]
|
||||
fn abs_sub(&self, other: &f32) -> f32 { abs_sub(*self, *other) }
|
||||
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
|
||||
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
|
||||
/// - `NAN` if the number is NaN
|
||||
///
|
||||
#[inline]
|
||||
fn signum(&self) -> f32 {
|
||||
if self.is_nan() { NAN } else { copysign(1.0, *self) }
|
||||
@ -330,14 +326,12 @@ impl Round for f32 {
|
||||
#[inline]
|
||||
fn trunc(&self) -> f32 { trunc(*self) }
|
||||
|
||||
///
|
||||
/// The fractional part of the number, satisfying:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = 1.65f32;
|
||||
/// assert!(x == x.trunc() + x.fract())
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
fn fract(&self) -> f32 { *self - self.trunc() }
|
||||
}
|
||||
@ -490,7 +484,6 @@ impl Real for f32 {
|
||||
#[inline]
|
||||
fn tanh(&self) -> f32 { tanh(*self) }
|
||||
|
||||
///
|
||||
/// Inverse hyperbolic sine
|
||||
///
|
||||
/// # Returns
|
||||
@ -498,7 +491,6 @@ impl Real for f32 {
|
||||
/// - on success, the inverse hyperbolic sine of `self` will be returned
|
||||
/// - `self` if `self` is `0.0`, `-0.0`, `INFINITY`, or `NEG_INFINITY`
|
||||
/// - `NAN` if `self` is `NAN`
|
||||
///
|
||||
#[inline]
|
||||
fn asinh(&self) -> f32 {
|
||||
match *self {
|
||||
@ -507,7 +499,6 @@ impl Real for f32 {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Inverse hyperbolic cosine
|
||||
///
|
||||
/// # Returns
|
||||
@ -515,7 +506,6 @@ impl Real for f32 {
|
||||
/// - on success, the inverse hyperbolic cosine of `self` will be returned
|
||||
/// - `INFINITY` if `self` is `INFINITY`
|
||||
/// - `NAN` if `self` is `NAN` or `self < 1.0` (including `NEG_INFINITY`)
|
||||
///
|
||||
#[inline]
|
||||
fn acosh(&self) -> f32 {
|
||||
match *self {
|
||||
@ -524,7 +514,6 @@ impl Real for f32 {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Inverse hyperbolic tangent
|
||||
///
|
||||
/// # Returns
|
||||
@ -535,7 +524,6 @@ impl Real for f32 {
|
||||
/// - `NEG_INFINITY` if `self` is `-1.0`
|
||||
/// - `NAN` if the `self` is `NAN` or outside the domain of `-1.0 <= self <= 1.0`
|
||||
/// (including `INFINITY` and `NEG_INFINITY`)
|
||||
///
|
||||
#[inline]
|
||||
fn atanh(&self) -> f32 {
|
||||
0.5 * ((2.0 * *self) / (1.0 - *self)).ln_1p()
|
||||
@ -643,12 +631,10 @@ impl Float for f32 {
|
||||
ldexp(x, exp as c_int)
|
||||
}
|
||||
|
||||
///
|
||||
/// Breaks the number into a normalized fraction and a base-2 exponent, satisfying:
|
||||
///
|
||||
/// - `self = x * pow(2, exp)`
|
||||
/// - `0.5 <= abs(x) < 1.0`
|
||||
///
|
||||
#[inline]
|
||||
fn frexp(&self) -> (f32, int) {
|
||||
let mut exp = 0;
|
||||
@ -656,25 +642,19 @@ impl Float for f32 {
|
||||
(x, exp as int)
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the exponential of the number, minus `1`, in a way that is accurate
|
||||
/// even if the number is close to zero
|
||||
///
|
||||
#[inline]
|
||||
fn exp_m1(&self) -> f32 { exp_m1(*self) }
|
||||
|
||||
///
|
||||
/// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately
|
||||
/// than if the operations were performed separately
|
||||
///
|
||||
#[inline]
|
||||
fn ln_1p(&self) -> f32 { ln_1p(*self) }
|
||||
|
||||
///
|
||||
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
|
||||
/// produces a more accurate result with better performance than a separate multiplication
|
||||
/// operation followed by an add.
|
||||
///
|
||||
#[inline]
|
||||
fn mul_add(&self, a: f32, b: f32) -> f32 {
|
||||
mul_add(*self, a, b)
|
||||
@ -708,35 +688,30 @@ impl Float for f32 {
|
||||
// Section: String Conversions
|
||||
//
|
||||
|
||||
///
|
||||
/// Converts a float to a string
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str(num: f32) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigAll);
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string in hexadecimal format
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_hex(num: f32) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 16u, true, strconv::SignNeg, strconv::DigAll);
|
||||
num, 16u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string in a given radix, and a flag indicating
|
||||
/// whether it's a special value
|
||||
///
|
||||
@ -744,14 +719,12 @@ pub fn to_str_hex(num: f32) -> ~str {
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * radix - The base to use
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) {
|
||||
strconv::float_to_str_common(num, rdx, true,
|
||||
strconv::SignNeg, strconv::DigAll)
|
||||
strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string with exactly the number of
|
||||
/// provided significant digits
|
||||
///
|
||||
@ -759,15 +732,13 @@ pub fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) {
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of significant digits
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_exact(num: f32, dig: uint) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig));
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string with a maximum number of
|
||||
/// significant digits
|
||||
///
|
||||
@ -775,11 +746,40 @@ pub fn to_str_exact(num: f32, dig: uint) -> ~str {
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of significant digits
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_digits(num: f32, dig: uint) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig));
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
/// Converts a float to a string using the exponential notation with exactly the number of
|
||||
/// provided digits after the decimal point in the significand
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of digits after the decimal point
|
||||
/// * upper - Use `E` instead of `e` for the exponent sign
|
||||
#[inline]
|
||||
pub fn to_str_exp_exact(num: f32, dig: uint, upper: bool) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpDec, upper);
|
||||
r
|
||||
}
|
||||
|
||||
/// Converts a float to a string using the exponential notation with the maximum number of
|
||||
/// digits after the decimal point in the significand
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of digits after the decimal point
|
||||
/// * upper - Use `E` instead of `e` for the exponent sign
|
||||
#[inline]
|
||||
pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpDec, upper);
|
||||
r
|
||||
}
|
||||
|
||||
@ -804,14 +804,13 @@ impl num::ToStrRadix for f32 {
|
||||
#[inline]
|
||||
fn to_str_radix(&self, rdx: uint) -> ~str {
|
||||
let (r, special) = strconv::float_to_str_common(
|
||||
*self, rdx, true, strconv::SignNeg, strconv::DigAll);
|
||||
*self, rdx, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
|
||||
if special { fail!("number has a special value, \
|
||||
try to_str_radix_special() if those are expected") }
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Convert a string in base 16 to a float.
|
||||
/// Accepts a optional binary exponent.
|
||||
///
|
||||
@ -837,7 +836,6 @@ impl num::ToStrRadix for f32 {
|
||||
///
|
||||
/// `None` if the string did not represent a valid number. Otherwise,
|
||||
/// `Some(n)` where `n` is the floating-point number represented by `[num]`.
|
||||
///
|
||||
#[inline]
|
||||
pub fn from_str_hex(num: &str) -> Option<f32> {
|
||||
strconv::from_str_common(num, 16u, true, true, true,
|
||||
@ -845,7 +843,6 @@ pub fn from_str_hex(num: &str) -> Option<f32> {
|
||||
}
|
||||
|
||||
impl FromStr for f32 {
|
||||
///
|
||||
/// Convert a string in base 10 to a float.
|
||||
/// Accepts a optional decimal exponent.
|
||||
///
|
||||
@ -871,7 +868,6 @@ impl FromStr for f32 {
|
||||
///
|
||||
/// `None` if the string did not represent a valid number. Otherwise,
|
||||
/// `Some(n)` where `n` is the floating-point number represented by `num`.
|
||||
///
|
||||
#[inline]
|
||||
fn from_str(val: &str) -> Option<f32> {
|
||||
strconv::from_str_common(val, 10u, true, true, true,
|
||||
@ -880,7 +876,6 @@ impl FromStr for f32 {
|
||||
}
|
||||
|
||||
impl num::FromStrRadix for f32 {
|
||||
///
|
||||
/// Convert a string in an given base to a float.
|
||||
///
|
||||
/// Due to possible conflicts, this function does **not** accept
|
||||
@ -898,7 +893,6 @@ impl num::FromStrRadix for f32 {
|
||||
///
|
||||
/// `None` if the string did not represent a valid number. Otherwise,
|
||||
/// `Some(n)` where `n` is the floating-point number represented by `num`.
|
||||
///
|
||||
#[inline]
|
||||
fn from_str_radix(val: &str, rdx: uint) -> Option<f32> {
|
||||
strconv::from_str_common(val, rdx, true, true, false,
|
||||
|
@ -287,20 +287,16 @@ impl Signed for f64 {
|
||||
#[inline]
|
||||
fn abs(&self) -> f64 { abs(*self) }
|
||||
|
||||
///
|
||||
/// The positive difference of two numbers. Returns `0.0` if the number is less than or
|
||||
/// equal to `other`, otherwise the difference between`self` and `other` is returned.
|
||||
///
|
||||
#[inline]
|
||||
fn abs_sub(&self, other: &f64) -> f64 { abs_sub(*self, *other) }
|
||||
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
|
||||
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
|
||||
/// - `NAN` if the number is NaN
|
||||
///
|
||||
#[inline]
|
||||
fn signum(&self) -> f64 {
|
||||
if self.is_nan() { NAN } else { copysign(1.0, *self) }
|
||||
@ -332,14 +328,12 @@ impl Round for f64 {
|
||||
#[inline]
|
||||
fn trunc(&self) -> f64 { trunc(*self) }
|
||||
|
||||
///
|
||||
/// The fractional part of the number, satisfying:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = 1.65f64;
|
||||
/// assert!(x == x.trunc() + x.fract())
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
fn fract(&self) -> f64 { *self - self.trunc() }
|
||||
}
|
||||
@ -492,7 +486,6 @@ impl Real for f64 {
|
||||
#[inline]
|
||||
fn tanh(&self) -> f64 { tanh(*self) }
|
||||
|
||||
///
|
||||
/// Inverse hyperbolic sine
|
||||
///
|
||||
/// # Returns
|
||||
@ -500,7 +493,6 @@ impl Real for f64 {
|
||||
/// - on success, the inverse hyperbolic sine of `self` will be returned
|
||||
/// - `self` if `self` is `0.0`, `-0.0`, `INFINITY`, or `NEG_INFINITY`
|
||||
/// - `NAN` if `self` is `NAN`
|
||||
///
|
||||
#[inline]
|
||||
fn asinh(&self) -> f64 {
|
||||
match *self {
|
||||
@ -509,7 +501,6 @@ impl Real for f64 {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Inverse hyperbolic cosine
|
||||
///
|
||||
/// # Returns
|
||||
@ -517,7 +508,6 @@ impl Real for f64 {
|
||||
/// - on success, the inverse hyperbolic cosine of `self` will be returned
|
||||
/// - `INFINITY` if `self` is `INFINITY`
|
||||
/// - `NAN` if `self` is `NAN` or `self < 1.0` (including `NEG_INFINITY`)
|
||||
///
|
||||
#[inline]
|
||||
fn acosh(&self) -> f64 {
|
||||
match *self {
|
||||
@ -526,7 +516,6 @@ impl Real for f64 {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Inverse hyperbolic tangent
|
||||
///
|
||||
/// # Returns
|
||||
@ -537,7 +526,6 @@ impl Real for f64 {
|
||||
/// - `NEG_INFINITY` if `self` is `-1.0`
|
||||
/// - `NAN` if the `self` is `NAN` or outside the domain of `-1.0 <= self <= 1.0`
|
||||
/// (including `INFINITY` and `NEG_INFINITY`)
|
||||
///
|
||||
#[inline]
|
||||
fn atanh(&self) -> f64 {
|
||||
0.5 * ((2.0 * *self) / (1.0 - *self)).ln_1p()
|
||||
@ -645,12 +633,10 @@ impl Float for f64 {
|
||||
ldexp(x, exp as c_int)
|
||||
}
|
||||
|
||||
///
|
||||
/// Breaks the number into a normalized fraction and a base-2 exponent, satisfying:
|
||||
///
|
||||
/// - `self = x * pow(2, exp)`
|
||||
/// - `0.5 <= abs(x) < 1.0`
|
||||
///
|
||||
#[inline]
|
||||
fn frexp(&self) -> (f64, int) {
|
||||
let mut exp = 0;
|
||||
@ -658,25 +644,19 @@ impl Float for f64 {
|
||||
(x, exp as int)
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the exponential of the number, minus `1`, in a way that is accurate
|
||||
/// even if the number is close to zero
|
||||
///
|
||||
#[inline]
|
||||
fn exp_m1(&self) -> f64 { exp_m1(*self) }
|
||||
|
||||
///
|
||||
/// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately
|
||||
/// than if the operations were performed separately
|
||||
///
|
||||
#[inline]
|
||||
fn ln_1p(&self) -> f64 { ln_1p(*self) }
|
||||
|
||||
///
|
||||
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
|
||||
/// produces a more accurate result with better performance than a separate multiplication
|
||||
/// operation followed by an add.
|
||||
///
|
||||
#[inline]
|
||||
fn mul_add(&self, a: f64, b: f64) -> f64 {
|
||||
mul_add(*self, a, b)
|
||||
@ -710,35 +690,30 @@ impl Float for f64 {
|
||||
// Section: String Conversions
|
||||
//
|
||||
|
||||
///
|
||||
/// Converts a float to a string
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str(num: f64) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigAll);
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string in hexadecimal format
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_hex(num: f64) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 16u, true, strconv::SignNeg, strconv::DigAll);
|
||||
num, 16u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string in a given radix, and a flag indicating
|
||||
/// whether it's a special value
|
||||
///
|
||||
@ -746,14 +721,12 @@ pub fn to_str_hex(num: f64) -> ~str {
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * radix - The base to use
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) {
|
||||
strconv::float_to_str_common(num, rdx, true,
|
||||
strconv::SignNeg, strconv::DigAll)
|
||||
strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string with exactly the number of
|
||||
/// provided significant digits
|
||||
///
|
||||
@ -761,15 +734,13 @@ pub fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) {
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of significant digits
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_exact(num: f64, dig: uint) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig));
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a float to a string with a maximum number of
|
||||
/// significant digits
|
||||
///
|
||||
@ -777,11 +748,40 @@ pub fn to_str_exact(num: f64, dig: uint) -> ~str {
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of significant digits
|
||||
///
|
||||
#[inline]
|
||||
pub fn to_str_digits(num: f64, dig: uint) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig));
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpNone, false);
|
||||
r
|
||||
}
|
||||
|
||||
/// Converts a float to a string using the exponential notation with exactly the number of
|
||||
/// provided digits after the decimal point in the significand
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of digits after the decimal point
|
||||
/// * upper - Use `E` instead of `e` for the exponent sign
|
||||
#[inline]
|
||||
pub fn to_str_exp_exact(num: f64, dig: uint, upper: bool) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpDec, upper);
|
||||
r
|
||||
}
|
||||
|
||||
/// Converts a float to a string using the exponential notation with the maximum number of
|
||||
/// digits after the decimal point in the significand
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * num - The float value
|
||||
/// * digits - The number of digits after the decimal point
|
||||
/// * upper - Use `E` instead of `e` for the exponent sign
|
||||
#[inline]
|
||||
pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> ~str {
|
||||
let (r, _) = strconv::float_to_str_common(
|
||||
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpDec, upper);
|
||||
r
|
||||
}
|
||||
|
||||
@ -806,14 +806,13 @@ impl num::ToStrRadix for f64 {
|
||||
#[inline]
|
||||
fn to_str_radix(&self, rdx: uint) -> ~str {
|
||||
let (r, special) = strconv::float_to_str_common(
|
||||
*self, rdx, true, strconv::SignNeg, strconv::DigAll);
|
||||
*self, rdx, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
|
||||
if special { fail!("number has a special value, \
|
||||
try to_str_radix_special() if those are expected") }
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Convert a string in base 16 to a float.
|
||||
/// Accepts a optional binary exponent.
|
||||
///
|
||||
@ -839,7 +838,6 @@ impl num::ToStrRadix for f64 {
|
||||
///
|
||||
/// `None` if the string did not represent a valid number. Otherwise,
|
||||
/// `Some(n)` where `n` is the floating-point number represented by `[num]`.
|
||||
///
|
||||
#[inline]
|
||||
pub fn from_str_hex(num: &str) -> Option<f64> {
|
||||
strconv::from_str_common(num, 16u, true, true, true,
|
||||
@ -847,7 +845,6 @@ pub fn from_str_hex(num: &str) -> Option<f64> {
|
||||
}
|
||||
|
||||
impl FromStr for f64 {
|
||||
///
|
||||
/// Convert a string in base 10 to a float.
|
||||
/// Accepts a optional decimal exponent.
|
||||
///
|
||||
@ -873,7 +870,6 @@ impl FromStr for f64 {
|
||||
///
|
||||
/// `none` if the string did not represent a valid number. Otherwise,
|
||||
/// `Some(n)` where `n` is the floating-point number represented by `num`.
|
||||
///
|
||||
#[inline]
|
||||
fn from_str(val: &str) -> Option<f64> {
|
||||
strconv::from_str_common(val, 10u, true, true, true,
|
||||
@ -882,7 +878,6 @@ impl FromStr for f64 {
|
||||
}
|
||||
|
||||
impl num::FromStrRadix for f64 {
|
||||
///
|
||||
/// Convert a string in an given base to a float.
|
||||
///
|
||||
/// Due to possible conflicts, this function does **not** accept
|
||||
@ -900,7 +895,6 @@ impl num::FromStrRadix for f64 {
|
||||
///
|
||||
/// `None` if the string did not represent a valid number. Otherwise,
|
||||
/// `Some(n)` where `n` is the floating-point number represented by `num`.
|
||||
///
|
||||
#[inline]
|
||||
fn from_str_radix(val: &str, rdx: uint) -> Option<f64> {
|
||||
strconv::from_str_common(val, rdx, true, true, false,
|
||||
|
@ -207,11 +207,13 @@ pub fn int_to_str_bytes_common<T:NumCast
|
||||
* # Arguments
|
||||
* - `num` - The number to convert. Accepts any number that
|
||||
* implements the numeric traits.
|
||||
* - `radix` - Base to use. Accepts only the values 2-36.
|
||||
* - `radix` - Base to use. Accepts only the values 2-36. If the exponential notation
|
||||
* is used, then this base is only used for the significand. The exponent
|
||||
* itself always printed using a base of 10.
|
||||
* - `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)`.
|
||||
* - `SignNone`: No sign at all. The exponent sign is also omitted.
|
||||
* - `SignNeg`: Only `-` on negative values.
|
||||
* - `SignAll`: Both `+` on positive, and `-` on negative numbers.
|
||||
* - `digits` - The amount of digits to use for emitting the
|
||||
@ -220,6 +222,17 @@ pub fn int_to_str_bytes_common<T:NumCast
|
||||
* fractions!
|
||||
* - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros.
|
||||
* - `DigExact(uint)`: Exactly N digits.
|
||||
* - `exp_format` - Whether or not to use the exponential (scientific) notation.
|
||||
* Options are:
|
||||
* - `ExpNone`: Do not use the exponential notation.
|
||||
* - `ExpDec`: Use the exponential notation with the exponent having a base of 10,
|
||||
* and exponent sign being `'e'` or `'E'` depending on the value of
|
||||
* the `exp_upper` argument. E.g. the number 1000 would be printed as 1e3.
|
||||
* - `ExpBin`: Use the exponential notation with the exponent having a base of 2,
|
||||
* and exponent sign being `'p'` or `'P'` depending on the value of
|
||||
* the `exp_upper` argument. E.g. the number 8 would be printed as 1p3.
|
||||
* - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if
|
||||
* exponential notation is desired.
|
||||
*
|
||||
* # Return value
|
||||
* A tuple containing the byte vector, and a boolean flag indicating
|
||||
@ -229,12 +242,26 @@ pub fn int_to_str_bytes_common<T:NumCast
|
||||
*
|
||||
* # Failure
|
||||
* - Fails if `radix` < 2 or `radix` > 36.
|
||||
* - Fails if `radix` > 14 and `exp_format` is `ExpDec` due to conflict
|
||||
* between digit and exponent sign `'e'`.
|
||||
* - Fails if `radix` > 25 and `exp_format` is `ExpBin` due to conflict
|
||||
* between digit and exponent sign `'p'`.
|
||||
*/
|
||||
pub fn float_to_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Float+Round+
|
||||
Div<T,T>+Neg<T>+Rem<T,T>+Mul<T,T>>(
|
||||
num: T, radix: uint, negative_zero: bool,
|
||||
sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) {
|
||||
sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_upper: bool
|
||||
) -> (~[u8], bool) {
|
||||
assert!(2 <= radix && radix <= 36);
|
||||
match exp_format {
|
||||
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
|
||||
=> fail!("float_to_str_bytes_common: radix {} incompatible with \
|
||||
use of 'e' as decimal exponent", radix),
|
||||
ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p'
|
||||
=> fail!("float_to_str_bytes_common: radix {} incompatible with \
|
||||
use of 'p' as binary exponent", radix),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
@ -260,6 +287,23 @@ pub fn float_to_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Float+Round+
|
||||
let mut buf: ~[u8] = ~[];
|
||||
let radix_gen: T = cast(radix as int).unwrap();
|
||||
|
||||
let (num, exp) = match exp_format {
|
||||
ExpNone => (num, 0i32),
|
||||
ExpDec | ExpBin => {
|
||||
if num == _0 {
|
||||
(num, 0i32)
|
||||
} else {
|
||||
let (exp, exp_base) = match exp_format {
|
||||
ExpDec => (num.abs().log10().floor(), cast::<f64, T>(10.0f64).unwrap()),
|
||||
ExpBin => (num.abs().log2().floor(), cast::<f64, T>(2.0f64).unwrap()),
|
||||
ExpNone => unreachable!()
|
||||
};
|
||||
|
||||
(num / exp_base.powf(&exp), cast::<T, i32>(exp).unwrap())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// First emit the non-fractional part, looping at least once to make
|
||||
// sure at least a `0` gets emitted.
|
||||
let mut deccum = num.trunc();
|
||||
@ -413,6 +457,21 @@ pub fn float_to_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Float+Round+
|
||||
}
|
||||
}
|
||||
|
||||
match exp_format {
|
||||
ExpNone => (),
|
||||
_ => {
|
||||
buf.push(match exp_format {
|
||||
ExpDec if exp_upper => 'E',
|
||||
ExpDec if !exp_upper => 'e',
|
||||
ExpBin if exp_upper => 'P',
|
||||
ExpBin if !exp_upper => 'p',
|
||||
_ => unreachable!()
|
||||
} as u8);
|
||||
|
||||
int_to_str_bytes_common(exp, 10, sign, |c| buf.push(c));
|
||||
}
|
||||
}
|
||||
|
||||
(buf, false)
|
||||
}
|
||||
|
||||
@ -424,9 +483,10 @@ pub fn float_to_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Float+Round+
|
||||
pub fn float_to_str_common<T:NumCast+Zero+One+Eq+Ord+NumStrConv+Float+Round+
|
||||
Div<T,T>+Neg<T>+Rem<T,T>+Mul<T,T>>(
|
||||
num: T, radix: uint, negative_zero: bool,
|
||||
sign: SignFormat, digits: SignificantDigits) -> (~str, bool) {
|
||||
sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_capital: bool
|
||||
) -> (~str, bool) {
|
||||
let (bytes, special) = float_to_str_bytes_common(num, radix,
|
||||
negative_zero, sign, digits);
|
||||
negative_zero, sign, digits, exp_format, exp_capital);
|
||||
(str::from_utf8_owned(bytes).unwrap(), special)
|
||||
}
|
||||
|
||||
|
@ -687,6 +687,8 @@ impl<'a> Context<'a> {
|
||||
"b" => "Bool",
|
||||
"c" => "Char",
|
||||
"d" | "i" => "Signed",
|
||||
"e" => "LowerExp",
|
||||
"E" => "UpperExp",
|
||||
"f" => "Float",
|
||||
"o" => "Octal",
|
||||
"p" => "Pointer",
|
||||
|
34
src/test/run-pass/exponential-notation.rs
Normal file
34
src/test/run-pass/exponential-notation.rs
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[feature(macro_rules)];
|
||||
|
||||
use s = std::num::strconv;
|
||||
use to_str = std::num::strconv::float_to_str_common;
|
||||
|
||||
macro_rules! t(($a:expr, $b:expr) => { { let (r, _) = $a; assert_eq!(r, $b.to_owned()) } })
|
||||
|
||||
pub fn main() {
|
||||
// Basic usage
|
||||
t!(to_str(1.2345678e-5, 10u, true, s::SignNeg, s::DigMax(6), s::ExpDec, false),
|
||||
"1.234568e-5")
|
||||
|
||||
// Hexadecimal output
|
||||
t!(to_str(7.281738281250e+01, 16u, true, s::SignAll, s::DigMax(6), s::ExpBin, false),
|
||||
"+1.2345p+6")
|
||||
t!(to_str(-1.777768135071e-02, 16u, true, s::SignAll, s::DigMax(6), s::ExpBin, false),
|
||||
"-1.2345p-6")
|
||||
|
||||
// Some denormals
|
||||
t!(to_str(4.9406564584124654e-324, 10u, true, s::SignNeg, s::DigMax(6), s::ExpBin, false),
|
||||
"1p-1074")
|
||||
t!(to_str(2.2250738585072009e-308, 10u, true, s::SignNeg, s::DigMax(6), s::ExpBin, false),
|
||||
"1p-1022")
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
@ -219,6 +219,14 @@ pub fn main() {
|
||||
t!(format!("{:+10.3f}", 1.0f64), " +1.000");
|
||||
t!(format!("{:+10.3f}", -1.0f64), " -1.000");
|
||||
|
||||
t!(format!("{:e}", 1.2345e6f32), "1.2345e6");
|
||||
t!(format!("{:e}", 1.2345e6f64), "1.2345e6");
|
||||
t!(format!("{:E}", 1.2345e6f64), "1.2345E6");
|
||||
t!(format!("{:.3e}", 1.2345e6f64), "1.234e6");
|
||||
t!(format!("{:10.3e}", 1.2345e6f64), " 1.234e6");
|
||||
t!(format!("{:+10.3e}", 1.2345e6f64), " +1.234e6");
|
||||
t!(format!("{:+10.3e}", -1.2345e6f64), " -1.234e6");
|
||||
|
||||
// Escaping
|
||||
t!(format!("\\{"), "{");
|
||||
t!(format!("\\}"), "}");
|
||||
|
Loading…
x
Reference in New Issue
Block a user