]> git.proxmox.com Git - rustc.git/blame - src/libcore/fmt/float.rs
Imported Upstream version 1.0.0+dfsg1
[rustc.git] / src / libcore / fmt / float.rs
CommitLineData
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
11pub use self::ExponentFormat::*;
12pub use self::SignificantDigits::*;
9346a6ac
AL
13
14use prelude::*;
1a4d82fc
JJ
15
16use char;
1a4d82fc 17use fmt;
9346a6ac 18use num::Float;
1a4d82fc 19use num::FpCategory as Fp;
9346a6ac
AL
20use ops::{Div, Rem, Mul};
21use slice;
22use str;
1a4d82fc
JJ
23
24/// A flag that specifies whether to use exponential (scientific) notation.
25pub 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.
36pub 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)]
46pub 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
52macro_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}
58doit! { 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.
81pub 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}