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