Skip to content

Commit 52ba3b6

Browse files
committed
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.
2 parents 7ea063e + acd718b commit 52ba3b6

File tree

7 files changed

+212
-90
lines changed

7 files changed

+212
-90
lines changed

src/libstd/fmt/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ The current mapping of types to traits is:
147147
* `p` ⇒ `Pointer`
148148
* `t` ⇒ `Binary`
149149
* `f` ⇒ `Float`
150+
* `e` ⇒ `LowerExp`
151+
* `E` ⇒ `UpperExp`
150152
* *nothing* ⇒ `Default`
151153
152154
What this means is that any type of argument which implements the
@@ -578,6 +580,12 @@ pub trait Pointer { fn fmt(&Self, &mut Formatter); }
578580
/// Format trait for the `f` character
579581
#[allow(missing_doc)]
580582
pub trait Float { fn fmt(&Self, &mut Formatter); }
583+
/// Format trait for the `e` character
584+
#[allow(missing_doc)]
585+
pub trait LowerExp { fn fmt(&Self, &mut Formatter); }
586+
/// Format trait for the `E` character
587+
#[allow(missing_doc)]
588+
pub trait UpperExp { fn fmt(&Self, &mut Formatter); }
581589

582590
/// The `write` function takes an output stream, a precompiled format string,
583591
/// and a list of arguments. The arguments will be formatted according to the
@@ -1085,6 +1093,28 @@ macro_rules! floating(($ty:ident) => {
10851093
fmt.pad_integral(s.as_bytes(), "", *f >= 0.0);
10861094
}
10871095
}
1096+
1097+
impl LowerExp for $ty {
1098+
fn fmt(f: &$ty, fmt: &mut Formatter) {
1099+
// XXX: this shouldn't perform an allocation
1100+
let s = match fmt.precision {
1101+
Some(i) => ::$ty::to_str_exp_exact(f.abs(), i, false),
1102+
None => ::$ty::to_str_exp_digits(f.abs(), 6, false)
1103+
};
1104+
fmt.pad_integral(s.as_bytes(), "", *f >= 0.0);
1105+
}
1106+
}
1107+
1108+
impl UpperExp for $ty {
1109+
fn fmt(f: &$ty, fmt: &mut Formatter) {
1110+
// XXX: this shouldn't perform an allocation
1111+
let s = match fmt.precision {
1112+
Some(i) => ::$ty::to_str_exp_exact(f.abs(), i, true),
1113+
None => ::$ty::to_str_exp_digits(f.abs(), 6, true)
1114+
};
1115+
fmt.pad_integral(s.as_bytes(), "", *f >= 0.0);
1116+
}
1117+
}
10881118
})
10891119
floating!(f32)
10901120
floating!(f64)

