Skip to content

Commit 59d7dfa

Browse files
committed
---
yaml --- r: 151748 b: refs/heads/try2 c: bcab97a h: refs/heads/master v: v3
1 parent 1b70e88 commit 59d7dfa

File tree

2 files changed

+363
-1
lines changed

2 files changed

+363
-1
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ refs/heads/snap-stage3: 78a7676898d9f80ab540c6df5d4c9ce35bb50463
55
refs/heads/try: 519addf6277dbafccbb4159db4b710c37eaa2ec5
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
77
refs/heads/ndm: f3868061cd7988080c30d6d5bf352a5a5fe2460b
8-
refs/heads/try2: 1de4b65d2a88e88201026485f9622916c5717555
8+
refs/heads/try2: bcab97a32eca0bec431ff3d1065f07e10c600d80
99
refs/heads/dist-snap: ba4081a5a8573875fed17545846f6f6902c8ba8d
1010
refs/tags/release-0.2: c870d2dffb391e14efb05aa27898f1f6333a9596
1111
refs/tags/release-0.3: b5f0d0f648d9a6153664837026ba1be43d3e2503
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
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_doc)]
12+
13+
use char;
14+
use container::Container;
15+
use fmt;
16+
use iter::{Iterator, range, DoubleEndedIterator};
17+
use num::{Float, FPNaN, FPInfinite, ToPrimitive, Primitive};
18+
use num::{Zero, One, cast};
19+
use option::{None, Some};
20+
use result::Ok;
21+
use slice::{ImmutableVector, MutableVector};
22+
use slice;
23+
use str::StrSlice;
24+
25+
/// A flag that specifies whether to use exponential (scientific) notation.
26+
pub enum ExponentFormat {
27+
/// Do not use exponential notation.
28+
ExpNone,
29+
/// Use exponential notation with the exponent having a base of 10 and the
30+
/// exponent sign being `e` or `E`. For example, 1000 would be printed
31+
/// 1e3.
32+
ExpDec,
33+
/// Use exponential notation with the exponent having a base of 2 and the
34+
/// exponent sign being `p` or `P`. For example, 8 would be printed 1p3.
35+
ExpBin,
36+
}
37+
38+
/// The number of digits used for emitting the fractional part of a number, if
39+
/// any.
40+
pub enum SignificantDigits {
41+
/// All calculable digits will be printed.
42+
///
43+
/// Note that bignums or fractions may cause a surprisingly large number
44+
/// of digits to be printed.
45+
DigAll,
46+
47+
/// At most the given number of digits will be printed, truncating any
48+
/// trailing zeroes.
49+
DigMax(uint),
50+
51+
/// Precisely the given number of digits will be printed.
52+
DigExact(uint)
53+
}
54+
55+
/// How to emit the sign of a number.
56+
pub enum SignFormat {
57+
/// No sign will be printed. The exponent sign will also be emitted.
58+
SignNone,
59+
/// `-` will be printed for negative values, but no sign will be emitted
60+
/// for positive numbers.
61+
SignNeg,
62+
/// `+` will be printed for positive values, and `-` will be printed for
63+
/// negative values.
64+
SignAll,
65+
}
66+
67+
static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
68+
static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
69+
70+
/**
71+
* Converts a number to its string representation as a byte vector.
72+
* This is meant to be a common base implementation for all numeric string
73+
* conversion functions like `to_str()` or `to_str_radix()`.
74+
*
75+
* # Arguments
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. If the exponential notation
79+
* is used, then this base is only used for the significand. The exponent
80+
* itself always printed using a base of 10.
81+
* - `negative_zero` - Whether to treat the special value `-0` as
82+
* `-0` or as `+0`.
83+
* - `sign` - How to emit the sign. See `SignFormat`.
84+
* - `digits` - The amount of digits to use for emitting the fractional
85+
* part, if any. See `SignificantDigits`.
86+
* - `exp_format` - Whether or not to use the exponential (scientific) notation.
87+
* See `ExponentFormat`.
88+
* - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if
89+
* exponential notation is desired.
90+
* - `f` - A closure to invoke with the bytes representing the
91+
* float.
92+
*
93+
* # Failure
94+
* - Fails if `radix` < 2 or `radix` > 36.
95+
* - Fails if `radix` > 14 and `exp_format` is `ExpDec` due to conflict
96+
* between digit and exponent sign `'e'`.
97+
* - Fails if `radix` > 25 and `exp_format` is `ExpBin` due to conflict
98+
* between digit and exponent sign `'p'`.
99+
*/
100+
pub fn float_to_str_bytes_common<T: Primitive + Float, U>(
101+
num: T,
102+
radix: uint,
103+
negative_zero: bool,
104+
sign: SignFormat,
105+
digits: SignificantDigits,
106+
exp_format: ExponentFormat,
107+
exp_upper: bool,
108+
f: |&[u8]| -> U
109+
) -> U {
110+
assert!(2 <= radix && radix <= 36);
111+
match exp_format {
112+
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
113+
=> fail!("float_to_str_bytes_common: radix {} incompatible with \
114+
use of 'e' as decimal exponent", radix),
115+
ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p'
116+
=> fail!("float_to_str_bytes_common: radix {} incompatible with \
117+
use of 'p' as binary exponent", radix),
118+
_ => ()
119+
}
120+
121+
let _0: T = Zero::zero();
122+
let _1: T = One::one();
123+
124+
match num.classify() {
125+
FPNaN => return f("NaN".as_bytes()),
126+
FPInfinite if num > _0 => {
127+
return match sign {
128+
SignAll => return f("+inf".as_bytes()),
129+
_ => return f("inf".as_bytes()),
130+
};
131+
}
132+
FPInfinite if num < _0 => {
133+
return match sign {
134+
SignNone => return f("inf".as_bytes()),
135+
_ => return f("-inf".as_bytes()),
136+
};
137+
}
138+
_ => {}
139+
}
140+
141+
let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity());
142+
// For an f64 the exponent is in the range of [-1022, 1023] for base 2, so
143+
// we may have up to that many digits. Give ourselves some extra wiggle room
144+
// otherwise as well.
145+
let mut buf = [0u8, ..1536];
146+
let mut end = 0;
147+
let radix_gen: T = cast(radix as int).unwrap();
148+
149+
let (num, exp) = match exp_format {
150+
ExpNone => (num, 0i32),
151+
ExpDec | ExpBin if num == _0 => (num, 0i32),
152+
ExpDec | ExpBin => {
153+
let (exp, exp_base) = match exp_format {
154+
ExpDec => (num.abs().log10().floor(), cast::<f64, T>(10.0f64).unwrap()),
155+
ExpBin => (num.abs().log2().floor(), cast::<f64, T>(2.0f64).unwrap()),
156+
ExpNone => fail!("unreachable"),
157+
};
158+
159+
(num / exp_base.powf(exp), cast::<T, i32>(exp).unwrap())
160+
}
161+
};
162+
163+
// First emit the non-fractional part, looping at least once to make
164+
// sure at least a `0` gets emitted.
165+
let mut deccum = num.trunc();
166+
loop {
167+
// Calculate the absolute value of each digit instead of only
168+
// doing it once for the whole number because a
169+
// representable negative number doesn't necessary have an
170+
// representable additive inverse of the same type
171+
// (See twos complement). But we assume that for the
172+
// numbers [-35 .. 0] we always have [0 .. 35].
173+
let current_digit = (deccum % radix_gen).abs();
174+
175+
// Decrease the deccumulator one digit at a time
176+
deccum = deccum / radix_gen;
177+
deccum = deccum.trunc();
178+
179+
let c = char::from_digit(current_digit.to_int().unwrap() as uint, radix);
180+
buf[end] = c.unwrap() as u8;
181+
end += 1;
182+
183+
// No more digits to calculate for the non-fractional part -> break
184+
if deccum == _0 { break; }
185+
}
186+
187+
// If limited digits, calculate one digit more for rounding.
188+
let (limit_digits, digit_count, exact) = match digits {
189+
DigAll => (false, 0u, false),
190+
DigMax(count) => (true, count+1, false),
191+
DigExact(count) => (true, count+1, true)
192+
};
193+
194+
// Decide what sign to put in front
195+
match sign {
196+
SignNeg | SignAll if neg => {
197+
buf[end] = '-' as u8;
198+
end += 1;
199+
}
200+
SignAll => {
201+
buf[end] = '+' as u8;
202+
end += 1;
203+
}
204+
_ => ()
205+
}
206+
207+
buf.mut_slice_to(end).reverse();
208+
209+
// Remember start of the fractional digits.
210+
// Points one beyond end of buf if none get generated,
211+
// or at the '.' otherwise.
212+
let start_fractional_digits = end;
213+
214+
// Now emit the fractional part, if any
215+
deccum = num.fract();
216+
if deccum != _0 || (limit_digits && exact && digit_count > 0) {
217+
buf[end] = '.' as u8;
218+
end += 1;
219+
let mut dig = 0u;
220+
221+
// calculate new digits while
222+
// - there is no limit and there are digits left
223+
// - or there is a limit, it's not reached yet and
224+
// - it's exact
225+
// - or it's a maximum, and there are still digits left
226+
while (!limit_digits && deccum != _0)
227+
|| (limit_digits && dig < digit_count && (
228+
exact
229+
|| (!exact && deccum != _0)
230+
)
231+
) {
232+
// Shift first fractional digit into the integer part
233+
deccum = deccum * radix_gen;
234+
235+
// Calculate the absolute value of each digit.
236+
// See note in first loop.
237+
let current_digit = deccum.trunc().abs();
238+
239+
let c = char::from_digit(current_digit.to_int().unwrap() as uint,
240+
radix);
241+
buf[end] = c.unwrap() as u8;
242+
end += 1;
243+
244+
// Decrease the deccumulator one fractional digit at a time
245+
deccum = deccum.fract();
246+
dig += 1u;
247+
}
248+
249+
// If digits are limited, and that limit has been reached,
250+
// cut off the one extra digit, and depending on its value
251+
// round the remaining ones.
252+
if limit_digits && dig == digit_count {
253+
let ascii2value = |chr: u8| {
254+
char::to_digit(chr as char, radix).unwrap()
255+
};
256+
let value2ascii = |val: uint| {
257+
char::from_digit(val, radix).unwrap() as u8
258+
};
259+
260+
let extra_digit = ascii2value(buf[end - 1]);
261+
end -= 1;
262+
if extra_digit >= radix / 2 { // -> need to round
263+
let mut i: int = end as int - 1;
264+
loop {
265+
// If reached left end of number, have to
266+
// insert additional digit:
267+
if i < 0
268+
|| buf[i as uint] == '-' as u8
269+
|| buf[i as uint] == '+' as u8 {
270+
for j in range(i as uint + 1, end).rev() {
271+
buf[j + 1] = buf[j];
272+
}
273+
buf[(i + 1) as uint] = value2ascii(1);
274+
end += 1;
275+
break;
276+
}
277+
278+
// Skip the '.'
279+
if buf[i as uint] == '.' as u8 { i -= 1; continue; }
280+
281+
// Either increment the digit,
282+
// or set to 0 if max and carry the 1.
283+
let current_digit = ascii2value(buf[i as uint]);
284+
if current_digit < (radix - 1) {
285+
buf[i as uint] = value2ascii(current_digit+1);
286+
break;
287+
} else {
288+
buf[i as uint] = value2ascii(0);
289+
i -= 1;
290+
}
291+
}
292+
}
293+
}
294+
}
295+
296+
// if number of digits is not exact, remove all trailing '0's up to
297+
// and including the '.'
298+
if !exact {
299+
let buf_max_i = end - 1;
300+
301+
// index to truncate from
302+
let mut i = buf_max_i;
303+
304+
// discover trailing zeros of fractional part
305+
while i > start_fractional_digits && buf[i] == '0' as u8 {
306+
i -= 1;
307+
}
308+
309+
// Only attempt to truncate digits if buf has fractional digits
310+
if i >= start_fractional_digits {
311+
// If buf ends with '.', cut that too.
312+
if buf[i] == '.' as u8 { i -= 1 }
313+
314+
// only resize buf if we actually remove digits
315+
if i < buf_max_i {
316+
end = i + 1;
317+
}
318+
}
319+
} // If exact and trailing '.', just cut that
320+
else {
321+
let max_i = end - 1;
322+
if buf[max_i] == '.' as u8 {
323+
end = max_i;
324+
}
325+
}
326+
327+
match exp_format {
328+
ExpNone => {},
329+
_ => {
330+
buf[end] = match exp_format {
331+
ExpDec if exp_upper => 'E',
332+
ExpDec if !exp_upper => 'e',
333+
ExpBin if exp_upper => 'P',
334+
ExpBin if !exp_upper => 'p',
335+
_ => fail!("unreachable"),
336+
} as u8;
337+
end += 1;
338+
339+
struct Filler<'a> {
340+
buf: &'a mut [u8],
341+
end: &'a mut uint,
342+
}
343+
344+
impl<'a> fmt::FormatWriter for Filler<'a> {
345+
fn write(&mut self, bytes: &[u8]) -> fmt::Result {
346+
slice::bytes::copy_memory(self.buf.mut_slice_from(*self.end),
347+
bytes);
348+
*self.end += bytes.len();
349+
Ok(())
350+
}
351+
}
352+
353+
let mut filler = Filler { buf: buf, end: &mut end };
354+
match sign {
355+
SignNeg => { let _ = write!(&mut filler, "{:-}", exp); }
356+
SignNone | SignAll => { let _ = write!(&mut filler, "{}", exp); }
357+
}
358+
}
359+
}
360+
361+
f(buf.slice_to(end))
362+
}

0 commit comments

Comments
 (0)