]> git.lizzy.rs Git - rust.git/blob - src/libstd/num/strconv.rs
Add a doctest for the std::string::as_string method.
[rust.git] / src / libstd / num / strconv.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 // ignore-lexer-test FIXME #15679
12
13 #![allow(missing_docs)]
14
15 pub use self::ExponentFormat::*;
16 pub use self::SignificantDigits::*;
17 pub use self::SignFormat::*;
18
19 use char;
20 use char::Char;
21 use num;
22 use num::{Int, Float, FPNaN, FPInfinite, ToPrimitive};
23 use slice::{SlicePrelude, CloneSliceAllocPrelude};
24 use str::StrPrelude;
25 use string::String;
26 use vec::Vec;
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     /// Use exponential notation with the exponent having a base of 2 and the
37     /// exponent sign being `p` or `P`. For example, 8 would be printed 1p3.
38     ExpBin,
39 }
40
41 /// The number of digits used for emitting the fractional part of a number, if
42 /// any.
43 pub enum SignificantDigits {
44     /// All calculable digits will be printed.
45     ///
46     /// Note that bignums or fractions may cause a surprisingly large number
47     /// of digits to be printed.
48     DigAll,
49
50     /// At most the given number of digits will be printed, truncating any
51     /// trailing zeroes.
52     DigMax(uint),
53
54     /// Precisely the given number of digits will be printed.
55     DigExact(uint)
56 }
57
58 /// How to emit the sign of a number.
59 pub enum SignFormat {
60     /// No sign will be printed. The exponent sign will also be emitted.
61     SignNone,
62     /// `-` will be printed for negative values, but no sign will be emitted
63     /// for positive numbers.
64     SignNeg,
65     /// `+` will be printed for positive values, and `-` will be printed for
66     /// negative values.
67     SignAll,
68 }
69
70 /// Converts an integral number to its string representation as a byte vector.
71 /// This is meant to be a common base implementation for all integral string
72 /// conversion functions like `to_string()` or `to_str_radix()`.
73 ///
74 /// # Arguments
75 ///
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.
79 /// - `sign`          - How to emit the sign. Options are:
80 ///     - `SignNone`: No sign at all. Basically emits `abs(num)`.
81 ///     - `SignNeg`:  Only `-` on negative values.
82 ///     - `SignAll`:  Both `+` on positive, and `-` on negative numbers.
83 /// - `f`             - a callback which will be invoked for each ascii character
84 ///                     which composes the string representation of this integer
85 ///
86 /// # Panics
87 ///
88 /// - Panics if `radix` < 2 or `radix` > 36.
89 fn int_to_str_bytes_common<T: Int>(num: T, radix: uint, sign: SignFormat, f: |u8|) {
90     assert!(2 <= radix && radix <= 36);
91
92     let _0: T = Int::zero();
93
94     let neg = num < _0;
95     let radix_gen: T = num::cast(radix).unwrap();
96
97     let mut deccum = num;
98     // This is just for integral types, the largest of which is a u64. The
99     // smallest base that we can have is 2, so the most number of digits we're
100     // ever going to have is 64
101     let mut buf = [0u8, ..64];
102     let mut cur = 0;
103
104     // Loop at least once to make sure at least a `0` gets emitted.
105     loop {
106         // Calculate the absolute value of each digit instead of only
107         // doing it once for the whole number because a
108         // representable negative number doesn't necessary have an
109         // representable additive inverse of the same type
110         // (See twos complement). But we assume that for the
111         // numbers [-35 .. 0] we always have [0 .. 35].
112         let current_digit_signed = deccum % radix_gen;
113         let current_digit = if current_digit_signed < _0 {
114             _0 - current_digit_signed
115         } else {
116             current_digit_signed
117         };
118         buf[cur] = match current_digit.to_u8().unwrap() {
119             i @ 0...9 => b'0' + i,
120             i         => b'a' + (i - 10),
121         };
122         cur += 1;
123
124         deccum = deccum / radix_gen;
125         // No more digits to calculate for the non-fractional part -> break
126         if deccum == _0 { break; }
127     }
128
129     // Decide what sign to put in front
130     match sign {
131         SignNeg | SignAll if neg => { f(b'-'); }
132         SignAll => { f(b'+'); }
133         _ => ()
134     }
135
136     // We built the number in reverse order, so un-reverse it here
137     while cur > 0 {
138         cur -= 1;
139         f(buf[cur]);
140     }
141 }
142
143 /// Converts a number to its string representation as a byte vector.
144 /// This is meant to be a common base implementation for all numeric string
145 /// conversion functions like `to_string()` or `to_str_radix()`.
146 ///
147 /// # Arguments
148 ///
149 /// - `num`           - The number to convert. Accepts any number that
150 ///                     implements the numeric traits.
151 /// - `radix`         - Base to use. Accepts only the values 2-36. If the exponential notation
152 ///                     is used, then this base is only used for the significand. The exponent
153 ///                     itself always printed using a base of 10.
154 /// - `negative_zero` - Whether to treat the special value `-0` as
155 ///                     `-0` or as `+0`.
156 /// - `sign`          - How to emit the sign. See `SignFormat`.
157 /// - `digits`        - The amount of digits to use for emitting the fractional
158 ///                     part, if any. See `SignificantDigits`.
159 /// - `exp_format`   - Whether or not to use the exponential (scientific) notation.
160 ///                    See `ExponentFormat`.
161 /// - `exp_capital`   - Whether or not to use a capital letter for the exponent sign, if
162 ///                     exponential notation is desired.
163 ///
164 /// # Return value
165 ///
166 /// A tuple containing the byte vector, and a boolean flag indicating
167 /// whether it represents a special value like `inf`, `-inf`, `NaN` or not.
168 /// It returns a tuple because there can be ambiguity between a special value
169 /// and a number representation at higher bases.
170 ///
171 /// # Panics
172 ///
173 /// - Panics if `radix` < 2 or `radix` > 36.
174 /// - Panics if `radix` > 14 and `exp_format` is `ExpDec` due to conflict
175 ///   between digit and exponent sign `'e'`.
176 /// - Panics if `radix` > 25 and `exp_format` is `ExpBin` due to conflict
177 ///   between digit and exponent sign `'p'`.
178 pub fn float_to_str_bytes_common<T: Float>(
179         num: T, radix: uint, negative_zero: bool,
180         sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_upper: bool
181         ) -> (Vec<u8>, bool) {
182     assert!(2 <= radix && radix <= 36);
183     match exp_format {
184         ExpDec if radix >= DIGIT_E_RADIX       // decimal exponent 'e'
185           => panic!("float_to_str_bytes_common: radix {} incompatible with \
186                     use of 'e' as decimal exponent", radix),
187         ExpBin if radix >= DIGIT_P_RADIX       // binary exponent 'p'
188           => panic!("float_to_str_bytes_common: radix {} incompatible with \
189                     use of 'p' as binary exponent", radix),
190         _ => ()
191     }
192
193     let _0: T = Float::zero();
194     let _1: T = Float::one();
195
196     match num.classify() {
197         FPNaN => { return (b"NaN".to_vec(), true); }
198         FPInfinite if num > _0 => {
199             return match sign {
200                 SignAll => (b"+inf".to_vec(), true),
201                 _       => (b"inf".to_vec(), true)
202             };
203         }
204         FPInfinite if num < _0 => {
205             return match sign {
206                 SignNone => (b"inf".to_vec(), true),
207                 _        => (b"-inf".to_vec(), true),
208             };
209         }
210         _ => {}
211     }
212
213     let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity());
214     let mut buf = Vec::new();
215     let radix_gen: T = num::cast(radix as int).unwrap();
216
217     let (num, exp) = match exp_format {
218         ExpNone => (num, 0i32),
219         ExpDec | ExpBin => {
220             if num == _0 {
221                 (num, 0i32)
222             } else {
223                 let (exp, exp_base) = match exp_format {
224                     ExpDec => (num.abs().log10().floor(), num::cast::<f64, T>(10.0f64).unwrap()),
225                     ExpBin => (num.abs().log2().floor(), num::cast::<f64, T>(2.0f64).unwrap()),
226                     ExpNone => unreachable!()
227                 };
228
229                 (num / exp_base.powf(exp), num::cast::<T, i32>(exp).unwrap())
230             }
231         }
232     };
233
234     // First emit the non-fractional part, looping at least once to make
235     // sure at least a `0` gets emitted.
236     let mut deccum = num.trunc();
237     loop {
238         // Calculate the absolute value of each digit instead of only
239         // doing it once for the whole number because a
240         // representable negative number doesn't necessary have an
241         // representable additive inverse of the same type
242         // (See twos complement). But we assume that for the
243         // numbers [-35 .. 0] we always have [0 .. 35].
244         let current_digit = (deccum % radix_gen).abs();
245
246         // Decrease the deccumulator one digit at a time
247         deccum = deccum / radix_gen;
248         deccum = deccum.trunc();
249
250         buf.push(char::from_digit(current_digit.to_int().unwrap() as uint, radix)
251              .unwrap() as u8);
252
253         // No more digits to calculate for the non-fractional part -> break
254         if deccum == _0 { break; }
255     }
256
257     // If limited digits, calculate one digit more for rounding.
258     let (limit_digits, digit_count, exact) = match digits {
259         DigAll          => (false, 0u,      false),
260         DigMax(count)   => (true,  count+1, false),
261         DigExact(count) => (true,  count+1, true)
262     };
263
264     // Decide what sign to put in front
265     match sign {
266         SignNeg | SignAll if neg => {
267             buf.push(b'-');
268         }
269         SignAll => {
270             buf.push(b'+');
271         }
272         _ => ()
273     }
274
275     buf.reverse();
276
277     // Remember start of the fractional digits.
278     // Points one beyond end of buf if none get generated,
279     // or at the '.' otherwise.
280     let start_fractional_digits = buf.len();
281
282     // Now emit the fractional part, if any
283     deccum = num.fract();
284     if deccum != _0 || (limit_digits && exact && digit_count > 0) {
285         buf.push(b'.');
286         let mut dig = 0u;
287
288         // calculate new digits while
289         // - there is no limit and there are digits left
290         // - or there is a limit, it's not reached yet and
291         //   - it's exact
292         //   - or it's a maximum, and there are still digits left
293         while (!limit_digits && deccum != _0)
294            || (limit_digits && dig < digit_count && (
295                    exact
296                 || (!exact && deccum != _0)
297               )
298         ) {
299             // Shift first fractional digit into the integer part
300             deccum = deccum * radix_gen;
301
302             // Calculate the absolute value of each digit.
303             // See note in first loop.
304             let current_digit = deccum.trunc().abs();
305
306             buf.push(char::from_digit(
307                 current_digit.to_int().unwrap() as uint, radix).unwrap() as u8);
308
309             // Decrease the deccumulator one fractional digit at a time
310             deccum = deccum.fract();
311             dig += 1u;
312         }
313
314         // If digits are limited, and that limit has been reached,
315         // cut off the one extra digit, and depending on its value
316         // round the remaining ones.
317         if limit_digits && dig == digit_count {
318             let ascii2value = |chr: u8| {
319                 (chr as char).to_digit(radix).unwrap()
320             };
321             let value2ascii = |val: uint| {
322                 char::from_digit(val, radix).unwrap() as u8
323             };
324
325             let extra_digit = ascii2value(buf.pop().unwrap());
326             if extra_digit >= radix / 2 { // -> need to round
327                 let mut i: int = buf.len() as int - 1;
328                 loop {
329                     // If reached left end of number, have to
330                     // insert additional digit:
331                     if i < 0
332                     || buf[i as uint] == b'-'
333                     || buf[i as uint] == b'+' {
334                         buf.insert((i + 1) as uint, value2ascii(1));
335                         break;
336                     }
337
338                     // Skip the '.'
339                     if buf[i as uint] == b'.' { i -= 1; continue; }
340
341                     // Either increment the digit,
342                     // or set to 0 if max and carry the 1.
343                     let current_digit = ascii2value(buf[i as uint]);
344                     if current_digit < (radix - 1) {
345                         buf[i as uint] = value2ascii(current_digit+1);
346                         break;
347                     } else {
348                         buf[i as uint] = value2ascii(0);
349                         i -= 1;
350                     }
351                 }
352             }
353         }
354     }
355
356     // if number of digits is not exact, remove all trailing '0's up to
357     // and including the '.'
358     if !exact {
359         let buf_max_i = buf.len() - 1;
360
361         // index to truncate from
362         let mut i = buf_max_i;
363
364         // discover trailing zeros of fractional part
365         while i > start_fractional_digits && buf[i] == b'0' {
366             i -= 1;
367         }
368
369         // Only attempt to truncate digits if buf has fractional digits
370         if i >= start_fractional_digits {
371             // If buf ends with '.', cut that too.
372             if buf[i] == b'.' { i -= 1 }
373
374             // only resize buf if we actually remove digits
375             if i < buf_max_i {
376                 buf = buf.slice(0, i + 1).to_vec();
377             }
378         }
379     } // If exact and trailing '.', just cut that
380     else {
381         let max_i = buf.len() - 1;
382         if buf[max_i] == b'.' {
383             buf = buf.slice(0, max_i).to_vec();
384         }
385     }
386
387     match exp_format {
388         ExpNone => (),
389         _ => {
390             buf.push(match exp_format {
391                 ExpDec if exp_upper => 'E',
392                 ExpDec if !exp_upper => 'e',
393                 ExpBin if exp_upper => 'P',
394                 ExpBin if !exp_upper => 'p',
395                 _ => unreachable!()
396             } as u8);
397
398             int_to_str_bytes_common(exp, 10, sign, |c| buf.push(c));
399         }
400     }
401
402     (buf, false)
403 }
404
405 /**
406  * Converts a number to its string representation. This is a wrapper for
407  * `to_str_bytes_common()`, for details see there.
408  */
409 #[inline]
410 pub fn float_to_str_common<T: Float>(
411         num: T, radix: uint, negative_zero: bool,
412         sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_capital: bool
413         ) -> (String, bool) {
414     let (bytes, special) = float_to_str_bytes_common(num, radix,
415                                negative_zero, sign, digits, exp_format, exp_capital);
416     (String::from_utf8(bytes).unwrap(), special)
417 }
418
419 // Some constants for from_str_bytes_common's input validation,
420 // they define minimum radix values for which the character is a valid digit.
421 static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
422 static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
423
424 #[cfg(test)]
425 mod tests {
426     use string::ToString;
427
428     #[test]
429     fn test_int_to_str_overflow() {
430         let mut i8_val: i8 = 127_i8;
431         assert_eq!(i8_val.to_string(), "127".to_string());
432
433         i8_val += 1 as i8;
434         assert_eq!(i8_val.to_string(), "-128".to_string());
435
436         let mut i16_val: i16 = 32_767_i16;
437         assert_eq!(i16_val.to_string(), "32767".to_string());
438
439         i16_val += 1 as i16;
440         assert_eq!(i16_val.to_string(), "-32768".to_string());
441
442         let mut i32_val: i32 = 2_147_483_647_i32;
443         assert_eq!(i32_val.to_string(), "2147483647".to_string());
444
445         i32_val += 1 as i32;
446         assert_eq!(i32_val.to_string(), "-2147483648".to_string());
447
448         let mut i64_val: i64 = 9_223_372_036_854_775_807_i64;
449         assert_eq!(i64_val.to_string(), "9223372036854775807".to_string());
450
451         i64_val += 1 as i64;
452         assert_eq!(i64_val.to_string(), "-9223372036854775808".to_string());
453     }
454 }
455
456 #[cfg(test)]
457 mod bench {
458     extern crate test;
459
460     mod uint {
461         use super::test::Bencher;
462         use rand::{weak_rng, Rng};
463         use std::fmt;
464
465         #[inline]
466         fn to_string(x: uint, base: u8) {
467             format!("{}", fmt::radix(x, base));
468         }
469
470         #[bench]
471         fn to_str_bin(b: &mut Bencher) {
472             let mut rng = weak_rng();
473             b.iter(|| { to_string(rng.gen::<uint>(), 2); })
474         }
475
476         #[bench]
477         fn to_str_oct(b: &mut Bencher) {
478             let mut rng = weak_rng();
479             b.iter(|| { to_string(rng.gen::<uint>(), 8); })
480         }
481
482         #[bench]
483         fn to_str_dec(b: &mut Bencher) {
484             let mut rng = weak_rng();
485             b.iter(|| { to_string(rng.gen::<uint>(), 10); })
486         }
487
488         #[bench]
489         fn to_str_hex(b: &mut Bencher) {
490             let mut rng = weak_rng();
491             b.iter(|| { to_string(rng.gen::<uint>(), 16); })
492         }
493
494         #[bench]
495         fn to_str_base_36(b: &mut Bencher) {
496             let mut rng = weak_rng();
497             b.iter(|| { to_string(rng.gen::<uint>(), 36); })
498         }
499     }
500
501     mod int {
502         use super::test::Bencher;
503         use rand::{weak_rng, Rng};
504         use std::fmt;
505
506         #[inline]
507         fn to_string(x: int, base: u8) {
508             format!("{}", fmt::radix(x, base));
509         }
510
511         #[bench]
512         fn to_str_bin(b: &mut Bencher) {
513             let mut rng = weak_rng();
514             b.iter(|| { to_string(rng.gen::<int>(), 2); })
515         }
516
517         #[bench]
518         fn to_str_oct(b: &mut Bencher) {
519             let mut rng = weak_rng();
520             b.iter(|| { to_string(rng.gen::<int>(), 8); })
521         }
522
523         #[bench]
524         fn to_str_dec(b: &mut Bencher) {
525             let mut rng = weak_rng();
526             b.iter(|| { to_string(rng.gen::<int>(), 10); })
527         }
528
529         #[bench]
530         fn to_str_hex(b: &mut Bencher) {
531             let mut rng = weak_rng();
532             b.iter(|| { to_string(rng.gen::<int>(), 16); })
533         }
534
535         #[bench]
536         fn to_str_base_36(b: &mut Bencher) {
537             let mut rng = weak_rng();
538             b.iter(|| { to_string(rng.gen::<int>(), 36); })
539         }
540     }
541
542     mod f64 {
543         use super::test::Bencher;
544         use rand::{weak_rng, Rng};
545         use f64;
546
547         #[bench]
548         fn float_to_string(b: &mut Bencher) {
549             let mut rng = weak_rng();
550             b.iter(|| { f64::to_string(rng.gen()); })
551         }
552     }
553 }