src/libstd/num/f32.rs

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -285,20 +285,16 @@ impl Signed for f32 {
285285
#[inline]
286286
fn abs(&self) -> f32 { abs(*self) }
287287

288-
///
289288
/// The positive difference of two numbers. Returns `0.0` if the number is less than or
290289
/// equal to `other`, otherwise the difference between`self` and `other` is returned.
291-
///
292290
#[inline]
293291
fn abs_sub(&self, other: &f32) -> f32 { abs_sub(*self, *other) }
294292

295-
///
296293
/// # Returns
297294
///
298295
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
299296
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
300297
/// - `NAN` if the number is NaN
301-
///
302298
#[inline]
303299
fn signum(&self) -> f32 {
304300
if self.is_nan() { NAN } else { copysign(1.0, *self) }
@@ -330,14 +326,12 @@ impl Round for f32 {
330326
#[inline]
331327
fn trunc(&self) -> f32 { trunc(*self) }
332328

333-
///
334329
/// The fractional part of the number, satisfying:
335330
///
336331
/// ```rust
337332
/// let x = 1.65f32;
338333
/// assert!(x == x.trunc() + x.fract())
339334
/// ```
340-
///
341335
#[inline]
342336
fn fract(&self) -> f32 { *self - self.trunc() }
343337
}
@@ -490,15 +484,13 @@ impl Real for f32 {
490484
#[inline]
491485
fn tanh(&self) -> f32 { tanh(*self) }
492486

493-
///
494487
/// Inverse hyperbolic sine
495488
///
496489
/// # Returns
497490
///
498491
/// - on success, the inverse hyperbolic sine of `self` will be returned
499492
/// - `self` if `self` is `0.0`, `-0.0`, `INFINITY`, or `NEG_INFINITY`
500493
/// - `NAN` if `self` is `NAN`
501-
///
502494
#[inline]
503495
fn asinh(&self) -> f32 {
504496
match *self {
@@ -507,15 +499,13 @@ impl Real for f32 {
507499
}
508500
}
509501

510-
///
511502
/// Inverse hyperbolic cosine
512503
///
513504
/// # Returns
514505
///
515506
/// - on success, the inverse hyperbolic cosine of `self` will be returned
516507
/// - `INFINITY` if `self` is `INFINITY`
517508
/// - `NAN` if `self` is `NAN` or `self < 1.0` (including `NEG_INFINITY`)
518-
///
519509
#[inline]
520510
fn acosh(&self) -> f32 {
521511
match *self {
@@ -524,7 +514,6 @@ impl Real for f32 {
524514
}
525515
}
526516

527-
///
528517
/// Inverse hyperbolic tangent
529518
///
530519
/// # Returns
@@ -535,7 +524,6 @@ impl Real for f32 {
535524
/// - `NEG_INFINITY` if `self` is `-1.0`
536525
/// - `NAN` if the `self` is `NAN` or outside the domain of `-1.0 <= self <= 1.0`
537526
/// (including `INFINITY` and `NEG_INFINITY`)
538-
///
539527
#[inline]
540528
fn atanh(&self) -> f32 {
541529
0.5 * ((2.0 * *self) / (1.0 - *self)).ln_1p()
@@ -643,38 +631,30 @@ impl Float for f32 {
643631
ldexp(x, exp as c_int)
644632
}
645633

646-
///
647634
/// Breaks the number into a normalized fraction and a base-2 exponent, satisfying:
648635
///
649636
/// - `self = x * pow(2, exp)`
650637
/// - `0.5 <= abs(x) < 1.0`
651-
///
652638
#[inline]
653639
fn frexp(&self) -> (f32, int) {
654640
let mut exp = 0;
655641
let x = frexp(*self, &mut exp);
656642
(x, exp as int)
657643
}
658644

659-
///
660645
/// Returns the exponential of the number, minus `1`, in a way that is accurate
661646
/// even if the number is close to zero
662-
///
663647
#[inline]
664648
fn exp_m1(&self) -> f32 { exp_m1(*self) }
665649

666-
///
667650
/// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately
668651
/// than if the operations were performed separately
669-
///
670652
#[inline]
671653
fn ln_1p(&self) -> f32 { ln_1p(*self) }
672654

673-
///
674655
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
675656
/// produces a more accurate result with better performance than a separate multiplication
676657
/// operation followed by an add.
677-
///
678658
#[inline]
679659
fn mul_add(&self, a: f32, b: f32) -> f32 {
680660
mul_add(*self, a, b)
@@ -708,78 +688,98 @@ impl Float for f32 {
708688
// Section: String Conversions
709689
//
710690

711-
///
712691
/// Converts a float to a string
713692
///
714693
/// # Arguments
715694
///
716695
/// * num - The float value
717-
///
718696
#[inline]
719697
pub fn to_str(num: f32) -> ~str {
720698
let (r, _) = strconv::float_to_str_common(
721-
num, 10u, true, strconv::SignNeg, strconv::DigAll);
699+
num, 10u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
722700
r
723701
}
724702

725-
///
726703
/// Converts a float to a string in hexadecimal format
727704
///
728705
/// # Arguments
729706
///
730707
/// * num - The float value
731-
///
732708
#[inline]
733709
pub fn to_str_hex(num: f32) -> ~str {
734710
let (r, _) = strconv::float_to_str_common(
735-
num, 16u, true, strconv::SignNeg, strconv::DigAll);
711+
num, 16u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
736712
r
737713
}
738714

739-
///
740715
/// Converts a float to a string in a given radix, and a flag indicating
741716
/// whether it's a special value
742717
///
743718
/// # Arguments
744719
///
745720
/// * num - The float value
746721
/// * radix - The base to use
747-
///
748722
#[inline]
749723
pub fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) {
750724
strconv::float_to_str_common(num, rdx, true,
751-
strconv::SignNeg, strconv::DigAll)
725+
strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false)
752726
}
753727

754-
///
755728
/// Converts a float to a string with exactly the number of
756729
/// provided significant digits
757730
///
758731
/// # Arguments
759732
///
760733
/// * num - The float value
761734
/// * digits - The number of significant digits
762-
///
763735
#[inline]
764736
pub fn to_str_exact(num: f32, dig: uint) -> ~str {
765737
let (r, _) = strconv::float_to_str_common(
766-
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig));
738+
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpNone, false);
767739
r
768740
}
769741

770-
///
771742
/// Converts a float to a string with a maximum number of
772743
/// significant digits
773744
///
774745
/// # Arguments
775746
///
776747
/// * num - The float value
777748
/// * digits - The number of significant digits
778-
///
779749
#[inline]
780750
pub fn to_str_digits(num: f32, dig: uint) -> ~str {
781751
let (r, _) = strconv::float_to_str_common(
782-
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig));
752+
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpNone, false);
753+
r
754+
}
755+
756+
/// Converts a float to a string using the exponential notation with exactly the number of
757+
/// provided digits after the decimal point in the significand
758+
///
759+
/// # Arguments
760+
///
761+
/// * num - The float value
762+
/// * digits - The number of digits after the decimal point
763+
/// * upper - Use `E` instead of `e` for the exponent sign
764+
#[inline]
765+
pub fn to_str_exp_exact(num: f32, dig: uint, upper: bool) -> ~str {
766+
let (r, _) = strconv::float_to_str_common(
767+
num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpDec, upper);
768+
r
769+
}
770+
771+
/// Converts a float to a string using the exponential notation with the maximum number of
772+
/// digits after the decimal point in the significand
773+
///
774+
/// # Arguments
775+
///
776+
/// * num - The float value
777+
/// * digits - The number of digits after the decimal point
778+
/// * upper - Use `E` instead of `e` for the exponent sign
779+
#[inline]
780+
pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> ~str {
781+
let (r, _) = strconv::float_to_str_common(
782+
num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpDec, upper);
783783
r
784784
}
785785

@@ -804,14 +804,13 @@ impl num::ToStrRadix for f32 {
804804
#[inline]
805805
fn to_str_radix(&self, rdx: uint) -> ~str {
806806
let (r, special) = strconv::float_to_str_common(
807-
*self, rdx, true, strconv::SignNeg, strconv::DigAll);
807+
*self, rdx, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false);
808808
if special { fail!("number has a special value, \
809809
try to_str_radix_special() if those are expected") }
810810
r
811811
}
812812
}
813813

814-
///
815814
/// Convert a string in base 16 to a float.
816815
/// Accepts a optional binary exponent.
817816
///
@@ -837,15 +836,13 @@ impl num::ToStrRadix for f32 {
837836
///
838837
/// `None` if the string did not represent a valid number. Otherwise,
839838
/// `Some(n)` where `n` is the floating-point number represented by `[num]`.
840-
///
841839
#[inline]
842840
pub fn from_str_hex(num: &str) -> Option<f32> {
843841
strconv::from_str_common(num, 16u, true, true, true,
844842
strconv::ExpBin, false, false)
845843
}
846844

847845
impl FromStr for f32 {
848-
///
849846
/// Convert a string in base 10 to a float.
850847
/// Accepts a optional decimal exponent.
851848
///
@@ -871,7 +868,6 @@ impl FromStr for f32 {
871868
///
872869
/// `None` if the string did not represent a valid number. Otherwise,
873870
/// `Some(n)` where `n` is the floating-point number represented by `num`.
874-
///
875871
#[inline]
876872
fn from_str(val: &str) -> Option<f32> {
877873
strconv::from_str_common(val, 10u, true, true, true,
@@ -880,7 +876,6 @@ impl FromStr for f32 {
880876
}
881877

882878
impl num::FromStrRadix for f32 {
883-
///
884879
/// Convert a string in an given base to a float.
885880
///
886881
/// Due to possible conflicts, this function does **not** accept
@@ -898,7 +893,6 @@ impl num::FromStrRadix for f32 {
898893
///
899894
/// `None` if the string did not represent a valid number. Otherwise,
900895
/// `Some(n)` where `n` is the floating-point number represented by `num`.
901-
///
902896
#[inline]
903897
fn from_str_radix(val: &str, rdx: uint) -> Option<f32> {
904898
strconv::from_str_common(val, rdx, true, true, false,

0 commit comments

Comments
 (0)