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