]> git.proxmox.com Git - rustc.git/blame - src/libcore/fmt/float.rs
Merge tag 'upstream/1.0.0_beta'
[rustc.git] / src / libcore / fmt / float.rs
CommitLineData
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
13pub use self::ExponentFormat::*;
14pub use self::SignificantDigits::*;
15pub use self::SignFormat::*;
16
17use char;
18use char::CharExt;
19use fmt;
c34b1796 20use iter::Iterator;
1a4d82fc
JJ
21use num::{cast, Float, ToPrimitive};
22use num::FpCategory as Fp;
23use ops::FnOnce;
24use result::Result::Ok;
25use slice::{self, SliceExt};
26use str::{self, StrExt};
27
28/// A flag that specifies whether to use exponential (scientific) notation.
29pub 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.
40pub 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.
50pub enum SignFormat {
51 /// `-` will be printed for negative values, but no sign will be emitted
52 /// for positive numbers.
53 SignNeg
54}
55
c34b1796 56const 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'`.
88pub 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}