Skip to content

Commit

Permalink
auto merge of #11611 : SiegeLord/rust/exp_printing, r=alexcrichton
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Jan 23, 2014
2 parents 7ea063e + acd718b commit 52ba3b6
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 90 deletions.
30 changes: 30 additions & 0 deletions src/libstd/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
78 changes: 36 additions & 42 deletions src/libstd/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down Expand Up @@ -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() }
}
Expand Down Expand Up @@ -490,15 +484,13 @@ impl Real for f32 {
#[inline]
fn tanh(&self) -> f32 { tanh(*self) }

///
/// Inverse hyperbolic sine
///
/// # Returns
///
/// - 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 {
Expand All @@ -507,15 +499,13 @@ impl Real for f32 {
}
}

///
/// Inverse hyperbolic cosine
///
/// # Returns
///
/// - 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 {
Expand All @@ -524,7 +514,6 @@ impl Real for f32 {
}
}

///
/// Inverse hyperbolic tangent
///
/// # Returns
Expand All @@ -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()
Expand Down Expand Up @@ -643,38 +631,30 @@ 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;
let x = frexp(*self, &mut exp);
(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)
Expand Down Expand Up @@ -708,78 +688,98 @@ 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
///
/// # Arguments
///
/// * 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
///
/// # Arguments
///
/// * 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
///
/// # Arguments
///
/// * 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
}

Expand All @@ -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.
///
Expand All @@ -837,15 +836,13 @@ 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,
strconv::ExpBin, false, false)
}

impl FromStr for f32 {
///
/// Convert a string in base 10 to a float.
/// Accepts a optional decimal exponent.
///
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 52ba3b6

Please sign in to comment.