|
| 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