]>
Commit | Line | Data |
---|---|---|
9346a6ac | 1 | // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
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 | ||
1a4d82fc JJ |
11 | pub use self::ExponentFormat::*; |
12 | pub use self::SignificantDigits::*; | |
9346a6ac AL |
13 | |
14 | use prelude::*; | |
1a4d82fc JJ |
15 | |
16 | use char; | |
1a4d82fc | 17 | use fmt; |
9346a6ac | 18 | use num::Float; |
1a4d82fc | 19 | use num::FpCategory as Fp; |
9346a6ac AL |
20 | use ops::{Div, Rem, Mul}; |
21 | use slice; | |
22 | use str; | |
1a4d82fc JJ |
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 | } | |
33 | ||
34 | /// The number of digits used for emitting the fractional part of a number, if | |
35 | /// any. | |
36 | pub enum SignificantDigits { | |
37 | /// At most the given number of digits will be printed, truncating any | |
38 | /// trailing zeroes. | |
c34b1796 | 39 | DigMax(usize), |
1a4d82fc JJ |
40 | |
41 | /// Precisely the given number of digits will be printed. | |
c34b1796 | 42 | DigExact(usize) |
1a4d82fc JJ |
43 | } |
44 | ||
9346a6ac AL |
45 | #[doc(hidden)] |
46 | pub trait MyFloat: Float + PartialEq + PartialOrd + Div<Output=Self> + | |
47 | Mul<Output=Self> + Rem<Output=Self> + Copy { | |
48 | fn from_u32(u: u32) -> Self; | |
49 | fn to_i32(&self) -> i32; | |
1a4d82fc JJ |
50 | } |
51 | ||
9346a6ac AL |
52 | macro_rules! doit { |
53 | ($($t:ident)*) => ($(impl MyFloat for $t { | |
54 | fn from_u32(u: u32) -> $t { u as $t } | |
55 | fn to_i32(&self) -> i32 { *self as i32 } | |
56 | })*) | |
57 | } | |
58 | doit! { f32 f64 } | |
1a4d82fc | 59 | |
9346a6ac AL |
60 | /// Converts a float number to its string representation. |
61 | /// This is meant to be a common base implementation for various formatting styles. | |
62 | /// The number is assumed to be non-negative, callers use `Formatter::pad_integral` | |
63 | /// to add the right sign, if any. | |
1a4d82fc JJ |
64 | /// |
65 | /// # Arguments | |
66 | /// | |
9346a6ac | 67 | /// - `num` - The number to convert (non-negative). Accepts any number that |
1a4d82fc | 68 | /// implements the numeric traits. |
1a4d82fc JJ |
69 | /// - `digits` - The amount of digits to use for emitting the fractional |
70 | /// part, if any. See `SignificantDigits`. | |
71 | /// - `exp_format` - Whether or not to use the exponential (scientific) notation. | |
72 | /// See `ExponentFormat`. | |
73 | /// - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if | |
74 | /// exponential notation is desired. | |
9346a6ac | 75 | /// - `f` - A closure to invoke with the string representing the |
1a4d82fc JJ |
76 | /// float. |
77 | /// | |
78 | /// # Panics | |
79 | /// | |
9346a6ac AL |
80 | /// - Panics if `num` is negative. |
81 | pub fn float_to_str_bytes_common<T: MyFloat, U, F>( | |
1a4d82fc | 82 | num: T, |
1a4d82fc JJ |
83 | digits: SignificantDigits, |
84 | exp_format: ExponentFormat, | |
85 | exp_upper: bool, | |
86 | f: F | |
87 | ) -> U where | |
88 | F: FnOnce(&str) -> U, | |
89 | { | |
9346a6ac AL |
90 | let _0: T = T::zero(); |
91 | let _1: T = T::one(); | |
92 | let radix: u32 = 10; | |
93 | let radix_f = T::from_u32(radix); | |
1a4d82fc | 94 | |
9346a6ac | 95 | assert!(num.is_nan() || num >= _0, "float_to_str_bytes_common: number is negative"); |
1a4d82fc JJ |
96 | |
97 | match num.classify() { | |
98 | Fp::Nan => return f("NaN"), | |
99 | Fp::Infinite if num > _0 => { | |
100 | return f("inf"); | |
101 | } | |
102 | Fp::Infinite if num < _0 => { | |
103 | return f("-inf"); | |
104 | } | |
105 | _ => {} | |
106 | } | |
107 | ||
9346a6ac AL |
108 | // For an f64 the (decimal) exponent is roughly in the range of [-307, 308], so |
109 | // we may have up to that many digits. We err on the side of caution and | |
110 | // add 50% extra wiggle room. | |
111 | let mut buf = [0; 462]; | |
1a4d82fc | 112 | let mut end = 0; |
1a4d82fc JJ |
113 | |
114 | let (num, exp) = match exp_format { | |
9346a6ac AL |
115 | ExpDec if num != _0 => { |
116 | let exp = num.log10().floor(); | |
117 | (num / radix_f.powf(exp), exp.to_i32()) | |
1a4d82fc | 118 | } |
9346a6ac | 119 | _ => (num, 0) |
1a4d82fc JJ |
120 | }; |
121 | ||
122 | // First emit the non-fractional part, looping at least once to make | |
123 | // sure at least a `0` gets emitted. | |
124 | let mut deccum = num.trunc(); | |
125 | loop { | |
9346a6ac | 126 | let current_digit = deccum % radix_f; |
1a4d82fc JJ |
127 | |
128 | // Decrease the deccumulator one digit at a time | |
9346a6ac | 129 | deccum = deccum / radix_f; |
1a4d82fc JJ |
130 | deccum = deccum.trunc(); |
131 | ||
9346a6ac | 132 | let c = char::from_digit(current_digit.to_i32() as u32, radix); |
1a4d82fc JJ |
133 | buf[end] = c.unwrap() as u8; |
134 | end += 1; | |
135 | ||
136 | // No more digits to calculate for the non-fractional part -> break | |
137 | if deccum == _0 { break; } | |
138 | } | |
139 | ||
140 | // If limited digits, calculate one digit more for rounding. | |
141 | let (limit_digits, digit_count, exact) = match digits { | |
142 | DigMax(count) => (true, count + 1, false), | |
143 | DigExact(count) => (true, count + 1, true) | |
144 | }; | |
145 | ||
85aaf69f | 146 | buf[..end].reverse(); |
1a4d82fc JJ |
147 | |
148 | // Remember start of the fractional digits. | |
149 | // Points one beyond end of buf if none get generated, | |
150 | // or at the '.' otherwise. | |
151 | let start_fractional_digits = end; | |
152 | ||
153 | // Now emit the fractional part, if any | |
154 | deccum = num.fract(); | |
155 | if deccum != _0 || (limit_digits && exact && digit_count > 0) { | |
156 | buf[end] = b'.'; | |
157 | end += 1; | |
85aaf69f | 158 | let mut dig = 0; |
1a4d82fc JJ |
159 | |
160 | // calculate new digits while | |
161 | // - there is no limit and there are digits left | |
162 | // - or there is a limit, it's not reached yet and | |
163 | // - it's exact | |
164 | // - or it's a maximum, and there are still digits left | |
165 | while (!limit_digits && deccum != _0) | |
166 | || (limit_digits && dig < digit_count && ( | |
167 | exact | |
168 | || (!exact && deccum != _0) | |
169 | ) | |
170 | ) { | |
171 | // Shift first fractional digit into the integer part | |
9346a6ac | 172 | deccum = deccum * radix_f; |
1a4d82fc | 173 | |
9346a6ac | 174 | let current_digit = deccum.trunc(); |
1a4d82fc | 175 | |
9346a6ac | 176 | let c = char::from_digit(current_digit.to_i32() as u32, radix); |
1a4d82fc JJ |
177 | buf[end] = c.unwrap() as u8; |
178 | end += 1; | |
179 | ||
180 | // Decrease the deccumulator one fractional digit at a time | |
181 | deccum = deccum.fract(); | |
85aaf69f | 182 | dig += 1; |
1a4d82fc JJ |
183 | } |
184 | ||
185 | // If digits are limited, and that limit has been reached, | |
186 | // cut off the one extra digit, and depending on its value | |
187 | // round the remaining ones. | |
188 | if limit_digits && dig == digit_count { | |
85aaf69f | 189 | let ascii2value = |chr: u8| { |
1a4d82fc JJ |
190 | (chr as char).to_digit(radix).unwrap() |
191 | }; | |
85aaf69f | 192 | let value2ascii = |val: u32| { |
1a4d82fc JJ |
193 | char::from_digit(val, radix).unwrap() as u8 |
194 | }; | |
195 | ||
196 | let extra_digit = ascii2value(buf[end - 1]); | |
197 | end -= 1; | |
198 | if extra_digit >= radix / 2 { // -> need to round | |
c34b1796 | 199 | let mut i: isize = end as isize - 1; |
1a4d82fc JJ |
200 | loop { |
201 | // If reached left end of number, have to | |
202 | // insert additional digit: | |
203 | if i < 0 | |
c34b1796 AL |
204 | || buf[i as usize] == b'-' |
205 | || buf[i as usize] == b'+' { | |
9346a6ac | 206 | for j in ((i + 1) as usize..end).rev() { |
1a4d82fc JJ |
207 | buf[j + 1] = buf[j]; |
208 | } | |
c34b1796 | 209 | buf[(i + 1) as usize] = value2ascii(1); |
1a4d82fc JJ |
210 | end += 1; |
211 | break; | |
212 | } | |
213 | ||
214 | // Skip the '.' | |
c34b1796 | 215 | if buf[i as usize] == b'.' { i -= 1; continue; } |
1a4d82fc JJ |
216 | |
217 | // Either increment the digit, | |
218 | // or set to 0 if max and carry the 1. | |
c34b1796 | 219 | let current_digit = ascii2value(buf[i as usize]); |
1a4d82fc | 220 | if current_digit < (radix - 1) { |
c34b1796 | 221 | buf[i as usize] = value2ascii(current_digit+1); |
1a4d82fc JJ |
222 | break; |
223 | } else { | |
c34b1796 | 224 | buf[i as usize] = value2ascii(0); |
1a4d82fc JJ |
225 | i -= 1; |
226 | } | |
227 | } | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | // if number of digits is not exact, remove all trailing '0's up to | |
233 | // and including the '.' | |
234 | if !exact { | |
235 | let buf_max_i = end - 1; | |
236 | ||
237 | // index to truncate from | |
238 | let mut i = buf_max_i; | |
239 | ||
240 | // discover trailing zeros of fractional part | |
241 | while i > start_fractional_digits && buf[i] == b'0' { | |
242 | i -= 1; | |
243 | } | |
244 | ||
245 | // Only attempt to truncate digits if buf has fractional digits | |
246 | if i >= start_fractional_digits { | |
247 | // If buf ends with '.', cut that too. | |
248 | if buf[i] == b'.' { i -= 1 } | |
249 | ||
250 | // only resize buf if we actually remove digits | |
251 | if i < buf_max_i { | |
252 | end = i + 1; | |
253 | } | |
254 | } | |
255 | } // If exact and trailing '.', just cut that | |
256 | else { | |
257 | let max_i = end - 1; | |
258 | if buf[max_i] == b'.' { | |
259 | end = max_i; | |
260 | } | |
261 | } | |
262 | ||
263 | match exp_format { | |
264 | ExpNone => {}, | |
9346a6ac AL |
265 | ExpDec => { |
266 | buf[end] = if exp_upper { b'E' } else { b'e' }; | |
1a4d82fc JJ |
267 | end += 1; |
268 | ||
269 | struct Filler<'a> { | |
270 | buf: &'a mut [u8], | |
c34b1796 | 271 | end: &'a mut usize, |
1a4d82fc JJ |
272 | } |
273 | ||
85aaf69f | 274 | impl<'a> fmt::Write for Filler<'a> { |
1a4d82fc | 275 | fn write_str(&mut self, s: &str) -> fmt::Result { |
c34b1796 AL |
276 | slice::bytes::copy_memory(s.as_bytes(), |
277 | &mut self.buf[(*self.end)..]); | |
1a4d82fc JJ |
278 | *self.end += s.len(); |
279 | Ok(()) | |
280 | } | |
281 | } | |
282 | ||
283 | let mut filler = Filler { buf: &mut buf, end: &mut end }; | |
9346a6ac | 284 | let _ = fmt::write(&mut filler, format_args!("{:-}", exp)); |
1a4d82fc JJ |
285 | } |
286 | } | |
287 | ||
85aaf69f | 288 | f(unsafe { str::from_utf8_unchecked(&buf[..end]) }) |
1a4d82fc | 289 | } |