]> git.lizzy.rs Git - rust.git/blob - src/libcore/fmt/float.rs
doc: remove incomplete sentence
[rust.git] / src / libcore / fmt / float.rs
1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![allow(missing_docs)]
12
13 pub use self::ExponentFormat::*;
14 pub use self::SignificantDigits::*;
15 pub use self::SignFormat::*;
16
17 use char;
18 use char::Char;
19 use fmt;
20 use iter::{IteratorExt, range};
21 use num::{cast, Float, ToPrimitive};
22 use num::FpCategory as Fp;
23 use ops::FnOnce;
24 use result::Result::Ok;
25 use slice::{mod, SliceExt};
26 use str::{mod, StrExt};
27
28 /// A flag that specifies whether to use exponential (scientific) notation.
29 pub enum ExponentFormat {
30     /// Do not use exponential notation.
31     ExpNone,
32     /// Use exponential notation with the exponent having a base of 10 and the
33     /// exponent sign being `e` or `E`. For example, 1000 would be printed
34     /// 1e3.
35     ExpDec
36 }
37
38 /// The number of digits used for emitting the fractional part of a number, if
39 /// any.
40 pub enum SignificantDigits {
41     /// At most the given number of digits will be printed, truncating any
42     /// trailing zeroes.
43     DigMax(uint),
44
45     /// Precisely the given number of digits will be printed.
46     DigExact(uint)
47 }
48
49 /// How to emit the sign of a number.
50 pub enum SignFormat {
51     /// `-` will be printed for negative values, but no sign will be emitted
52     /// for positive numbers.
53     SignNeg
54 }
55
56 static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
57
58 /// Converts a number to its string representation as a byte vector.
59 /// This is meant to be a common base implementation for all numeric string
60 /// conversion functions like `to_string()` or `to_str_radix()`.
61 ///
62 /// # Arguments
63 ///
64 /// - `num`           - The number to convert. Accepts any number that
65 ///                     implements the numeric traits.
66 /// - `radix`         - Base to use. Accepts only the values 2-36. If the exponential notation
67 ///                     is used, then this base is only used for the significand. The exponent
68 ///                     itself always printed using a base of 10.
69 /// - `negative_zero` - Whether to treat the special value `-0` as
70 ///                     `-0` or as `+0`.
71 /// - `sign`          - How to emit the sign. See `SignFormat`.
72 /// - `digits`        - The amount of digits to use for emitting the fractional
73 ///                     part, if any. See `SignificantDigits`.
74 /// - `exp_format`   - Whether or not to use the exponential (scientific) notation.
75 ///                    See `ExponentFormat`.
76 /// - `exp_capital`   - Whether or not to use a capital letter for the exponent sign, if
77 ///                     exponential notation is desired.
78 /// - `f`             - A closure to invoke with the bytes representing the
79 ///                     float.
80 ///
81 /// # Panics
82 ///
83 /// - Panics if `radix` < 2 or `radix` > 36.
84 /// - Panics if `radix` > 14 and `exp_format` is `ExpDec` due to conflict
85 ///   between digit and exponent sign `'e'`.
86 /// - Panics if `radix` > 25 and `exp_format` is `ExpBin` due to conflict
87 ///   between digit and exponent sign `'p'`.
88 pub fn float_to_str_bytes_common<T: Float, U, F>(
89     num: T,
90     radix: uint,
91     negative_zero: bool,
92     sign: SignFormat,
93     digits: SignificantDigits,
94     exp_format: ExponentFormat,
95     exp_upper: bool,
96     f: F
97 ) -> U where
98     F: FnOnce(&str) -> U,
99 {
100     assert!(2 <= radix && radix <= 36);
101     match exp_format {
102         ExpDec if radix >= DIGIT_E_RADIX       // decimal exponent 'e'
103           => panic!("float_to_str_bytes_common: radix {} incompatible with \
104                     use of 'e' as decimal exponent", radix),
105         _ => ()
106     }
107
108     let _0: T = Float::zero();
109     let _1: T = Float::one();
110
111     match num.classify() {
112         Fp::Nan => return f("NaN"),
113         Fp::Infinite if num > _0 => {
114             return f("inf");
115         }
116         Fp::Infinite if num < _0 => {
117             return f("-inf");
118         }
119         _ => {}
120     }
121
122     let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity());
123     // For an f64 the exponent is in the range of [-1022, 1023] for base 2, so
124     // we may have up to that many digits. Give ourselves some extra wiggle room
125     // otherwise as well.
126     let mut buf = [0u8; 1536];
127     let mut end = 0;
128     let radix_gen: T = cast(radix as int).unwrap();
129
130     let (num, exp) = match exp_format {
131         ExpNone => (num, 0i32),
132         ExpDec if num == _0 => (num, 0i32),
133         ExpDec => {
134             let (exp, exp_base) = match exp_format {
135                 ExpDec => (num.abs().log10().floor(), cast::<f64, T>(10.0f64).unwrap()),
136                 ExpNone => panic!("unreachable"),
137             };
138
139             (num / exp_base.powf(exp), cast::<T, i32>(exp).unwrap())
140         }
141     };
142
143     // First emit the non-fractional part, looping at least once to make
144     // sure at least a `0` gets emitted.
145     let mut deccum = num.trunc();
146     loop {
147         // Calculate the absolute value of each digit instead of only
148         // doing it once for the whole number because a
149         // representable negative number doesn't necessary have an
150         // representable additive inverse of the same type
151         // (See twos complement). But we assume that for the
152         // numbers [-35 .. 0] we always have [0 .. 35].
153         let current_digit = (deccum % radix_gen).abs();
154
155         // Decrease the deccumulator one digit at a time
156         deccum = deccum / radix_gen;
157         deccum = deccum.trunc();
158
159         let c = char::from_digit(current_digit.to_int().unwrap() as uint, radix);
160         buf[end] = c.unwrap() as u8;
161         end += 1;
162
163         // No more digits to calculate for the non-fractional part -> break
164         if deccum == _0 { break; }
165     }
166
167     // If limited digits, calculate one digit more for rounding.
168     let (limit_digits, digit_count, exact) = match digits {
169         DigMax(count)   => (true, count + 1, false),
170         DigExact(count) => (true, count + 1, true)
171     };
172
173     // Decide what sign to put in front
174     match sign {
175         SignNeg if neg => {
176             buf[end] = b'-';
177             end += 1;
178         }
179         _ => ()
180     }
181
182     buf.slice_to_mut(end).reverse();
183
184     // Remember start of the fractional digits.
185     // Points one beyond end of buf if none get generated,
186     // or at the '.' otherwise.
187     let start_fractional_digits = end;
188
189     // Now emit the fractional part, if any
190     deccum = num.fract();
191     if deccum != _0 || (limit_digits && exact && digit_count > 0) {
192         buf[end] = b'.';
193         end += 1;
194         let mut dig = 0u;
195
196         // calculate new digits while
197         // - there is no limit and there are digits left
198         // - or there is a limit, it's not reached yet and
199         //   - it's exact
200         //   - or it's a maximum, and there are still digits left
201         while (!limit_digits && deccum != _0)
202            || (limit_digits && dig < digit_count && (
203                    exact
204                 || (!exact && deccum != _0)
205               )
206         ) {
207             // Shift first fractional digit into the integer part
208             deccum = deccum * radix_gen;
209
210             // Calculate the absolute value of each digit.
211             // See note in first loop.
212             let current_digit = deccum.trunc().abs();
213
214             let c = char::from_digit(current_digit.to_int().unwrap() as uint,
215                                      radix);
216             buf[end] = c.unwrap() as u8;
217             end += 1;
218
219             // Decrease the deccumulator one fractional digit at a time
220             deccum = deccum.fract();
221             dig += 1u;
222         }
223
224         // If digits are limited, and that limit has been reached,
225         // cut off the one extra digit, and depending on its value
226         // round the remaining ones.
227         if limit_digits && dig == digit_count {
228             let ascii2value = |&: chr: u8| {
229                 (chr as char).to_digit(radix).unwrap()
230             };
231             let value2ascii = |&: val: uint| {
232                 char::from_digit(val, radix).unwrap() as u8
233             };
234
235             let extra_digit = ascii2value(buf[end - 1]);
236             end -= 1;
237             if extra_digit >= radix / 2 { // -> need to round
238                 let mut i: int = end as int - 1;
239                 loop {
240                     // If reached left end of number, have to
241                     // insert additional digit:
242                     if i < 0
243                     || buf[i as uint] == b'-'
244                     || buf[i as uint] == b'+' {
245                         for j in range(i as uint + 1, end).rev() {
246                             buf[j + 1] = buf[j];
247                         }
248                         buf[(i + 1) as uint] = value2ascii(1);
249                         end += 1;
250                         break;
251                     }
252
253                     // Skip the '.'
254                     if buf[i as uint] == b'.' { i -= 1; continue; }
255
256                     // Either increment the digit,
257                     // or set to 0 if max and carry the 1.
258                     let current_digit = ascii2value(buf[i as uint]);
259                     if current_digit < (radix - 1) {
260                         buf[i as uint] = value2ascii(current_digit+1);
261                         break;
262                     } else {
263                         buf[i as uint] = value2ascii(0);
264                         i -= 1;
265                     }
266                 }
267             }
268         }
269     }
270
271     // if number of digits is not exact, remove all trailing '0's up to
272     // and including the '.'
273     if !exact {
274         let buf_max_i = end - 1;
275
276         // index to truncate from
277         let mut i = buf_max_i;
278
279         // discover trailing zeros of fractional part
280         while i > start_fractional_digits && buf[i] == b'0' {
281             i -= 1;
282         }
283
284         // Only attempt to truncate digits if buf has fractional digits
285         if i >= start_fractional_digits {
286             // If buf ends with '.', cut that too.
287             if buf[i] == b'.' { i -= 1 }
288
289             // only resize buf if we actually remove digits
290             if i < buf_max_i {
291                 end = i + 1;
292             }
293         }
294     } // If exact and trailing '.', just cut that
295     else {
296         let max_i = end - 1;
297         if buf[max_i] == b'.' {
298             end = max_i;
299         }
300     }
301
302     match exp_format {
303         ExpNone => {},
304         _ => {
305             buf[end] = match exp_format {
306                 ExpDec if exp_upper => 'E',
307                 ExpDec if !exp_upper => 'e',
308                 _ => panic!("unreachable"),
309             } as u8;
310             end += 1;
311
312             struct Filler<'a> {
313                 buf: &'a mut [u8],
314                 end: &'a mut uint,
315             }
316
317             impl<'a> fmt::Writer for Filler<'a> {
318                 fn write_str(&mut self, s: &str) -> fmt::Result {
319                     slice::bytes::copy_memory(self.buf.slice_from_mut(*self.end),
320                                               s.as_bytes());
321                     *self.end += s.len();
322                     Ok(())
323                 }
324             }
325
326             let mut filler = Filler { buf: &mut buf, end: &mut end };
327             match sign {
328                 SignNeg => {
329                     let _ = fmt::write(&mut filler, format_args!("{:-}", exp));
330                 }
331             }
332         }
333     }
334
335     f(unsafe { str::from_utf8_unchecked(buf[..end]) })
336 }