]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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::CharExt; | |
19 | use fmt; | |
c34b1796 | 20 | use iter::Iterator; |
1a4d82fc JJ |
21 | use num::{cast, Float, ToPrimitive}; |
22 | use num::FpCategory as Fp; | |
23 | use ops::FnOnce; | |
24 | use result::Result::Ok; | |
25 | use slice::{self, SliceExt}; | |
26 | use str::{self, 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. | |
c34b1796 | 43 | DigMax(usize), |
1a4d82fc JJ |
44 | |
45 | /// Precisely the given number of digits will be printed. | |
c34b1796 | 46 | DigExact(usize) |
1a4d82fc JJ |
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 | ||
c34b1796 | 56 | const DIGIT_E_RADIX: u32 = ('e' as u32) - ('a' as u32) + 11; |
1a4d82fc JJ |
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, | |
85aaf69f | 90 | radix: u32, |
1a4d82fc JJ |
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. | |
c34b1796 | 126 | let mut buf = [0; 1536]; |
1a4d82fc | 127 | let mut end = 0; |
c34b1796 | 128 | let radix_gen: T = cast(radix as isize).unwrap(); |
1a4d82fc JJ |
129 | |
130 | let (num, exp) = match exp_format { | |
c34b1796 AL |
131 | ExpNone => (num, 0), |
132 | ExpDec if num == _0 => (num, 0), | |
1a4d82fc JJ |
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 | ||
c34b1796 | 159 | let c = char::from_digit(current_digit.to_isize().unwrap() as u32, radix); |
1a4d82fc JJ |
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 | ||
85aaf69f | 182 | buf[..end].reverse(); |
1a4d82fc JJ |
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; | |
85aaf69f | 194 | let mut dig = 0; |
1a4d82fc JJ |
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 | ||
c34b1796 | 214 | let c = char::from_digit(current_digit.to_isize().unwrap() as u32, |
1a4d82fc JJ |
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(); | |
85aaf69f | 221 | dig += 1; |
1a4d82fc JJ |
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 { | |
85aaf69f | 228 | let ascii2value = |chr: u8| { |
1a4d82fc JJ |
229 | (chr as char).to_digit(radix).unwrap() |
230 | }; | |
85aaf69f | 231 | let value2ascii = |val: u32| { |
1a4d82fc JJ |
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 | |
c34b1796 | 238 | let mut i: isize = end as isize - 1; |
1a4d82fc JJ |
239 | loop { |
240 | // If reached left end of number, have to | |
241 | // insert additional digit: | |
242 | if i < 0 | |
c34b1796 AL |
243 | || buf[i as usize] == b'-' |
244 | || buf[i as usize] == b'+' { | |
245 | for j in (i as usize + 1..end).rev() { | |
1a4d82fc JJ |
246 | buf[j + 1] = buf[j]; |
247 | } | |
c34b1796 | 248 | buf[(i + 1) as usize] = value2ascii(1); |
1a4d82fc JJ |
249 | end += 1; |
250 | break; | |
251 | } | |
252 | ||
253 | // Skip the '.' | |
c34b1796 | 254 | if buf[i as usize] == b'.' { i -= 1; continue; } |
1a4d82fc JJ |
255 | |
256 | // Either increment the digit, | |
257 | // or set to 0 if max and carry the 1. | |
c34b1796 | 258 | let current_digit = ascii2value(buf[i as usize]); |
1a4d82fc | 259 | if current_digit < (radix - 1) { |
c34b1796 | 260 | buf[i as usize] = value2ascii(current_digit+1); |
1a4d82fc JJ |
261 | break; |
262 | } else { | |
c34b1796 | 263 | buf[i as usize] = value2ascii(0); |
1a4d82fc JJ |
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], | |
c34b1796 | 314 | end: &'a mut usize, |
1a4d82fc JJ |
315 | } |
316 | ||
85aaf69f | 317 | impl<'a> fmt::Write for Filler<'a> { |
1a4d82fc | 318 | fn write_str(&mut self, s: &str) -> fmt::Result { |
c34b1796 AL |
319 | slice::bytes::copy_memory(s.as_bytes(), |
320 | &mut self.buf[(*self.end)..]); | |
1a4d82fc JJ |
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 | ||
85aaf69f | 335 | f(unsafe { str::from_utf8_unchecked(&buf[..end]) }) |
1a4d82fc | 336 | } |