From c82da7a54b9efb1a0ccbe11de66c71f547bf7db9 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Sun, 19 Apr 2015 14:19:54 +0900 Subject: [PATCH 1/9] core: added core::num::flt2dec for floating-point formatting. This is a fork of the flt2dec portion of rust-strconv [1] with a necessary relicensing (the original code was licensed CC0-1.0). Each module is accompanied with large unit tests, integrated in this commit as coretest::num::flt2dec. This module is added in order to replace the existing core::fmt::float method. The forked revision of rust-strconv is from 2015-04-20, with a commit ID 9adf6d3571c6764a6f240a740c823024f70dc1c7. [1] https://github.com/lifthrasiir/rust-strconv/ --- src/libcore/num/flt2dec/bignum.rs | 355 +++++ src/libcore/num/flt2dec/decoder.rs | 100 ++ src/libcore/num/flt2dec/estimator.rs | 23 + src/libcore/num/flt2dec/mod.rs | 662 ++++++++++ src/libcore/num/flt2dec/strategy/dragon.rs | 327 +++++ src/libcore/num/flt2dec/strategy/grisu.rs | 749 +++++++++++ src/libcore/num/mod.rs | 3 + src/libcoretest/lib.rs | 1 + src/libcoretest/num/flt2dec/bignum.rs | 160 +++ src/libcoretest/num/flt2dec/estimator.rs | 61 + src/libcoretest/num/flt2dec/mod.rs | 1158 +++++++++++++++++ .../num/flt2dec/strategy/dragon.rs | 117 ++ src/libcoretest/num/flt2dec/strategy/grisu.rs | 177 +++ src/libcoretest/num/mod.rs | 2 + 14 files changed, 3895 insertions(+) create mode 100644 src/libcore/num/flt2dec/bignum.rs create mode 100644 src/libcore/num/flt2dec/decoder.rs create mode 100644 src/libcore/num/flt2dec/estimator.rs create mode 100644 src/libcore/num/flt2dec/mod.rs create mode 100644 src/libcore/num/flt2dec/strategy/dragon.rs create mode 100644 src/libcore/num/flt2dec/strategy/grisu.rs create mode 100644 src/libcoretest/num/flt2dec/bignum.rs create mode 100644 src/libcoretest/num/flt2dec/estimator.rs create mode 100644 src/libcoretest/num/flt2dec/mod.rs create mode 100644 src/libcoretest/num/flt2dec/strategy/dragon.rs create mode 100644 src/libcoretest/num/flt2dec/strategy/grisu.rs diff --git a/src/libcore/num/flt2dec/bignum.rs b/src/libcore/num/flt2dec/bignum.rs new file mode 100644 index 0000000000000..449d080dc5cb6 --- /dev/null +++ b/src/libcore/num/flt2dec/bignum.rs @@ -0,0 +1,355 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Custom arbitrary-precision number (bignum) implementation. +//! +//! This is designed to avoid the heap allocation at expense of stack memory. +//! The most used bignum type, `Big32x36`, is limited by 32 × 36 = 1,152 bits +//! and will take at most 152 bytes of stack memory. This is (barely) enough +//! for handling all possible finite `f64` values. +//! +//! In principle it is possible to have multiple bignum types for different +//! inputs, but we don't do so to avoid the code bloat. Each bignum is still +//! tracked for the actual usages, so it normally doesn't matter. + +#![macro_use] + +use prelude::*; +use mem; +use intrinsics; + +/// Arithmetic operations required by bignums. +pub trait FullOps { + /// Returns `(carry', v')` such that `carry' * 2^W + v' = self + other + carry`, + /// where `W` is the number of bits in `Self`. + fn full_add(self, other: Self, carry: bool) -> (bool /*carry*/, Self); + + /// Returns `(carry', v')` such that `carry' * 2^W + v' = self * other + carry`, + /// where `W` is the number of bits in `Self`. + fn full_mul(self, other: Self, carry: Self) -> (Self /*carry*/, Self); + + /// Returns `(carry', v')` such that `carry' * 2^W + v' = self * other + other2 + carry`, + /// where `W` is the number of bits in `Self`. + fn full_mul_add(self, other: Self, other2: Self, carry: Self) -> (Self /*carry*/, Self); + + /// Returns `(quo, rem)` such that `borrow * 2^W + self = quo * other + rem` + /// and `0 <= rem < other`, where `W` is the number of bits in `Self`. + fn full_div_rem(self, other: Self, borrow: Self) -> (Self /*quotient*/, Self /*remainder*/); +} + +macro_rules! impl_full_ops { + ($($ty:ty: add($addfn:path), mul/div($bigty:ident);)*) => ( + $( + impl FullOps for $ty { + fn full_add(self, other: $ty, carry: bool) -> (bool, $ty) { + // this cannot overflow, the output is between 0 and 2*2^nbits - 1 + // FIXME will LLVM optimize this into ADC or similar??? + let (v, carry1) = unsafe { $addfn(self, other) }; + let (v, carry2) = unsafe { $addfn(v, if carry {1} else {0}) }; + (carry1 || carry2, v) + } + + fn full_mul(self, other: $ty, carry: $ty) -> ($ty, $ty) { + // this cannot overflow, the output is between 0 and 2^nbits * (2^nbits - 1) + let nbits = mem::size_of::<$ty>() * 8; + let v = (self as $bigty) * (other as $bigty) + (carry as $bigty); + ((v >> nbits) as $ty, v as $ty) + } + + fn full_mul_add(self, other: $ty, other2: $ty, carry: $ty) -> ($ty, $ty) { + // this cannot overflow, the output is between 0 and 2^(2*nbits) - 1 + let nbits = mem::size_of::<$ty>() * 8; + let v = (self as $bigty) * (other as $bigty) + (other2 as $bigty) + + (carry as $bigty); + ((v >> nbits) as $ty, v as $ty) + } + + fn full_div_rem(self, other: $ty, borrow: $ty) -> ($ty, $ty) { + debug_assert!(borrow < other); + // this cannot overflow, the dividend is between 0 and other * 2^nbits - 1 + let nbits = mem::size_of::<$ty>() * 8; + let lhs = ((borrow as $bigty) << nbits) | (self as $bigty); + let rhs = other as $bigty; + ((lhs / rhs) as $ty, (lhs % rhs) as $ty) + } + } + )* + ) +} + +impl_full_ops! { + u8: add(intrinsics::u8_add_with_overflow), mul/div(u16); + u16: add(intrinsics::u16_add_with_overflow), mul/div(u32); + u32: add(intrinsics::u32_add_with_overflow), mul/div(u64); +// u64: add(intrinsics::u64_add_with_overflow), mul/div(u128); // damn! +} + +macro_rules! define_bignum { + ($name:ident: type=$ty:ty, n=$n:expr) => ( + /// Stack-allocated arbitrary-precision (up to certain limit) integer. + /// + /// This is backed by an fixed-size array of given type ("digit"). + /// While the array is not very large (normally some hundred bytes), + /// copying it recklessly may result in the performance hit. + /// Thus this is intentionally not `Copy`. + /// + /// All operations available to bignums panic in the case of over/underflows. + /// The caller is responsible to use large enough bignum types. + pub struct $name { + /// One plus the offset to the maximum "digit" in the use. + /// This does not decrease, so be aware of the computation order. + /// `base[size..]` should be zero. + size: usize, + /// Digits. `[a, b, c, ...]` represents `a + b*n + c*n^2 + ...`. + base: [$ty; $n] + } + + impl $name { + /// Makes a bignum from one digit. + pub fn from_small(v: $ty) -> $name { + let mut base = [0; $n]; + base[0] = v; + $name { size: 1, base: base } + } + + /// Makes a bignum from `u64` value. + pub fn from_u64(mut v: u64) -> $name { + use mem; + + let mut base = [0; $n]; + let mut sz = 0; + while v > 0 { + base[sz] = v as $ty; + v >>= mem::size_of::<$ty>() * 8; + sz += 1; + } + $name { size: sz, base: base } + } + + /// Returns true if the bignum is zero. + pub fn is_zero(&self) -> bool { + self.base[..self.size].iter().all(|&v| v == 0) + } + + /// Adds `other` to itself and returns its own mutable reference. + pub fn add<'a>(&'a mut self, other: &$name) -> &'a mut $name { + use cmp; + use num::flt2dec::bignum::FullOps; + + let mut sz = cmp::max(self.size, other.size); + let mut carry = false; + for (a, b) in self.base[..sz].iter_mut().zip(other.base[..sz].iter()) { + let (c, v) = (*a).full_add(*b, carry); + *a = v; + carry = c; + } + if carry { + self.base[sz] = 1; + sz += 1; + } + self.size = sz; + self + } + + /// Subtracts `other` from itself and returns its own mutable reference. + pub fn sub<'a>(&'a mut self, other: &$name) -> &'a mut $name { + use cmp; + use num::flt2dec::bignum::FullOps; + + let sz = cmp::max(self.size, other.size); + let mut noborrow = true; + for (a, b) in self.base[..sz].iter_mut().zip(other.base[..sz].iter()) { + let (c, v) = (*a).full_add(!*b, noborrow); + *a = v; + noborrow = c; + } + assert!(noborrow); + self.size = sz; + self + } + + /// Multiplies itself by a digit-sized `other` and returns its own + /// mutable reference. + pub fn mul_small<'a>(&'a mut self, other: $ty) -> &'a mut $name { + use num::flt2dec::bignum::FullOps; + + let mut sz = self.size; + let mut carry = 0; + for a in self.base[..sz].iter_mut() { + let (c, v) = (*a).full_mul(other, carry); + *a = v; + carry = c; + } + if carry > 0 { + self.base[sz] = carry; + sz += 1; + } + self.size = sz; + self + } + + /// Multiplies itself by `2^bits` and returns its own mutable reference. + pub fn mul_pow2<'a>(&'a mut self, bits: usize) -> &'a mut $name { + use mem; + + let digitbits = mem::size_of::<$ty>() * 8; + let digits = bits / digitbits; + let bits = bits % digitbits; + + assert!(digits < $n); + debug_assert!(self.base[$n-digits..].iter().all(|&v| v == 0)); + debug_assert!(bits == 0 || (self.base[$n-digits-1] >> (digitbits - bits)) == 0); + + // shift by `digits * digitbits` bits + for i in (0..self.size).rev() { + self.base[i+digits] = self.base[i]; + } + for i in 0..digits { + self.base[i] = 0; + } + + // shift by `nbits` bits + let mut sz = self.size + digits; + if bits > 0 { + let last = sz; + let overflow = self.base[last-1] >> (digitbits - bits); + if overflow > 0 { + self.base[last] = overflow; + sz += 1; + } + for i in (digits+1..last).rev() { + self.base[i] = (self.base[i] << bits) | + (self.base[i-1] >> (digitbits - bits)); + } + self.base[digits] <<= bits; + // self.base[..digits] is zero, no need to shift + } + + self.size = sz; + self + } + + /// Multiplies itself by a number described by `other[0] + other[1] * n + + /// other[2] * n^2 + ...` and returns its own mutable reference. + pub fn mul_digits<'a>(&'a mut self, other: &[$ty]) -> &'a mut $name { + // the internal routine. works best when aa.len() <= bb.len(). + fn mul_inner(ret: &mut [$ty; $n], aa: &[$ty], bb: &[$ty]) -> usize { + use num::flt2dec::bignum::FullOps; + + let mut retsz = 0; + for (i, &a) in aa.iter().enumerate() { + if a == 0 { continue; } + let mut sz = bb.len(); + let mut carry = 0; + for (j, &b) in bb.iter().enumerate() { + let (c, v) = a.full_mul_add(b, ret[i + j], carry); + ret[i + j] = v; + carry = c; + } + if carry > 0 { + ret[i + sz] = carry; + sz += 1; + } + if retsz < i + sz { + retsz = i + sz; + } + } + retsz + } + + let mut ret = [0; $n]; + let retsz = if self.size < other.len() { + mul_inner(&mut ret, &self.base[..self.size], other) + } else { + mul_inner(&mut ret, other, &self.base[..self.size]) + }; + self.base = ret; + self.size = retsz; + self + } + + /// Divides itself by a digit-sized `other` and returns its own + /// mutable reference *and* the remainder. + pub fn div_rem_small<'a>(&'a mut self, other: $ty) -> (&'a mut $name, $ty) { + use num::flt2dec::bignum::FullOps; + + assert!(other > 0); + + let sz = self.size; + let mut borrow = 0; + for a in self.base[..sz].iter_mut().rev() { + let (q, r) = (*a).full_div_rem(other, borrow); + *a = q; + borrow = r; + } + (self, borrow) + } + } + + impl ::cmp::PartialEq for $name { + fn eq(&self, other: &$name) -> bool { self.base[..] == other.base[..] } + } + + impl ::cmp::Eq for $name { + } + + impl ::cmp::PartialOrd for $name { + fn partial_cmp(&self, other: &$name) -> ::option::Option<::cmp::Ordering> { + ::option::Option::Some(self.cmp(other)) + } + } + + impl ::cmp::Ord for $name { + fn cmp(&self, other: &$name) -> ::cmp::Ordering { + use cmp::max; + use iter::order; + + let sz = max(self.size, other.size); + let lhs = self.base[..sz].iter().cloned().rev(); + let rhs = other.base[..sz].iter().cloned().rev(); + order::cmp(lhs, rhs) + } + } + + impl ::clone::Clone for $name { + fn clone(&self) -> $name { + $name { size: self.size, base: self.base } + } + } + + impl ::fmt::Debug for $name { + fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { + use mem; + + let sz = if self.size < 1 {1} else {self.size}; + let digitlen = mem::size_of::<$ty>() * 2; + + try!(write!(f, "{:#x}", self.base[sz-1])); + for &v in self.base[..sz-1].iter().rev() { + try!(write!(f, "_{:01$x}", v, digitlen)); + } + ::result::Result::Ok(()) + } + } + ) +} + +/// The digit type for `Big32x36`. +pub type Digit32 = u32; + +define_bignum!(Big32x36: type=Digit32, n=36); + +// this one is used for testing only. +#[doc(hidden)] +pub mod tests { + use prelude::*; + define_bignum!(Big8x3: type=u8, n=3); +} + diff --git a/src/libcore/num/flt2dec/decoder.rs b/src/libcore/num/flt2dec/decoder.rs new file mode 100644 index 0000000000000..4b7d00f80e1f1 --- /dev/null +++ b/src/libcore/num/flt2dec/decoder.rs @@ -0,0 +1,100 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Decodes a floating-point value into individual parts and error ranges. + +use prelude::*; + +use {f32, f64}; +use num::{Float, FpCategory}; + +/// Decoded unsigned finite value, such that: +/// +/// - The original value equals to `mant * 2^exp`. +/// +/// - Any number from `(mant - minus) * 2^exp` to `(mant + plus) * 2^exp` will +/// round to the original value. The range is inclusive only when +/// `inclusive` is true. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Decoded { + /// The scaled mantissa. + pub mant: u64, + /// The lower error range. + pub minus: u64, + /// The upper error range. + pub plus: u64, + /// The shared exponent in base 2. + pub exp: i16, + /// True when the error range is inclusive. + /// + /// In IEEE 754, this is true when the original mantissa was even. + pub inclusive: bool, +} + +/// Decoded unsigned value. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum FullDecoded { + /// Not-a-number. + Nan, + /// Infinities, either positive or negative. + Infinite, + /// Zero, either positive or negative. + Zero, + /// Finite numbers with further decoded fields. + Finite(Decoded), +} + +/// A floating point type which can be `decode`d. +pub trait DecodableFloat: Float + Copy { + /// The minimum positive normalized value. + fn min_pos_norm_value() -> Self; +} + +impl DecodableFloat for f32 { + fn min_pos_norm_value() -> Self { f32::MIN_POSITIVE } +} + +impl DecodableFloat for f64 { + fn min_pos_norm_value() -> Self { f64::MIN_POSITIVE } +} + +/// Returns a sign (true when negative) and `FullDecoded` value +/// from given floating point number. +pub fn decode(v: T) -> (/*negative?*/ bool, FullDecoded) { + let (mant, exp, sign) = v.integer_decode(); + let even = (mant & 1) == 0; + let decoded = match v.classify() { + FpCategory::Nan => FullDecoded::Nan, + FpCategory::Infinite => FullDecoded::Infinite, + FpCategory::Zero => FullDecoded::Zero, + FpCategory::Subnormal => { + // (mant - 2, exp) -- (mant, exp) -- (mant + 2, exp) + // Float::integer_decode always preserves the exponent, + // so the mantissa is scaled for subnormals. + FullDecoded::Finite(Decoded { mant: mant, minus: 1, plus: 1, + exp: exp, inclusive: even }) + } + FpCategory::Normal => { + let minnorm = ::min_pos_norm_value().integer_decode(); + if mant == minnorm.0 && exp == minnorm.1 { + // (maxmant, exp - 1) -- (minnormmant, exp) -- (minnormmant + 1, exp) + // where maxmant = minnormmant * 2 - 1 + FullDecoded::Finite(Decoded { mant: mant << 1, minus: 1, plus: 2, + exp: exp - 1, inclusive: even }) + } else { + // (mant - 1, exp) -- (mant, exp) -- (mant + 1, exp) + FullDecoded::Finite(Decoded { mant: mant << 1, minus: 1, plus: 1, + exp: exp - 1, inclusive: even }) + } + } + }; + (sign < 0, decoded) +} + diff --git a/src/libcore/num/flt2dec/estimator.rs b/src/libcore/num/flt2dec/estimator.rs new file mode 100644 index 0000000000000..d3e06e406dbea --- /dev/null +++ b/src/libcore/num/flt2dec/estimator.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The exponent estimator. + +/// Finds `k_0` such that `10^(k_0-1) < mant * 2^exp <= 10^(k_0+1)`. +/// +/// This is used to approximate `k = ceil(log_10 (mant * 2^exp))`; +/// the true `k` is either `k_0` or `k_0+1`. +#[doc(hidden)] +pub fn estimate_scaling_factor(mant: u64, exp: i16) -> i16 { + // 2^(nbits-1) < mant <= 2^nbits if mant > 0 + let nbits = 64 - (mant - 1).leading_zeros() as i64; + (((nbits + exp as i64) * 1292913986) >> 32) as i16 +} + diff --git a/src/libcore/num/flt2dec/mod.rs b/src/libcore/num/flt2dec/mod.rs new file mode 100644 index 0000000000000..8b4b3a45e9b33 --- /dev/null +++ b/src/libcore/num/flt2dec/mod.rs @@ -0,0 +1,662 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + +Floating-point number to decimal conversion routines. + +# Problem statement + +We are given the floating-point number `v = f * 2^e` with an integer `f`, +and its bounds `minus` and `plus` such that any number between `v - minus` and +`v + plus` will be rounded to `v`. For the simplicity we assume that +this range is exclusive. Then we would like to get the unique decimal +representation `V = 0.d[0..n-1] * 10^k` such that: + +- `d[0]` is non-zero. + +- It's correctly rounded when parsed back: `v - minus < V < v + plus`. + Furthermore it is shortest such one, i.e. there is no representation + with less than `n` digits that is correctly rounded. + +- It's closest to the original value: `abs(V - v) <= 10^(k-n) / 2`. Note that + there might be two representations satisfying this uniqueness requirement, + in which case some tie-breaking mechanism is used. + +We will call this mode of operation as to the *shortest* mode. This mode is used +when there is no additional constraint, and can be thought as a "natural" mode +as it matches the ordinary intuition (it at least prints `0.1f32` as "0.1"). + +We have two more modes of operation closely related to each other. In these modes +we are given either the number of significant digits `n` or the last-digit +limitation `limit` (which determines the actual `n`), and we would like to get +the representation `V = 0.d[0..n-1] * 10^k` such that: + +- `d[0]` is non-zero, unless `n` was zero in which case only `k` is returned. + +- It's closest to the original value: `abs(V - v) <= 10^(k-n) / 2`. Again, + there might be some tie-breaking mechanism. + +When `limit` is given but not `n`, we set `n` such that `k - n = limit` +so that the last digit `d[n-1]` is scaled by `10^(k-n) = 10^limit`. +If such `n` is negative, we clip it to zero so that we will only get `k`. +We are also limited by the supplied buffer. This limitation is used to print +the number up to given number of fractional digits without knowing +the correct `k` beforehand. + +We will call the mode of operation requiring `n` as to the *exact* mode, +and one requiring `limit` as to the *fixed* mode. The exact mode is a subset of +the fixed mode: the sufficiently large last-digit limitation will eventually fill +the supplied buffer and let the algorithm to return. + +# Implementation overview + +It is easy to get the floating point printing correct but slow (Russ Cox has +[demonstrated](http://research.swtch.com/ftoa) how it's easy), or incorrect but +fast (naïve division and modulo). But it is surprisingly hard to print +floating point numbers correctly *and* efficiently. + +There are two classes of algorithms widely known to be correct. + +- The "Dragon" family of algorithm is first described by Guy L. Steele Jr. and + Jon L. White. They rely on the fixed-size big integer for their correctness. + A slight improvement was found later, which is posthumously described by + Robert G. Burger and R. Kent Dybvig. David Gay's `dtoa.c` routine is + a popular implementation of this strategy. + +- The "Grisu" family of algorithm is first described by Florian Loitsch. + They use very cheap integer-only procedure to determine the close-to-correct + representation which is at least guaranteed to be shortest. The variant, + Grisu3, actively detects if the resulting representation is incorrect. + +We implement both algorithms with necessary tweaks to suit our requirements. +In particular, published literatures are short of the actual implementation +difficulties like how to avoid arithmetic overflows. Each implementation, +available in `strategy::dragon` and `strategy::grisu` respectively, +extensively describes all necessary justifications and many proofs for them. +(It is still difficult to follow though. You have been warned.) + +Both implementations expose two public functions: + +- `format_shortest(decoded, buf)`, which always needs at least + `MAX_SIG_DIGITS` digits of buffer. Implements the shortest mode. + +- `format_exact(decoded, buf, limit)`, which accepts as small as + one digit of buffer. Implements exact and fixed modes. + +They try to fill the `u8` buffer with digits and returns the number of digits +written and the exponent `k`. They are total for all finite `f32` and `f64` +inputs (Grisu internally falls back to Dragon if possible). + +The rendered digits are formatted into the actual string form with +four functions: + +- `to_shortest_str` prints the shortest representation, which can be padded by + zeroes to make *at least* given number of fractional digits. + +- `to_shortest_exp_str` prints the shortest representation, which can be + padded by zeroes when its exponent is in the specified ranges, + or can be printed in the exponential form such as `1.23e45`. + +- `to_exact_exp_str` prints the exact representation with given number of + digits in the exponential form. + +- `to_exact_fixed_str` prints the fixed representation with *exactly* + given number of fractional digits. + +They all return a slice of preallocated `Part` array, which corresponds to +the individual part of strings: a fixed string, a part of rendered digits, +a number of zeroes or a small (`u16`) number. The caller is expected to +provide an enough buffer and `Part` array, and to assemble the final +string from parts itself. + +All algorithms and formatting functions are accompanied by extensive tests +in `coretest::num::flt2dec` module. It also shows how to use individual +functions. + +*/ + +// while this is extensively documented, this is in principle private which is +// only made public for testing. do not expose us. +#![doc(hidden)] + +use prelude::*; +use i16; +use num::Float; +use slice::bytes; +pub use self::decoder::{decode, DecodableFloat, FullDecoded, Decoded}; + +pub mod estimator; +pub mod bignum; +pub mod decoder; + +/// Digit-generation algorithms. +pub mod strategy { + pub mod dragon; + pub mod grisu; +} + +/// The minimum size of buffer necessary for the shortest mode. +/// +/// It is a bit non-trivial to derive, but this is one plus the maximal number of +/// significant decimal digits from formatting algorithms with the shortest result. +/// The exact formula is `ceil(# bits in mantissa * log_10 2 + 1)`. +pub const MAX_SIG_DIGITS: usize = 17; + +/// When `d[..n]` contains decimal digits, increase the last digit and propagate carry. +/// Returns a next digit when it causes the length change. +#[doc(hidden)] +pub fn round_up(d: &mut [u8], n: usize) -> Option { + match d[..n].iter().rposition(|&c| c != b'9') { + Some(i) => { // d[i+1..n] is all nines + d[i] += 1; + for j in i+1..n { d[j] = b'0'; } + None + } + None if n > 0 => { // 999..999 rounds to 1000..000 with an increased exponent + d[0] = b'1'; + for j in 1..n { d[j] = b'0'; } + Some(b'0') + } + None => { // an empty buffer rounds up (a bit strange but reasonable) + Some(b'1') + } + } +} + +/// Formatted parts. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Part<'a> { + /// Given number of zero digits. + Zero(usize), + /// A literal number up to 5 digits. + Num(u16), + /// A verbatim copy of given bytes. + Copy(&'a [u8]), +} + +impl<'a> Part<'a> { + /// Returns the exact byte length of given part. + pub fn len(&self) -> usize { + match *self { + Part::Zero(nzeroes) => nzeroes, + Part::Num(v) => if v < 1_000 { if v < 10 { 1 } else if v < 100 { 2 } else { 3 } } + else { if v < 10_000 { 4 } else { 5 } }, + Part::Copy(buf) => buf.len(), + } + } + + /// Writes a part into the supplied buffer. + /// Returns the number of written bytes, or `None` if the buffer is not enough. + /// (It may still leave partially written bytes in the buffer; do not rely on that.) + pub fn write(&self, out: &mut [u8]) -> Option { + let len = self.len(); + if out.len() >= len { + match *self { + Part::Zero(nzeroes) => { + for c in &mut out[..nzeroes] { *c = b'0'; } + } + Part::Num(mut v) => { + for c in out[..len].iter_mut().rev() { + *c = b'0' + (v % 10) as u8; + v /= 10; + } + } + Part::Copy(buf) => { + bytes::copy_memory(buf, out); + } + } + Some(len) + } else { + None + } + } +} + +/// Formatted result containing one or more parts. +/// This can be written to the byte buffer or converted to the allocated string. +#[derive(Clone)] +pub struct Formatted<'a> { + /// A byte slice representing a sign, either `""`, `"-"` or `"+"`. + pub sign: &'static [u8], + /// Formatted parts to be rendered after a sign and optional zero padding. + pub parts: &'a [Part<'a>], +} + +impl<'a> Formatted<'a> { + /// Returns the exact byte length of combined formatted result. + pub fn len(&self) -> usize { + let mut len = self.sign.len(); + for part in self.parts { + len += part.len(); + } + len + } + + /// Writes all formatted parts into the supplied buffer. + /// Returns the number of written bytes, or `None` if the buffer is not enough. + /// (It may still leave partially written bytes in the buffer; do not rely on that.) + pub fn write(&self, out: &mut [u8]) -> Option { + if out.len() < self.sign.len() { return None; } + bytes::copy_memory(self.sign, out); + + let mut written = self.sign.len(); + for part in self.parts { + match part.write(&mut out[written..]) { + Some(len) => { written += len; } + None => { return None; } + } + } + Some(written) + } +} + +/// Formats given decimal digits `0.<...buf...> * 10^exp` into the decimal form +/// with at least given number of fractional digits. The result is stored to +/// the supplied parts array and a slice of written parts is returned. +/// +/// `frac_digits` can be less than the number of actual fractional digits in `buf`; +/// it will be ignored and full digits will be printed. It is only used to print +/// additional zeroes after rendered digits. Thus `frac_digits` of 0 means that +/// it will only print given digits and nothing else. +fn digits_to_dec_str<'a>(buf: &'a [u8], exp: i16, frac_digits: usize, + parts: &'a mut [Part<'a>]) -> &'a [Part<'a>] { + assert!(!buf.is_empty()); + assert!(buf[0] > b'0'); + assert!(parts.len() >= 4); + + // if there is the restriction on the last digit position, `buf` is assumed to be + // left-padded with the virtual zeroes. the number of virtual zeroes, `nzeroes`, + // equals to `max(0, exp + frag_digits - buf.len())`, so that the position of + // the last digit `exp - buf.len() - nzeroes` is no more than `-frac_digits`: + // + // |<-virtual->| + // |<---- buf ---->| zeroes | exp + // 0. 1 2 3 4 5 6 7 8 9 _ _ _ _ _ _ x 10 + // | | | + // 10^exp 10^(exp-buf.len()) 10^(exp-buf.len()-nzeroes) + // + // `nzeroes` is individually calculated for each case in order to avoid overflow. + + if exp <= 0 { + // the decimal point is before rendered digits: [0.][000...000][1234][____] + let minus_exp = -(exp as i32) as usize; + parts[0] = Part::Copy(b"0."); + parts[1] = Part::Zero(minus_exp); + parts[2] = Part::Copy(buf); + if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { + parts[3] = Part::Zero((frac_digits - buf.len()) - minus_exp); + &parts[..4] + } else { + &parts[..3] + } + } else { + let exp = exp as usize; + if exp < buf.len() { + // the decimal point is inside rendered digits: [12][.][34][____] + parts[0] = Part::Copy(&buf[..exp]); + parts[1] = Part::Copy(b"."); + parts[2] = Part::Copy(&buf[exp..]); + if frac_digits > buf.len() - exp { + parts[3] = Part::Zero(frac_digits - (buf.len() - exp)); + &parts[..4] + } else { + &parts[..3] + } + } else { + // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. + parts[0] = Part::Copy(buf); + parts[1] = Part::Zero(exp - buf.len()); + if frac_digits > 0 { + parts[2] = Part::Copy(b"."); + parts[3] = Part::Zero(frac_digits); + &parts[..4] + } else { + &parts[..2] + } + } + } +} + +/// Formats given decimal digits `0.<...buf...> * 10^exp` into the exponential form +/// with at least given number of significant digits. When `upper` is true, +/// the exponent will be prefixed by `E`; otherwise that's `e`. The result is +/// stored to the supplied parts array and a slice of written parts is returned. +/// +/// `min_digits` can be less than the number of actual significant digits in `buf`; +/// it will be ignored and full digits will be printed. It is only used to print +/// additional zeroes after rendered digits. Thus `min_digits` of 0 means that +/// it will only print given digits and nothing else. +fn digits_to_exp_str<'a>(buf: &'a [u8], exp: i16, min_ndigits: usize, upper: bool, + parts: &'a mut [Part<'a>]) -> &'a [Part<'a>] { + assert!(!buf.is_empty()); + assert!(buf[0] > b'0'); + assert!(parts.len() >= 6); + + let mut n = 0; + + parts[n] = Part::Copy(&buf[..1]); + n += 1; + + if buf.len() > 1 || min_ndigits > 1 { + parts[n] = Part::Copy(b"."); + parts[n + 1] = Part::Copy(&buf[1..]); + n += 2; + if min_ndigits > buf.len() { + parts[n] = Part::Zero(min_ndigits - buf.len()); + n += 1; + } + } + + // 0.1234 x 10^exp = 1.234 x 10^(exp-1) + let exp = exp as i32 - 1; // avoid underflow when exp is i16::MIN + if exp < 0 { + parts[n] = Part::Copy(if upper { b"E-" } else { b"e-" }); + parts[n + 1] = Part::Num(-exp as u16); + } else { + parts[n] = Part::Copy(if upper { b"E" } else { b"e" }); + parts[n + 1] = Part::Num(exp as u16); + } + &parts[..n + 2] +} + +/// Sign formatting options. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Sign { + /// Prints `-` only for the negative non-zero values. + Minus, // -inf -1 0 0 1 inf nan + /// Prints `-` only for any negative values (including the negative zero). + MinusRaw, // -inf -1 0 0 1 inf nan + /// Prints `-` for the negative non-zero values, or `+` otherwise. + MinusPlus, // -inf -1 +0 +0 +1 +inf nan + /// Prints `-` for any negative values (including the negative zero), or `+` otherwise. + MinusPlusRaw, // -inf -1 -0 +0 +1 +inf nan +} + +/// Returns the static byte string corresponding to the sign to be formatted. +/// It can be either `b""`, `b"+"` or `b"-"`. +fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static [u8] { + match (*decoded, sign) { + (FullDecoded::Nan, _) => b"", + (FullDecoded::Zero, Sign::Minus) => b"", + (FullDecoded::Zero, Sign::MinusRaw) => if negative { b"-" } else { b"" }, + (FullDecoded::Zero, Sign::MinusPlus) => b"+", + (FullDecoded::Zero, Sign::MinusPlusRaw) => if negative { b"-" } else { b"+" }, + (_, Sign::Minus) | (_, Sign::MinusRaw) => if negative { b"-" } else { b"" }, + (_, Sign::MinusPlus) | (_, Sign::MinusPlusRaw) => if negative { b"-" } else { b"+" }, + } +} + +/// Formats given floating point number into the decimal form with at least +/// given number of fractional digits. The result is stored to the supplied parts +/// array while utilizing given byte buffer as a scratch. `upper` is only used to +/// determine the case of non-finite values, i.e. `inf` and `nan`. The first part +/// to be rendered is always a `Part::Sign` (which can be an empty string +/// if no sign is rendered). +/// +/// `format_shortest` should be the underlying digit-generation function. +/// You probably would want `strategy::grisu::format_shortest` for this. +/// +/// `frac_digits` can be less than the number of actual fractional digits in `v`; +/// it will be ignored and full digits will be printed. It is only used to print +/// additional zeroes after rendered digits. Thus `frac_digits` of 0 means that +/// it will only print given digits and nothing else. +/// +/// The byte buffer should be at least `MAX_SIG_DIGITS` bytes long. +/// There should be at least 5 parts available, due to the worst case like +/// `[+][0.][0000][45][0000]` with `frac_digits = 10`. +pub fn to_shortest_str<'a, T, F>(mut format_shortest: F, v: T, + sign: Sign, frac_digits: usize, upper: bool, + buf: &'a mut [u8], parts: &'a mut [Part<'a>]) -> Formatted<'a> + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + assert!(parts.len() >= 4); + assert!(buf.len() >= MAX_SIG_DIGITS); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Infinite => { + parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Zero => { + if frac_digits > 0 { // [0.][0000] + parts[0] = Part::Copy(b"0."); + parts[1] = Part::Zero(frac_digits); + Formatted { sign: sign, parts: &parts[..2] } + } else { + parts[0] = Part::Copy(b"0"); + Formatted { sign: sign, parts: &parts[..1] } + } + } + FullDecoded::Finite(ref decoded) => { + let (len, exp) = format_shortest(decoded, buf); + Formatted { sign: sign, + parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) } + } + } +} + +/// Formats given floating point number into the decimal form or +/// the exponential form, depending on the resulting exponent. The result is +/// stored to the supplied parts array while utilizing given byte buffer +/// as a scratch. `upper` is used to determine the case of non-finite values +/// (`inf` and `nan`) or the case of the exponent prefix (`e` or `E`). +/// The first part to be rendered is always a `Part::Sign` (which can be +/// an empty string if no sign is rendered). +/// +/// `format_shortest` should be the underlying digit-generation function. +/// You probably would want `strategy::grisu::format_shortest` for this. +/// +/// The `dec_bounds` is a tuple `(lo, hi)` such that the number is formatted +/// as decimal only when `10^lo <= V < 10^hi`. Note that this is the *apparant* `V` +/// instead of the actual `v`! Thus any printed exponent in the exponential form +/// cannot be in this range, avoiding any confusion. +/// +/// The byte buffer should be at least `MAX_SIG_DIGITS` bytes long. +/// There should be at least 7 parts available, due to the worst case like +/// `[+][1][.][2345][e][-][67]`. +pub fn to_shortest_exp_str<'a, T, F>(mut format_shortest: F, v: T, + sign: Sign, dec_bounds: (i16, i16), upper: bool, + buf: &'a mut [u8], parts: &'a mut [Part<'a>]) -> Formatted<'a> + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + assert!(parts.len() >= 6); + assert!(buf.len() >= MAX_SIG_DIGITS); + assert!(dec_bounds.0 <= dec_bounds.1); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Infinite => { + parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Zero => { + parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { + Part::Copy(b"0") + } else { + Part::Copy(if upper { b"0E0" } else { b"0e0" }) + }; + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Finite(ref decoded) => { + let (len, exp) = format_shortest(decoded, buf); + let vis_exp = exp as i32 - 1; + let parts = if dec_bounds.0 as i32 <= vis_exp && vis_exp < dec_bounds.1 as i32 { + digits_to_dec_str(&buf[..len], exp, 0, parts) + } else { + digits_to_exp_str(&buf[..len], exp, 0, upper, parts) + }; + Formatted { sign: sign, parts: parts } + } + } +} + +/// Returns rather crude approximation (upper bound) for the maximum buffer size +/// calculated from the given decoded exponent. +/// +/// The exact limit is: +/// +/// - when `exp < 0`, the maximum length is `ceil(log_10 (5^-exp * (2^64 - 1)))`. +/// - when `exp >= 0`, the maximum length is `ceil(log_10 (2^exp * (2^64 - 1)))`. +/// +/// `ceil(log_10 (x^exp * (2^64 - 1)))` is less than `ceil(log_10 (2^64 - 1)) + +/// ceil(exp * log_10 x)`, which is in turn less than `20 + (1 + exp * log_10 x)`. +/// We use the facts that `log_10 2 < 5/16` and `log_10 5 < 12/16`, which is +/// enough for our purposes. +/// +/// Why do we need this? `format_exact` functions will fill the entire buffer +/// unless limited by the last digit restriction, but it is possible that +/// the number of digits requested is ridiculously large (say, 30,000 digits). +/// The vast majority of buffer will be filled with zeroes, so we don't want to +/// allocate all the buffer beforehand. Consequently, for any given arguments, +/// 826 bytes of buffer should be sufficient for `f64`. Compare this with +/// the actual number for the worst case: 770 bytes (when `exp = -1074`). +fn estimate_max_buf_len(exp: i16) -> usize { + 21 + ((if exp < 0 { -12 } else { 5 } * exp as i32) as usize >> 4) +} + +/// Formats given floating point number into the exponential form with +/// exactly given number of significant digits. The result is stored to +/// the supplied parts array while utilizing given byte buffer as a scratch. +/// `upper` is used to determine the case of the exponent prefix (`e` or `E`). +/// The first part to be rendered is always a `Part::Sign` (which can be +/// an empty string if no sign is rendered). +/// +/// `format_exact` should be the underlying digit-generation function. +/// You probably would want `strategy::grisu::format_exact` for this. +/// +/// The byte buffer should be at least `ndigits` bytes long unless `ndigits` is +/// so large that only the fixed number of digits will be ever written. +/// (The tipping point for `f64` is about 800, so 1000 bytes should be enough.) +/// There should be at least 7 parts available, due to the worst case like +/// `[+][1][.][2345][e][-][67]`. +pub fn to_exact_exp_str<'a, T, F>(mut format_exact: F, v: T, + sign: Sign, ndigits: usize, upper: bool, + buf: &'a mut [u8], parts: &'a mut [Part<'a>]) -> Formatted<'a> + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + assert!(parts.len() >= 6); + assert!(ndigits > 0); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Infinite => { + parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Zero => { + if ndigits > 1 { // [0.][0000][e0] + parts[0] = Part::Copy(b"0."); + parts[1] = Part::Zero(ndigits - 1); + parts[2] = Part::Copy(if upper { b"E0" } else { b"e0" }); + Formatted { sign: sign, parts: &parts[..3] } + } else { + parts[0] = Part::Copy(if upper { b"0E0" } else { b"0e0" }); + Formatted { sign: sign, parts: &parts[..1] } + } + } + FullDecoded::Finite(ref decoded) => { + let maxlen = estimate_max_buf_len(decoded.exp); + assert!(buf.len() >= ndigits || buf.len() >= maxlen); + + let trunc = if ndigits < maxlen { ndigits } else { maxlen }; + let (len, exp) = format_exact(decoded, &mut buf[..trunc], i16::MIN); + Formatted { sign: sign, + parts: digits_to_exp_str(&buf[..len], exp, ndigits, upper, parts) } + } + } +} + +/// Formats given floating point number into the decimal form with exactly +/// given number of fractional digits. The result is stored to the supplied parts +/// array while utilizing given byte buffer as a scratch. `upper` is only used to +/// determine the case of non-finite values, i.e. `inf` and `nan`. The first part +/// to be rendered is always a `Part::Sign` (which can be an empty string +/// if no sign is rendered). +/// +/// `format_exact` should be the underlying digit-generation function. +/// You probably would want `strategy::grisu::format_exact` for this. +/// +/// The byte buffer should be enough for the output unless `frac_digits` is +/// so large that only the fixed number of digits will be ever written. +/// (The tipping point for `f64` is about 800, and 1000 bytes should be enough.) +/// There should be at least 5 parts available, due to the worst case like +/// `[+][0.][0000][45][0000]` with `frac_digits = 10`. +pub fn to_exact_fixed_str<'a, T, F>(mut format_exact: F, v: T, + sign: Sign, frac_digits: usize, upper: bool, + buf: &'a mut [u8], parts: &'a mut [Part<'a>]) -> Formatted<'a> + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + assert!(parts.len() >= 4); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Infinite => { + parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + Formatted { sign: sign, parts: &parts[..1] } + } + FullDecoded::Zero => { + if frac_digits > 0 { // [0.][0000] + parts[0] = Part::Copy(b"0."); + parts[1] = Part::Zero(frac_digits); + Formatted { sign: sign, parts: &parts[..2] } + } else { + parts[0] = Part::Copy(b"0"); + Formatted { sign: sign, parts: &parts[..1] } + } + } + FullDecoded::Finite(ref decoded) => { + let maxlen = estimate_max_buf_len(decoded.exp); + assert!(buf.len() >= maxlen); + + // it *is* possible that `frac_digits` is ridiculously large. + // `format_exact` will end rendering digits much earlier in this case, + // because we are strictly limited by `maxlen`. + let limit = if frac_digits < 0x8000 { -(frac_digits as i16) } else { i16::MIN }; + let (len, exp) = format_exact(decoded, &mut buf[..maxlen], limit); + if exp <= limit { + // `format_exact` always returns at least one digit even though the restriction + // hasn't been met, so we catch this condition and treats as like zeroes. + // this does not include the case that the restriction has been met + // only after the final rounding-up; it's a regular case with `exp = limit + 1`. + debug_assert_eq!(len, 0); + if frac_digits > 0 { // [0.][0000] + parts[0] = Part::Copy(b"0."); + parts[1] = Part::Zero(frac_digits); + Formatted { sign: sign, parts: &parts[..2] } + } else { + parts[0] = Part::Copy(b"0"); + Formatted { sign: sign, parts: &parts[..1] } + } + } else { + Formatted { sign: sign, + parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) } + } + } + } +} + diff --git a/src/libcore/num/flt2dec/strategy/dragon.rs b/src/libcore/num/flt2dec/strategy/dragon.rs new file mode 100644 index 0000000000000..bd735594e8225 --- /dev/null +++ b/src/libcore/num/flt2dec/strategy/dragon.rs @@ -0,0 +1,327 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! +Almost direct (but slightly optimized) Rust translation of Figure 3 of [1]. + +[1] Burger, R. G. and Dybvig, R. K. 1996. Printing floating-point numbers + quickly and accurately. SIGPLAN Not. 31, 5 (May. 1996), 108-116. +*/ + +use prelude::*; +use num::Float; +use cmp::Ordering; + +use num::flt2dec::{Decoded, MAX_SIG_DIGITS, round_up}; +use num::flt2dec::estimator::estimate_scaling_factor; +use num::flt2dec::bignum::Digit32 as Digit; +use num::flt2dec::bignum::Big32x36 as Big; + +// FIXME(#22540) const ref to static array seems to ICE +static POW10: [Digit; 10] = [1, 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000, 1000000000]; +static TWOPOW10: [Digit; 10] = [2, 20, 200, 2000, 20000, 200000, + 2000000, 20000000, 200000000, 2000000000]; + +// precalculated arrays of `Digit`s for 10^(2^n) +static POW10TO16: [Digit; 2] = [0x6fc10000, 0x2386f2]; +static POW10TO32: [Digit; 4] = [0, 0x85acef81, 0x2d6d415b, 0x4ee]; +static POW10TO64: [Digit; 7] = [0, 0, 0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x184f03]; +static POW10TO128: [Digit; 14] = + [0, 0, 0, 0, 0x2e953e01, 0x3df9909, 0xf1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08, + 0xbccdb0da, 0xa6337f19, 0xe91f2603, 0x24e]; +static POW10TO256: [Digit; 27] = + [0, 0, 0, 0, 0, 0, 0, 0, 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, + 0xcf4a6e70, 0xd595d80f, 0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, + 0xcc5573c0, 0x65f9ef17, 0x55bc28f2, 0x80dcc7f7, 0xf46eeddc, 0x5fdcefce, 0x553f7]; + +#[doc(hidden)] +pub fn mul_pow10<'a>(x: &'a mut Big, n: usize) -> &'a mut Big { + debug_assert!(n < 512); + if n & 7 != 0 { x.mul_small(POW10[n & 7]); } + if n & 8 != 0 { x.mul_small(POW10[8]); } + if n & 16 != 0 { x.mul_digits(&POW10TO16); } + if n & 32 != 0 { x.mul_digits(&POW10TO32); } + if n & 64 != 0 { x.mul_digits(&POW10TO64); } + if n & 128 != 0 { x.mul_digits(&POW10TO128); } + if n & 256 != 0 { x.mul_digits(&POW10TO256); } + x +} + +fn div_2pow10<'a>(x: &'a mut Big, mut n: usize) -> &'a mut Big { + let largest = POW10.len() - 1; + while n > largest { + x.div_rem_small(POW10[largest]); + n -= largest; + } + x.div_rem_small(TWOPOW10[n]); + x +} + +// only usable when `x < 16 * scale`; `scaleN` should be `scale.mul_small(N)` +fn div_rem_upto_16<'a>(x: &'a mut Big, scale: &Big, + scale2: &Big, scale4: &Big, scale8: &Big) -> (u8, &'a mut Big) { + let mut d = 0; + if *x >= *scale8 { x.sub(scale8); d += 8; } + if *x >= *scale4 { x.sub(scale4); d += 4; } + if *x >= *scale2 { x.sub(scale2); d += 2; } + if *x >= *scale { x.sub(scale); d += 1; } + debug_assert!(*x < *scale); + (d, x) +} + +/// The shortest mode implementation for Dragon. +pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) { + // the number `v` to format is known to be: + // - equal to `mant * 2^exp`; + // - preceded by `(mant - 2 * minus) * 2^exp` in the original type; and + // - followed by `(mant + 2 * plus) * 2^exp` in the original type. + // + // obviously, `minus` and `plus` cannot be zero. (for infinities, we use out-of-range values.) + // also we assume that at least one digit is generated, i.e. `mant` cannot be zero too. + // + // this also means that any number between `low = (mant - minus) * 2^exp` and + // `high = (mant + plus) * 2^exp` will map to this exact floating point number, + // with bounds included when the original mantissa was even (i.e. `!mant_was_odd`). + + assert!(d.mant > 0); + assert!(d.minus > 0); + assert!(d.plus > 0); + assert!(d.mant.checked_add(d.plus).is_some()); + assert!(d.mant.checked_sub(d.minus).is_some()); + assert!(buf.len() >= MAX_SIG_DIGITS); + + // `a.cmp(&b) < rounding` is `if d.inclusive {a <= b} else {a < b}` + let rounding = if d.inclusive {Ordering::Greater} else {Ordering::Equal}; + + // estimate `k_0` from original inputs satisfying `10^(k_0-1) < high <= 10^(k_0+1)`. + // the tight bound `k` satisfying `10^(k-1) < high <= 10^k` is calculated later. + let mut k = estimate_scaling_factor(d.mant + d.plus, d.exp); + + // convert `{mant, plus, minus} * 2^exp` into the fractional form so that: + // - `v = mant / scale` + // - `low = (mant - minus) / scale` + // - `high = (mant + plus) / scale` + let mut mant = Big::from_u64(d.mant); + let mut minus = Big::from_u64(d.minus); + let mut plus = Big::from_u64(d.plus); + let mut scale = Big::from_small(1); + if d.exp < 0 { + scale.mul_pow2(-d.exp as usize); + } else { + mant.mul_pow2(d.exp as usize); + minus.mul_pow2(d.exp as usize); + plus.mul_pow2(d.exp as usize); + } + + // divide `mant` by `10^k`. now `scale / 10 < mant + plus <= scale * 10`. + if k >= 0 { + mul_pow10(&mut scale, k as usize); + } else { + mul_pow10(&mut mant, -k as usize); + mul_pow10(&mut minus, -k as usize); + mul_pow10(&mut plus, -k as usize); + } + + // fixup when `mant + plus > scale` (or `>=`). + // we are not actually modifying `scale`, since we can skip the initial multiplication instead. + // now `scale < mant + plus <= scale * 10` and we are ready to generate digits. + // + // note that `d[0]` *can* be zero, when `scale - plus < mant < scale`. + // in this case rounding-up condition (`up` below) will be triggered immediately. + if scale.cmp(mant.clone().add(&plus)) < rounding { + // equivalent to scaling `scale` by 10 + k += 1; + } else { + mant.mul_small(10); + minus.mul_small(10); + plus.mul_small(10); + } + + // cache `(2, 4, 8) * scale` for digit generation. + let mut scale2 = scale.clone(); scale2.mul_pow2(1); + let mut scale4 = scale.clone(); scale4.mul_pow2(2); + let mut scale8 = scale.clone(); scale8.mul_pow2(3); + + let mut down; + let mut up; + let mut i = 0; + loop { + // invariants, where `d[0..n-1]` are digits generated so far: + // - `v = mant / scale * 10^(k-n-1) + d[0..n-1] * 10^(k-n)` + // - `v - low = minus / scale * 10^(k-n-1)` + // - `high - v = plus / scale * 10^(k-n-1)` + // - `(mant + plus) / scale <= 10` (thus `mant / scale < 10`) + // where `d[i..j]` is a shorthand for `d[i] * 10^(j-i) + ... + d[j-1] * 10 + d[j]`. + + // generate one digit: `d[n] = floor(mant / scale) < 10`. + let (d, _) = div_rem_upto_16(&mut mant, &scale, &scale2, &scale4, &scale8); + debug_assert!(d < 10); + buf[i] = b'0' + d; + i += 1; + + // this is a simplified description of the modified Dragon algorithm. + // many intermediate derivations and completeness arguments are omitted for convenience. + // + // start with modified invariants, as we've updated `n`: + // - `v = mant / scale * 10^(k-n) + d[0..n-1] * 10^(k-n)` + // - `v - low = minus / scale * 10^(k-n)` + // - `high - v = plus / scale * 10^(k-n)` + // + // assume that `d[0..n-1]` is the shortest representation between `low` and `high`, + // i.e. `d[0..n-1]` satisfies both of the following but `d[0..n-2]` doesn't: + // - `low < d[0..n-1] * 10^(k-n) < high` (bijectivity: digits round to `v`); and + // - `abs(v / 10^(k-n) - d[0..n-1]) <= 1/2` (the last digit is correct). + // + // the second condition simplifies to `2 * mant <= scale`. + // solving invariants in terms of `mant`, `low` and `high` yields + // a simpler version of the first condition: `-plus < mant < minus`. + // since `-plus < 0 <= mant`, we have the correct shortest representation + // when `mant < minus` and `2 * mant <= scale`. + // (the former becomes `mant <= minus` when the original mantissa is even.) + // + // when the second doesn't hold (`2 * mant > scale`), we need to increase the last digit. + // this is enough for restoring that condition: we already know that + // the digit generation guarantees `0 <= v / 10^(k-n) - d[0..n-1] < 1`. + // in this case, the first condition becomes `-plus < mant - scale < minus`. + // since `mant < scale` after the generation, we have `scale < mant + plus`. + // (again, this becomes `scale <= mant + plus` when the original mantissa is even.) + // + // in short: + // - stop and round `down` (keep digits as is) when `mant < minus` (or `<=`). + // - stop and round `up` (increase the last digit) when `scale < mant + plus` (or `<=`). + // - keep generating otherwise. + down = mant.cmp(&minus) < rounding; + up = scale.cmp(mant.clone().add(&plus)) < rounding; + if down || up { break; } // we have the shortest representation, proceed to the rounding + + // restore the invariants. + // this makes the algorithm always terminating: `minus` and `plus` always increases, + // but `mant` is clipped modulo `scale` and `scale` is fixed. + mant.mul_small(10); + minus.mul_small(10); + plus.mul_small(10); + } + + // rounding up happens when + // i) only the rounding-up condition was triggered, or + // ii) both conditions were triggered and tie breaking prefers rounding up. + if up && (!down || *mant.mul_pow2(1) >= scale) { + // if rounding up changes the length, the exponent should also change. + // it seems that this condition is very hard to satisfy (possibly impossible), + // but we are just being safe and consistent here. + if let Some(c) = round_up(buf, i) { + buf[i] = c; + i += 1; + k += 1; + } + } + + (i, k) +} + +/// The exact and fixed mode implementation for Dragon. +pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) { + assert!(d.mant > 0); + assert!(d.minus > 0); + assert!(d.plus > 0); + assert!(d.mant.checked_add(d.plus).is_some()); + assert!(d.mant.checked_sub(d.minus).is_some()); + + // estimate `k_0` from original inputs satisfying `10^(k_0-1) < v <= 10^(k_0+1)`. + let mut k = estimate_scaling_factor(d.mant, d.exp); + + // `v = mant / scale`. + let mut mant = Big::from_u64(d.mant); + let mut scale = Big::from_small(1); + if d.exp < 0 { + scale.mul_pow2(-d.exp as usize); + } else { + mant.mul_pow2(d.exp as usize); + } + + // divide `mant` by `10^k`. now `scale / 10 < mant <= scale * 10`. + if k >= 0 { + mul_pow10(&mut scale, k as usize); + } else { + mul_pow10(&mut mant, -k as usize); + } + + // fixup when `mant + plus >= scale`, where `plus / scale = 10^-buf.len() / 2`. + // in order to keep the fixed-size bignum, we actually use `mant + floor(plus) >= scale`. + // we are not actually modifying `scale`, since we can skip the initial multiplication instead. + // again with the shortest algorithm, `d[0]` can be zero but will be eventually rounded up. + if *div_2pow10(&mut scale.clone(), buf.len()).add(&mant) >= scale { + // equivalent to scaling `scale` by 10 + k += 1; + } else { + mant.mul_small(10); + } + + // if we are working with the last-digit limitation, we need to shorten the buffer + // before the actual rendering in order to avoid double rounding. + // note that we have to enlarge the buffer again when rounding up happens! + let mut len = if k < limit { + // oops, we cannot even produce *one* digit. + // this is possible when, say, we've got something like 9.5 and it's being rounded to 10. + // we return an empty buffer, with an exception of the later rounding-up case + // which occurs when `k == limit` and has to produce exactly one digit. + 0 + } else if ((k as i32 - limit as i32) as usize) < buf.len() { + (k - limit) as usize + } else { + buf.len() + }; + + if len > 0 { + // cache `(2, 4, 8) * scale` for digit generation. + // (this can be expensive, so do not calculate them when the buffer is empty.) + let mut scale2 = scale.clone(); scale2.mul_pow2(1); + let mut scale4 = scale.clone(); scale4.mul_pow2(2); + let mut scale8 = scale.clone(); scale8.mul_pow2(3); + + for i in 0..len { + if mant.is_zero() { // following digits are all zeroes, we stop here + // do *not* try to perform rounding! rather, fill remaining digits. + for c in &mut buf[i..len] { *c = b'0'; } + return (len, k); + } + + let mut d = 0; + if mant >= scale8 { mant.sub(&scale8); d += 8; } + if mant >= scale4 { mant.sub(&scale4); d += 4; } + if mant >= scale2 { mant.sub(&scale2); d += 2; } + if mant >= scale { mant.sub(&scale); d += 1; } + debug_assert!(mant < scale); + debug_assert!(d < 10); + buf[i] = b'0' + d; + mant.mul_small(10); + } + } + + // rounding up if we stop in the middle of digits + if mant >= *scale.mul_small(5) { + // if rounding up changes the length, the exponent should also change. + // but we've been requested a fixed number of digits, so do not alter the buffer... + if let Some(c) = round_up(buf, len) { + // ...unless we've been requested the fixed precision instead. + // we also need to check that, if the original buffer was empty, + // the additional digit can only be added when `k == limit` (edge case). + k += 1; + if k > limit && len < buf.len() { + buf[len] = c; + len += 1; + } + } + } + + (len, k) +} + diff --git a/src/libcore/num/flt2dec/strategy/grisu.rs b/src/libcore/num/flt2dec/strategy/grisu.rs new file mode 100644 index 0000000000000..220811e9985c3 --- /dev/null +++ b/src/libcore/num/flt2dec/strategy/grisu.rs @@ -0,0 +1,749 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! +Rust adaptation of Grisu3 algorithm described in [1]. It uses about +1KB of precomputed table, and in turn, it's very quick for most inputs. + +[1] Florian Loitsch. 2010. Printing floating-point numbers quickly and + accurately with integers. SIGPLAN Not. 45, 6 (June 2010), 233-243. +*/ + +use prelude::*; +use num::Float; + +use num::flt2dec::{Decoded, MAX_SIG_DIGITS, round_up}; + +/// A custom 64-bit floating point type, representing `f * 2^e`. +#[derive(Copy, Clone, Debug)] +#[doc(hidden)] +pub struct Fp { + /// The integer mantissa. + pub f: u64, + /// The exponent in base 2. + pub e: i16, +} + +impl Fp { + /// Returns a correctly rounded product of itself and `other`. + fn mul(&self, other: &Fp) -> Fp { + const MASK: u64 = 0xffffffff; + let a = self.f >> 32; + let b = self.f & MASK; + let c = other.f >> 32; + let d = other.f & MASK; + let ac = a * c; + let bc = b * c; + let ad = a * d; + let bd = b * d; + let tmp = (bd >> 32) + (ad & MASK) + (bc & MASK) + (1 << 31) /* round */; + let f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + let e = self.e + other.e + 64; + Fp { f: f, e: e } + } + + /// Normalizes itself so that the resulting mantissa is at least `2^63`. + fn normalize(&self) -> Fp { + let mut f = self.f; + let mut e = self.e; + if f >> (64 - 32) == 0 { f <<= 32; e -= 32; } + if f >> (64 - 16) == 0 { f <<= 16; e -= 16; } + if f >> (64 - 8) == 0 { f <<= 8; e -= 8; } + if f >> (64 - 4) == 0 { f <<= 4; e -= 4; } + if f >> (64 - 2) == 0 { f <<= 2; e -= 2; } + if f >> (64 - 1) == 0 { f <<= 1; e -= 1; } + debug_assert!(f >= (1 >> 63)); + Fp { f: f, e: e } + } + + /// Normalizes itself to have the shared exponent. + /// It can only decrease the exponent (and thus increase the mantissa). + fn normalize_to(&self, e: i16) -> Fp { + let edelta = self.e - e; + assert!(edelta >= 0); + let edelta = edelta as usize; + assert_eq!(self.f << edelta >> edelta, self.f); + Fp { f: self.f << edelta, e: e } + } +} + +// see the comments in `format_shortest_opt` for the rationale. +#[doc(hidden)] pub const ALPHA: i16 = -60; +#[doc(hidden)] pub const GAMMA: i16 = -32; + +/* +# the following Python code generates this table: +for i in xrange(-308, 333, 8): + if i >= 0: f = 10**i; e = 0 + else: f = 2**(80-4*i) // 10**-i; e = 4 * i - 80 + l = f.bit_length() + f = ((f << 64 >> (l-1)) + 1) >> 1; e += l - 64 + print ' (%#018x, %5d, %4d),' % (f, e, i) +*/ +// FIXME(#22540) const ref to static array seems to ICE +#[doc(hidden)] +pub static CACHED_POW10: [(u64, i16, i16); 81] = [ // (f, e, k) + (0xe61acf033d1a45df, -1087, -308), + (0xab70fe17c79ac6ca, -1060, -300), + (0xff77b1fcbebcdc4f, -1034, -292), + (0xbe5691ef416bd60c, -1007, -284), + (0x8dd01fad907ffc3c, -980, -276), + (0xd3515c2831559a83, -954, -268), + (0x9d71ac8fada6c9b5, -927, -260), + (0xea9c227723ee8bcb, -901, -252), + (0xaecc49914078536d, -874, -244), + (0x823c12795db6ce57, -847, -236), + (0xc21094364dfb5637, -821, -228), + (0x9096ea6f3848984f, -794, -220), + (0xd77485cb25823ac7, -768, -212), + (0xa086cfcd97bf97f4, -741, -204), + (0xef340a98172aace5, -715, -196), + (0xb23867fb2a35b28e, -688, -188), + (0x84c8d4dfd2c63f3b, -661, -180), + (0xc5dd44271ad3cdba, -635, -172), + (0x936b9fcebb25c996, -608, -164), + (0xdbac6c247d62a584, -582, -156), + (0xa3ab66580d5fdaf6, -555, -148), + (0xf3e2f893dec3f126, -529, -140), + (0xb5b5ada8aaff80b8, -502, -132), + (0x87625f056c7c4a8b, -475, -124), + (0xc9bcff6034c13053, -449, -116), + (0x964e858c91ba2655, -422, -108), + (0xdff9772470297ebd, -396, -100), + (0xa6dfbd9fb8e5b88f, -369, -92), + (0xf8a95fcf88747d94, -343, -84), + (0xb94470938fa89bcf, -316, -76), + (0x8a08f0f8bf0f156b, -289, -68), + (0xcdb02555653131b6, -263, -60), + (0x993fe2c6d07b7fac, -236, -52), + (0xe45c10c42a2b3b06, -210, -44), + (0xaa242499697392d3, -183, -36), + (0xfd87b5f28300ca0e, -157, -28), + (0xbce5086492111aeb, -130, -20), + (0x8cbccc096f5088cc, -103, -12), + (0xd1b71758e219652c, -77, -4), + (0x9c40000000000000, -50, 4), + (0xe8d4a51000000000, -24, 12), + (0xad78ebc5ac620000, 3, 20), + (0x813f3978f8940984, 30, 28), + (0xc097ce7bc90715b3, 56, 36), + (0x8f7e32ce7bea5c70, 83, 44), + (0xd5d238a4abe98068, 109, 52), + (0x9f4f2726179a2245, 136, 60), + (0xed63a231d4c4fb27, 162, 68), + (0xb0de65388cc8ada8, 189, 76), + (0x83c7088e1aab65db, 216, 84), + (0xc45d1df942711d9a, 242, 92), + (0x924d692ca61be758, 269, 100), + (0xda01ee641a708dea, 295, 108), + (0xa26da3999aef774a, 322, 116), + (0xf209787bb47d6b85, 348, 124), + (0xb454e4a179dd1877, 375, 132), + (0x865b86925b9bc5c2, 402, 140), + (0xc83553c5c8965d3d, 428, 148), + (0x952ab45cfa97a0b3, 455, 156), + (0xde469fbd99a05fe3, 481, 164), + (0xa59bc234db398c25, 508, 172), + (0xf6c69a72a3989f5c, 534, 180), + (0xb7dcbf5354e9bece, 561, 188), + (0x88fcf317f22241e2, 588, 196), + (0xcc20ce9bd35c78a5, 614, 204), + (0x98165af37b2153df, 641, 212), + (0xe2a0b5dc971f303a, 667, 220), + (0xa8d9d1535ce3b396, 694, 228), + (0xfb9b7cd9a4a7443c, 720, 236), + (0xbb764c4ca7a44410, 747, 244), + (0x8bab8eefb6409c1a, 774, 252), + (0xd01fef10a657842c, 800, 260), + (0x9b10a4e5e9913129, 827, 268), + (0xe7109bfba19c0c9d, 853, 276), + (0xac2820d9623bf429, 880, 284), + (0x80444b5e7aa7cf85, 907, 292), + (0xbf21e44003acdd2d, 933, 300), + (0x8e679c2f5e44ff8f, 960, 308), + (0xd433179d9c8cb841, 986, 316), + (0x9e19db92b4e31ba9, 1013, 324), + (0xeb96bf6ebadf77d9, 1039, 332), +]; + +#[doc(hidden)] pub const CACHED_POW10_FIRST_E: i16 = -1087; +#[doc(hidden)] pub const CACHED_POW10_LAST_E: i16 = 1039; + +#[doc(hidden)] +pub fn cached_power(alpha: i16, gamma: i16) -> (i16, Fp) { + let offset = CACHED_POW10_FIRST_E as i32; + let range = (CACHED_POW10.len() as i32) - 1; + let domain = (CACHED_POW10_LAST_E - CACHED_POW10_FIRST_E) as i32; + let idx = ((gamma as i32) - offset) * range / domain; + let (f, e, k) = CACHED_POW10[idx as usize]; + debug_assert!(alpha <= e && e <= gamma); + (k, Fp { f: f, e: e }) +} + +/// Given `x > 0`, returns `(k, 10^k)` such that `10^k <= x < 10^(k+1)`. +#[doc(hidden)] +pub fn max_pow10_no_more_than(x: u32) -> (u8, u32) { + debug_assert!(x > 0); + + const X9: u32 = 10_0000_0000; + const X8: u32 = 1_0000_0000; + const X7: u32 = 1000_0000; + const X6: u32 = 100_0000; + const X5: u32 = 10_0000; + const X4: u32 = 1_0000; + const X3: u32 = 1000; + const X2: u32 = 100; + const X1: u32 = 10; + + if x < X4 { + if x < X2 { if x < X1 {(0, 1)} else {(1, X1)} } + else { if x < X3 {(2, X2)} else {(3, X3)} } + } else { + if x < X6 { if x < X5 {(4, X4)} else {(5, X5)} } + else if x < X8 { if x < X7 {(6, X6)} else {(7, X7)} } + else { if x < X9 {(8, X8)} else {(9, X9)} } + } +} + +/// The shortest mode implementation for Grisu. +/// +/// It returns `None` when it would return an inexact representation otherwise. +pub fn format_shortest_opt(d: &Decoded, + buf: &mut [u8]) -> Option<(/*#digits*/ usize, /*exp*/ i16)> { + assert!(d.mant > 0); + assert!(d.minus > 0); + assert!(d.plus > 0); + assert!(d.mant.checked_add(d.plus).is_some()); + assert!(d.mant.checked_sub(d.minus).is_some()); + assert!(buf.len() >= MAX_SIG_DIGITS); + assert!(d.mant + d.plus < (1 << 61)); // we need at least three bits of additional precision + + // start with the normalized values with the shared exponent + let plus = Fp { f: d.mant + d.plus, e: d.exp }.normalize(); + let minus = Fp { f: d.mant - d.minus, e: d.exp }.normalize_to(plus.e); + let v = Fp { f: d.mant, e: d.exp }.normalize_to(plus.e); + + // find any `cached = 10^minusk` such that `ALPHA <= minusk + plus.e + 64 <= GAMMA`. + // since `plus` is normalized, this means `2^(62 + ALPHA) <= plus * cached < 2^(64 + GAMMA)`; + // given our choices of `ALPHA` and `GAMMA`, this puts `plus * cached` into `[4, 2^32)`. + // + // it is obviously desirable to maximize `GAMMA - ALPHA`, + // so that we don't need many cached powers of 10, but there are some considerations: + // + // 1. we want to keep `floor(plus * cached)` within `u32` since it needs a costly division. + // (this is not really avoidable, remainder is required for accuracy estimation.) + // 2. the remainder of `floor(plus * cached)` repeatedly gets multiplied by 10, + // and it should not overflow. + // + // the first gives `64 + GAMMA <= 32`, while the second gives `10 * 2^-ALPHA <= 2^64`; + // -60 and -32 is the maximal range with this constraint, and V8 also uses them. + let (minusk, cached) = cached_power(ALPHA - plus.e - 64, GAMMA - plus.e - 64); + + // scale fps. this gives the maximal error of 1 ulp (proved from Theorem 5.1). + let plus = plus.mul(&cached); + let minus = minus.mul(&cached); + let v = v.mul(&cached); + debug_assert_eq!(plus.e, minus.e); + debug_assert_eq!(plus.e, v.e); + + // +- actual range of minus + // | <---|---------------------- unsafe region --------------------------> | + // | | | + // | |<--->| | <--------------- safe region ---------------> | | + // | | | | | | + // |1 ulp|1 ulp| |1 ulp|1 ulp| |1 ulp|1 ulp| + // |<--->|<--->| |<--->|<--->| |<--->|<--->| + // |-----|-----|-------...-------|-----|-----|-------...-------|-----|-----| + // | minus | | v | | plus | + // minus1 minus0 v - 1 ulp v + 1 ulp plus0 plus1 + // + // above `minus`, `v` and `plus` are *quantized* approximations (error < 1 ulp). + // as we don't know the error is positive or negative, we use two approximations spaced equally + // and have the maximal error of 2 ulps. + // + // the "unsafe region" is a liberal interval which we initially generate. + // the "safe region" is a conservative interval which we only accept. + // we start with the correct repr within the unsafe region, and try to find the closest repr + // to `v` which is also within the safe region. if we can't, we give up. + let plus1 = plus.f + 1; +// let plus0 = plus.f - 1; // only for explanation +// let minus0 = minus.f + 1; // only for explanation + let minus1 = minus.f - 1; + let e = -plus.e as usize; // shared exponent + + // divide `plus1` into integral and fractional parts. + // integral parts are guaranteed to fit in u32, since cached power guarantees `plus < 2^32` + // and normalized `plus.f` is always less than `2^64 - 2^4` due to the precision requirement. + let plus1int = (plus1 >> e) as u32; + let plus1frac = plus1 & ((1 << e) - 1); + + // calculate the largest `10^max_kappa` no more than `plus1` (thus `plus1 < 10^(max_kappa+1)`). + // this is an upper bound of `kappa` below. + let (max_kappa, max_ten_kappa) = max_pow10_no_more_than(plus1int); + + let mut i = 0; + let exp = max_kappa as i16 - minusk + 1; + + // Theorem 6.2: if `k` is the greatest integer s.t. `0 <= y mod 10^k <= y - x`, + // then `V = floor(y / 10^k) * 10^k` is in `[x, y]` and one of the shortest + // representations (with the minimal number of significant digits) in that range. + // + // find the digit length `kappa` between `(minus1, plus1)` as per Theorem 6.2. + // Theorem 6.2 can be adopted to exclude `x` by requiring `y mod 10^k < y - x` instead. + // (e.g. `x` = 32000, `y` = 32777; `kappa` = 2 since `y mod 10^3 = 777 < y - x = 777`.) + // the algorithm relies on the later verification phase to exclude `y`. + let delta1 = plus1 - minus1; +// let delta1int = (delta1 >> e) as usize; // only for explanation + let delta1frac = delta1 & ((1 << e) - 1); + + // render integral parts, while checking for the accuracy at each step. + let mut kappa = max_kappa as i16; + let mut ten_kappa = max_ten_kappa; // 10^kappa + let mut remainder = plus1int; // digits yet to be rendered + loop { // we always have at least one digit to render, as `plus1 >= 10^kappa` + // invariants: + // - `delta1int <= remainder < 10^(kappa+1)` + // - `plus1int = d[0..n-1] * 10^(kappa+1) + remainder` + // (it follows that `remainder = plus1int % 10^(kappa+1)`) + + // divide `remainder` by `10^kappa`. both are scaled by `2^-e`. + let q = remainder / ten_kappa; + let r = remainder % ten_kappa; + debug_assert!(q < 10); + buf[i] = b'0' + q as u8; + i += 1; + + let plus1rem = ((r as u64) << e) + plus1frac; // == (plus1 % 10^kappa) * 2^e + if plus1rem < delta1 { + // `plus1 % 10^kappa < delta1 = plus1 - minus1`; we've found the correct `kappa`. + let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent + return round_and_weed(&mut buf[..i], exp, plus1rem, delta1, plus1 - v.f, ten_kappa, 1); + } + + // break the loop when we have rendered all integral digits. + // the exact number of digits is `max_kappa + 1` as `plus1 < 10^(max_kappa+1)`. + if i > max_kappa as usize { + debug_assert_eq!(ten_kappa, 1); + debug_assert_eq!(kappa, 0); + break; + } + + // restore invariants + kappa -= 1; + ten_kappa /= 10; + remainder = r; + } + + // render fractional parts, while checking for the accuracy at each step. + // this time we rely on repeated multiplications, as division will lose the precision. + let mut remainder = plus1frac; + let mut threshold = delta1frac; + let mut ulp = 1; + loop { // the next digit should be significant as we've tested that before breaking out + // invariants, where `m = max_kappa + 1` (# of digits in the integral part): + // - `remainder < 2^e` + // - `plus1frac * 10^(n-m) = d[m..n-1] * 2^e + remainder` + + remainder *= 10; // won't overflow, `2^e * 10 < 2^64` + threshold *= 10; + ulp *= 10; + + // divide `remainder` by `10^kappa`. + // both are scaled by `2^e / 10^kappa`, so the latter is implicit here. + let q = remainder >> e; + let r = remainder & ((1 << e) - 1); + debug_assert!(q < 10); + buf[i] = b'0' + q as u8; + i += 1; + + if r < threshold { + let ten_kappa = 1 << e; // implicit divisor + return round_and_weed(&mut buf[..i], exp, r, threshold, + (plus1 - v.f) * ulp, ten_kappa, ulp); + } + + // restore invariants + kappa -= 1; + remainder = r; + } + + // we've generated all significant digits of `plus1`, but not sure if it's the optimal one. + // for example, if `minus1` is 3.14153... and `plus1` is 3.14158..., there are 5 different + // shortest representation from 3.14154 to 3.14158 but we only have the greatest one. + // we have to successively decrease the last digit and check if this is the optimal repr. + // there are at most 9 candidates (..1 to ..9), so this is fairly quick. ("rounding" phase) + // + // the function checks if this "optimal" repr is actually within the ulp ranges, + // and also, it is possible that the "second-to-optimal" repr can actually be optimal + // due to the rounding error. in either cases this returns `None`. ("weeding" phase) + // + // all arguments here are scaled by the common (but implicit) value `k`, so that: + // - `remainder = (plus1 % 10^kappa) * k` + // - `threshold = (plus1 - minus1) * k` (and also, `remainder < threshold`) + // - `plus1v = (plus1 - v) * k` (and also, `threshold > plus1v` from prior invariants) + // - `ten_kappa = 10^kappa * k` + // - `ulp = 2^-e * k` + fn round_and_weed(buf: &mut [u8], exp: i16, remainder: u64, threshold: u64, plus1v: u64, + ten_kappa: u64, ulp: u64) -> Option<(usize, i16)> { + assert!(!buf.is_empty()); + + // produce two approximations to `v` (actually `plus1 - v`) within 1.5 ulps. + // the resulting representation should be the closest representation to both. + // + // here `plus1 - v` is used since calculations are done with respect to `plus1` + // in order to avoid overflow/underflow (hence the seemingly swapped names). + let plus1v_down = plus1v + ulp; // plus1 - (v - 1 ulp) + let plus1v_up = plus1v - ulp; // plus1 - (v + 1 ulp) + + // decrease the last digit and stop at the closest representation to `v + 1 ulp`. + let mut plus1w = remainder; // plus1w(n) = plus1 - w(n) + { + let last = buf.last_mut().unwrap(); + + // we work with the approximated digits `w(n)`, which is initially equal to `plus1 - + // plus1 % 10^kappa`. after running the loop body `n` times, `w(n) = plus1 - + // plus1 % 10^kappa - n * 10^kappa`. we set `plus1w(n) = plus1 - w(n) = + // plus1 % 10^kappa + n * 10^kappa` (thus `remainder = plus1w(0)`) to simplify checks. + // note that `plus1w(n)` is always increasing. + // + // we have three conditions to terminate. any of them will make the loop unable to + // proceed, but we then have at least one valid representation known to be closest to + // `v + 1 ulp` anyway. we will denote them as TC1 through TC3 for brevity. + // + // TC1: `w(n) <= v + 1 ulp`, i.e. this is the last repr that can be the closest one. + // this is equivalent to `plus1 - w(n) = plus1w(n) >= plus1 - (v + 1 ulp) = plus1v_up`. + // combined with TC2 (which checks if `w(n+1)` is valid), this prevents the possible + // overflow on the calculation of `plus1w(n)`. + // + // TC2: `w(n+1) < minus1`, i.e. the next repr definitely does not round to `v`. + // this is equivalent to `plus1 - w(n) + 10^kappa = plus1w(n) + 10^kappa > + // plus1 - minus1 = threshold`. the left hand side can overflow, but we know + // `threshold > plus1v`, so if TC1 is false, `threshold - plus1w(n) > + // threshold - (plus1v - 1 ulp) > 1 ulp` and we can safely test if + // `threshold - plus1w(n) < 10^kappa` instead. + // + // TC3: `abs(w(n) - (v + 1 ulp)) <= abs(w(n+1) - (v + 1 ulp))`, i.e. the next repr is + // no closer to `v + 1 ulp` than the current repr. given `z(n) = plus1v_up - plus1w(n)`, + // this becomes `abs(z(n)) <= abs(z(n+1))`. again assuming that TC1 is false, we have + // `z(n) > 0`. we have two cases to consider: + // + // - when `z(n+1) >= 0`: TC3 becomes `z(n) <= z(n+1)`. as `plus1w(n)` is increasing, + // `z(n)` should be decreasing and this is clearly false. + // - when `z(n+1) < 0`: + // - TC3a: the precondition is `plus1v_up < plus1w(n) + 10^kappa`. assuming TC2 is + // false, `threshold >= plus1w(n) + 10^kappa` so it cannot overflow. + // - TC3b: TC3 becomes `z(n) <= -z(n+1)`, i.e. `plus1v_up - plus1w(n) >= + // plus1w(n+1) - plus1v_up = plus1w(n) + 10^kappa - plus1v_up`. the negated TC1 + // gives `plus1v_up > plus1w(n)`, so it cannot overflow or underflow when + // combined with TC3a. + // + // consequently, we should stop when `TC1 || TC2 || (TC3a && TC3b)`. the following is + // equal to its inverse, `!TC1 && !TC2 && (!TC3a || !TC3b)`. + while plus1w < plus1v_up && + threshold - plus1w >= ten_kappa && + (plus1w + ten_kappa < plus1v_up || + plus1v_up - plus1w >= plus1w + ten_kappa - plus1v_up) { + *last -= 1; + debug_assert!(*last > b'0'); // the shortest repr cannot end with `0` + plus1w += ten_kappa; + } + } + + // check if this representation is also the closest representation to `v - 1 ulp`. + // + // this is simply same to the terminating conditions for `v + 1 ulp`, with all `plus1v_up` + // replaced by `plus1v_down` instead. overflow analysis equally holds. + if plus1w < plus1v_down && + threshold - plus1w >= ten_kappa && + (plus1w + ten_kappa < plus1v_down || + plus1v_down - plus1w >= plus1w + ten_kappa - plus1v_down) { + return None; + } + + // now we have the closest representation to `v` between `plus1` and `minus1`. + // this is too liberal, though, so we reject any `w(n)` not between `plus0` and `minus0`, + // i.e. `plus1 - plus1w(n) <= minus0` or `plus1 - plus1w(n) >= plus0`. we utilize the facts + // that `threshold = plus1 - minus1` and `plus1 - plus0 = minus0 - minus1 = 2 ulp`. + if 2 * ulp <= plus1w && plus1w <= threshold - 4 * ulp { + Some((buf.len(), exp)) + } else { + None + } + } +} + +/// The shortest mode implementation for Grisu with Dragon fallback. +/// +/// This should be used for most cases. +pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) { + use num::flt2dec::strategy::dragon::format_shortest as fallback; + match format_shortest_opt(d, buf) { + Some(ret) => ret, + None => fallback(d, buf), + } +} + +/// The exact and fixed mode implementation for Grisu. +/// +/// It returns `None` when it would return an inexact representation otherwise. +pub fn format_exact_opt(d: &Decoded, buf: &mut [u8], limit: i16) + -> Option<(/*#digits*/ usize, /*exp*/ i16)> { + assert!(d.mant > 0); + assert!(d.mant < (1 << 61)); // we need at least three bits of additional precision + assert!(!buf.is_empty()); + + // normalize and scale `v`. + let v = Fp { f: d.mant, e: d.exp }.normalize(); + let (minusk, cached) = cached_power(ALPHA - v.e - 64, GAMMA - v.e - 64); + let v = v.mul(&cached); + + // divide `v` into integral and fractional parts. + let e = -v.e as usize; + let vint = (v.f >> e) as u32; + let vfrac = v.f & ((1 << e) - 1); + + // both old `v` and new `v` (scaled by `10^-k`) has an error of < 1 ulp (Theorem 5.1). + // as we don't know the error is positive or negative, we use two approximations + // spaced equally and have the maximal error of 2 ulps (same to the shortest case). + // + // the goal is to find the exactly rounded series of digits that are common to + // both `v - 1 ulp` and `v + 1 ulp`, so that we are maximally confident. + // if this is not possible, we don't know which one is the correct output for `v`, + // so we give up and fall back. + // + // `err` is defined as `1 ulp * 2^e` here (same to the ulp in `vfrac`), + // and we will scale it whenever `v` gets scaled. + let mut err = 1; + + // calculate the largest `10^max_kappa` no more than `v` (thus `v < 10^(max_kappa+1)`). + // this is an upper bound of `kappa` below. + let (max_kappa, max_ten_kappa) = max_pow10_no_more_than(vint); + + let mut i = 0; + let exp = max_kappa as i16 - minusk + 1; + + // if we are working with the last-digit limitation, we need to shorten the buffer + // before the actual rendering in order to avoid double rounding. + // note that we have to enlarge the buffer again when rounding up happens! + let len = if exp <= limit { + // oops, we cannot even produce *one* digit. + // this is possible when, say, we've got something like 9.5 and it's being rounded to 10. + // + // in principle we can immediately call `possibly_round` with an empty buffer, + // but scaling `max_ten_kappa << e` by 10 can result in overflow. + // thus we are being sloppy here and widen the error range by a factor of 10. + // this will increase the false negative rate, but only very, *very* slightly; + // it can only matter noticably when the mantissa is bigger than 60 bits. + return possibly_round(buf, 0, exp, limit, v.f / 10, (max_ten_kappa as u64) << e, err << e); + } else if ((exp as i32 - limit as i32) as usize) < buf.len() { + (exp - limit) as usize + } else { + buf.len() + }; + debug_assert!(len > 0); + + // render integral parts. + // the error is entirely fractional, so we don't need to check it in this part. + let mut kappa = max_kappa as i16; + let mut ten_kappa = max_ten_kappa; // 10^kappa + let mut remainder = vint; // digits yet to be rendered + loop { // we always have at least one digit to render + // invariants: + // - `remainder < 10^(kappa+1)` + // - `vint = d[0..n-1] * 10^(kappa+1) + remainder` + // (it follows that `remainder = vint % 10^(kappa+1)`) + + // divide `remainder` by `10^kappa`. both are scaled by `2^-e`. + let q = remainder / ten_kappa; + let r = remainder % ten_kappa; + debug_assert!(q < 10); + buf[i] = b'0' + q as u8; + i += 1; + + // is the buffer full? run the rounding pass with the remainder. + if i == len { + let vrem = ((r as u64) << e) + vfrac; // == (v % 10^kappa) * 2^e + return possibly_round(buf, len, exp, limit, vrem, (ten_kappa as u64) << e, err << e); + } + + // break the loop when we have rendered all integral digits. + // the exact number of digits is `max_kappa + 1` as `plus1 < 10^(max_kappa+1)`. + if i > max_kappa as usize { + debug_assert_eq!(ten_kappa, 1); + debug_assert_eq!(kappa, 0); + break; + } + + // restore invariants + kappa -= 1; + ten_kappa /= 10; + remainder = r; + } + + // render fractional parts. + // + // in principle we can continue to the last available digit and check for the accuracy. + // unfortunately we are working with the finite-sized integers, so we need some criterion + // to detect the overflow. V8 uses `remainder > err`, which becomes false when + // the first `i` significant digits of `v - 1 ulp` and `v` differ. however this rejects + // too many otherwise valid input. + // + // since the later phase has a correct overflow detection, we instead use tighter criterion: + // we continue til `err` exceeds `10^kappa / 2`, so that the range between `v - 1 ulp` and + // `v + 1 ulp` definitely contains two or more rounded representations. this is same to + // the first two comparisons from `possibly_round`, for the reference. + let mut remainder = vfrac; + let maxerr = 1 << (e - 1); + while err < maxerr { + // invariants, where `m = max_kappa + 1` (# of digits in the integral part): + // - `remainder < 2^e` + // - `vfrac * 10^(n-m) = d[m..n-1] * 2^e + remainder` + // - `err = 10^(n-m)` + + remainder *= 10; // won't overflow, `2^e * 10 < 2^64` + err *= 10; // won't overflow, `err * 10 < 2^e * 5 < 2^64` + + // divide `remainder` by `10^kappa`. + // both are scaled by `2^e / 10^kappa`, so the latter is implicit here. + let q = remainder >> e; + let r = remainder & ((1 << e) - 1); + debug_assert!(q < 10); + buf[i] = b'0' + q as u8; + i += 1; + + // is the buffer full? run the rounding pass with the remainder. + if i == len { + return possibly_round(buf, len, exp, limit, r, 1 << e, err); + } + + // restore invariants + remainder = r; + } + + // further calculation is useless (`possibly_round` definitely fails), so we give up. + return None; + + // we've generated all requested digits of `v`, which should be also same to corresponding + // digits of `v - 1 ulp`. now we check if there is a unique representation shared by + // both `v - 1 ulp` and `v + 1 ulp`; this can be either same to generated digits, or + // to the rounded-up version of those digits. if the range contains multiple representations + // of the same length, we cannot be sure and should return `None` instead. + // + // all arguments here are scaled by the common (but implicit) value `k`, so that: + // - `remainder = (v % 10^kappa) * k` + // - `ten_kappa = 10^kappa * k` + // - `ulp = 2^-e * k` + fn possibly_round(buf: &mut [u8], mut len: usize, mut exp: i16, limit: i16, + remainder: u64, ten_kappa: u64, ulp: u64) -> Option<(usize, i16)> { + debug_assert!(remainder < ten_kappa); + + // 10^kappa + // : : :<->: : + // : : : : : + // :|1 ulp|1 ulp| : + // :|<--->|<--->| : + // ----|-----|-----|---- + // | v | + // v - 1 ulp v + 1 ulp + // + // (for the reference, the dotted line indicates the exact value for + // possible representations in given number of digits.) + // + // error is too large that there are at least three possible representations + // between `v - 1 ulp` and `v + 1 ulp`. we cannot determine which one is correct. + if ulp >= ten_kappa { return None; } + + // 10^kappa + // :<------->: + // : : + // : |1 ulp|1 ulp| + // : |<--->|<--->| + // ----|-----|-----|---- + // | v | + // v - 1 ulp v + 1 ulp + // + // in fact, 1/2 ulp is enough to introduce two possible representations. + // (remember that we need a unique representation for both `v - 1 ulp` and `v + 1 ulp`.) + // this won't overflow, as `ulp < ten_kappa` from the first check. + if ten_kappa - ulp <= ulp { return None; } + + // remainder + // :<->| : + // : | : + // :<--------- 10^kappa ---------->: + // | : | : + // |1 ulp|1 ulp| : + // |<--->|<--->| : + // ----|-----|-----|------------------------ + // | v | + // v - 1 ulp v + 1 ulp + // + // if `v + 1 ulp` is closer to the rounded-down representation (which is already in `buf`), + // then we can safely return. note that `v - 1 ulp` *can* be less than the current + // representation, but as `1 ulp < 10^kappa / 2`, this condition is enough: + // the distance between `v - 1 ulp` and the current representation + // cannot exceed `10^kappa / 2`. + // + // the condition equals to `remainder + ulp < 10^kappa / 2`. + // since this can easily overflow, first check if `remainder < 10^kappa / 2`. + // we've already verified that `ulp < 10^kappa / 2`, so as long as + // `10^kappa` did not overflow after all, the second check is fine. + if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { + return Some((len, exp)); + } + + // :<------- remainder ------>| : + // : | : + // :<--------- 10^kappa --------->: + // : | | : | + // : |1 ulp|1 ulp| + // : |<--->|<--->| + // -----------------------|-----|-----|----- + // | v | + // v - 1 ulp v + 1 ulp + // + // on the other hands, if `v - 1 ulp` is closer to the rounded-up representation, + // we should round up and return. for the same reason we don't need to check `v + 1 ulp`. + // + // the condition equals to `remainder - ulp >= 10^kappa / 2`. + // again we first check if `remainder > ulp` (note that this is not `remainder >= ulp`, + // as `10^kappa` is never zero). also note that `remainder - ulp <= 10^kappa`, + // so the second check does not overflow. + if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { + if let Some(c) = round_up(buf, len) { + // only add an additional digit when we've been requested the fixed precision. + // we also need to check that, if the original buffer was empty, + // the additional digit can only be added when `exp == limit` (edge case). + exp += 1; + if exp > limit && len < buf.len() { + buf[len] = c; + len += 1; + } + } + return Some((len, exp)); + } + + // otherwise we are doomed (i.e. some values between `v - 1 ulp` and `v + 1 ulp` are + // rounding down and others are rounding up) and give up. + None + } +} + +/// The exact and fixed mode implementation for Grisu with Dragon fallback. +/// +/// This should be used for most cases. +pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) { + use num::flt2dec::strategy::dragon::format_exact as fallback; + match format_exact_opt(d, buf, limit) { + Some(ret) => ret, + None => fallback(d, buf, limit), + } +} + diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 71c2b38287e0e..011830ddb7882 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -44,6 +44,9 @@ pub struct Wrapping(#[stable(feature = "rust1", since = "1.0.0")] pub T); #[unstable(feature = "core", reason = "may be removed or relocated")] pub mod wrapping; +#[unstable(feature = "core", reason = "internal routines only exposed for testing")] +pub mod flt2dec; + /// Types that have a "zero" value. /// /// This trait is intended for use in conjunction with `Add`, as an identity: diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index 1566972275284..90c1e8b132e42 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -30,6 +30,7 @@ extern crate core; extern crate test; extern crate libc; extern crate rustc_unicode; +extern crate rand; mod any; mod atomic; diff --git a/src/libcoretest/num/flt2dec/bignum.rs b/src/libcoretest/num/flt2dec/bignum.rs new file mode 100644 index 0000000000000..09a1ed41dadd1 --- /dev/null +++ b/src/libcoretest/num/flt2dec/bignum.rs @@ -0,0 +1,160 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::prelude::v1::*; +use core::num::flt2dec::bignum::tests::Big8x3 as Big; + +#[test] +#[should_panic] +fn test_from_u64_overflow() { + Big::from_u64(0x1000000); +} + +#[test] +fn test_add() { + assert_eq!(*Big::from_small(3).add(&Big::from_small(4)), Big::from_small(7)); + assert_eq!(*Big::from_small(3).add(&Big::from_small(0)), Big::from_small(3)); + assert_eq!(*Big::from_small(0).add(&Big::from_small(3)), Big::from_small(3)); + assert_eq!(*Big::from_small(3).add(&Big::from_u64(0xfffe)), Big::from_u64(0x10001)); + assert_eq!(*Big::from_u64(0xfedc).add(&Big::from_u64(0x789)), Big::from_u64(0x10665)); + assert_eq!(*Big::from_u64(0x789).add(&Big::from_u64(0xfedc)), Big::from_u64(0x10665)); +} + +#[test] +#[should_panic] +fn test_add_overflow_1() { + Big::from_small(1).add(&Big::from_u64(0xffffff)); +} + +#[test] +#[should_panic] +fn test_add_overflow_2() { + Big::from_u64(0xffffff).add(&Big::from_small(1)); +} + +#[test] +fn test_sub() { + assert_eq!(*Big::from_small(7).sub(&Big::from_small(4)), Big::from_small(3)); + assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x789)), Big::from_u64(0xfedc)); + assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0xfedc)), Big::from_u64(0x789)); + assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10664)), Big::from_small(1)); + assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10665)), Big::from_small(0)); +} + +#[test] +#[should_panic] +fn test_sub_underflow_1() { + Big::from_u64(0x10665).sub(&Big::from_u64(0x10666)); +} + +#[test] +#[should_panic] +fn test_sub_underflow_2() { + Big::from_small(0).sub(&Big::from_u64(0x123456)); +} + +#[test] +fn test_mul_small() { + assert_eq!(*Big::from_small(7).mul_small(5), Big::from_small(35)); + assert_eq!(*Big::from_small(0xff).mul_small(0xff), Big::from_u64(0xfe01)); + assert_eq!(*Big::from_u64(0xffffff/13).mul_small(13), Big::from_u64(0xffffff)); +} + +#[test] +#[should_panic] +fn test_mul_small_overflow() { + Big::from_u64(0x800000).mul_small(2); +} + +#[test] +fn test_mul_pow2() { + assert_eq!(*Big::from_small(0x7).mul_pow2(4), Big::from_small(0x70)); + assert_eq!(*Big::from_small(0xff).mul_pow2(1), Big::from_u64(0x1fe)); + assert_eq!(*Big::from_small(0xff).mul_pow2(12), Big::from_u64(0xff000)); + assert_eq!(*Big::from_small(0x1).mul_pow2(23), Big::from_u64(0x800000)); + assert_eq!(*Big::from_u64(0x123).mul_pow2(0), Big::from_u64(0x123)); + assert_eq!(*Big::from_u64(0x123).mul_pow2(7), Big::from_u64(0x9180)); + assert_eq!(*Big::from_u64(0x123).mul_pow2(15), Big::from_u64(0x918000)); + assert_eq!(*Big::from_small(0).mul_pow2(23), Big::from_small(0)); +} + +#[test] +#[should_panic] +fn test_mul_pow2_overflow_1() { + Big::from_u64(0x1).mul_pow2(24); +} + +#[test] +#[should_panic] +fn test_mul_pow2_overflow_2() { + Big::from_u64(0x123).mul_pow2(16); +} + +#[test] +fn test_mul_digits() { + assert_eq!(*Big::from_small(3).mul_digits(&[5]), Big::from_small(15)); + assert_eq!(*Big::from_small(0xff).mul_digits(&[0xff]), Big::from_u64(0xfe01)); + assert_eq!(*Big::from_u64(0x123).mul_digits(&[0x56, 0x4]), Big::from_u64(0x4edc2)); + assert_eq!(*Big::from_u64(0x12345).mul_digits(&[0x67]), Big::from_u64(0x7530c3)); + assert_eq!(*Big::from_small(0x12).mul_digits(&[0x67, 0x45, 0x3]), Big::from_u64(0x3ae13e)); + assert_eq!(*Big::from_u64(0xffffff/13).mul_digits(&[13]), Big::from_u64(0xffffff)); + assert_eq!(*Big::from_small(13).mul_digits(&[0x3b, 0xb1, 0x13]), Big::from_u64(0xffffff)); +} + +#[test] +#[should_panic] +fn test_mul_digits_overflow_1() { + Big::from_u64(0x800000).mul_digits(&[2]); +} + +#[test] +#[should_panic] +fn test_mul_digits_overflow_2() { + Big::from_u64(0x1000).mul_digits(&[0, 0x10]); +} + +#[test] +fn test_div_rem_small() { + let as_val = |(q, r): (&mut Big, u8)| (q.clone(), r); + assert_eq!(as_val(Big::from_small(0xff).div_rem_small(15)), (Big::from_small(17), 0)); + assert_eq!(as_val(Big::from_small(0xff).div_rem_small(16)), (Big::from_small(15), 15)); + assert_eq!(as_val(Big::from_small(3).div_rem_small(40)), (Big::from_small(0), 3)); + assert_eq!(as_val(Big::from_u64(0xffffff).div_rem_small(123)), + (Big::from_u64(0xffffff / 123), (0xffffffu64 % 123) as u8)); + assert_eq!(as_val(Big::from_u64(0x10000).div_rem_small(123)), + (Big::from_u64(0x10000 / 123), (0x10000u64 % 123) as u8)); +} + +#[test] +fn test_is_zero() { + assert!(Big::from_small(0).is_zero()); + assert!(!Big::from_small(3).is_zero()); + assert!(!Big::from_u64(0x123).is_zero()); + assert!(!Big::from_u64(0xffffff).sub(&Big::from_u64(0xfffffe)).is_zero()); + assert!(Big::from_u64(0xffffff).sub(&Big::from_u64(0xffffff)).is_zero()); +} + +#[test] +fn test_ord() { + assert!(Big::from_u64(0) < Big::from_u64(0xffffff)); + assert!(Big::from_u64(0x102) < Big::from_u64(0x201)); +} + +#[test] +fn test_fmt() { + assert_eq!(format!("{:?}", Big::from_u64(0)), "0x0"); + assert_eq!(format!("{:?}", Big::from_u64(0x1)), "0x1"); + assert_eq!(format!("{:?}", Big::from_u64(0x12)), "0x12"); + assert_eq!(format!("{:?}", Big::from_u64(0x123)), "0x1_23"); + assert_eq!(format!("{:?}", Big::from_u64(0x1234)), "0x12_34"); + assert_eq!(format!("{:?}", Big::from_u64(0x12345)), "0x1_23_45"); + assert_eq!(format!("{:?}", Big::from_u64(0x123456)), "0x12_34_56"); +} + diff --git a/src/libcoretest/num/flt2dec/estimator.rs b/src/libcoretest/num/flt2dec/estimator.rs new file mode 100644 index 0000000000000..3b9d777a98095 --- /dev/null +++ b/src/libcoretest/num/flt2dec/estimator.rs @@ -0,0 +1,61 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::num::Float; +use core::num::flt2dec::estimator::*; + +#[test] +fn test_estimate_scaling_factor() { + macro_rules! assert_almost_eq { + ($actual:expr, $expected:expr) => ({ + let actual = $actual; + let expected = $expected; + println!("{} - {} = {} - {} = {}", stringify!($expected), stringify!($actual), + expected, actual, expected - actual); + assert!(expected == actual || expected == actual + 1, + "expected {}, actual {}", expected, actual); + }) + } + + assert_almost_eq!(estimate_scaling_factor(1, 0), 0); + assert_almost_eq!(estimate_scaling_factor(2, 0), 1); + assert_almost_eq!(estimate_scaling_factor(10, 0), 1); + assert_almost_eq!(estimate_scaling_factor(11, 0), 2); + assert_almost_eq!(estimate_scaling_factor(100, 0), 2); + assert_almost_eq!(estimate_scaling_factor(101, 0), 3); + assert_almost_eq!(estimate_scaling_factor(10000000000000000000, 0), 19); + assert_almost_eq!(estimate_scaling_factor(10000000000000000001, 0), 20); + + // 1/2^20 = 0.00000095367... + assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000, -20), -6); + assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000 + 1, -20), -5); + assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000, -20), -5); + assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000 + 1, -20), -4); + assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000, -20), -4); + assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000 + 1, -20), -3); + assert_almost_eq!(estimate_scaling_factor(1048575, -20), 0); + assert_almost_eq!(estimate_scaling_factor(1048576, -20), 0); + assert_almost_eq!(estimate_scaling_factor(1048577, -20), 1); + assert_almost_eq!(estimate_scaling_factor(10485759999999999999, -20), 13); + assert_almost_eq!(estimate_scaling_factor(10485760000000000000, -20), 13); + assert_almost_eq!(estimate_scaling_factor(10485760000000000001, -20), 14); + + // extreme values: + // 2^-1074 = 4.94065... * 10^-324 + // (2^53-1) * 2^971 = 1.79763... * 10^308 + assert_almost_eq!(estimate_scaling_factor(1, -1074), -323); + assert_almost_eq!(estimate_scaling_factor(0x1fffffffffffff, 971), 309); + + for i in -1074..972 { + let expected = Float::ldexp(1.0, i).log10().ceil(); + assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16); + } +} + diff --git a/src/libcoretest/num/flt2dec/mod.rs b/src/libcoretest/num/flt2dec/mod.rs new file mode 100644 index 0000000000000..07880b22ce5a8 --- /dev/null +++ b/src/libcoretest/num/flt2dec/mod.rs @@ -0,0 +1,1158 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::prelude::v1::*; +use std::{str, mem, i16, f32, f64, fmt}; +use std::num::Float as StdFloat; +use std::slice::bytes; +use std::__rand as rand; +use rand::{Rand, XorShiftRng}; +use rand::distributions::{IndependentSample, Range}; + +use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded}; +use core::num::flt2dec::{MAX_SIG_DIGITS, round_up, Part, Formatted, Sign}; +use core::num::flt2dec::{to_shortest_str, to_shortest_exp_str, + to_exact_exp_str, to_exact_fixed_str}; + +pub use test::Bencher; + +mod estimator; +mod bignum; +mod strategy { + mod dragon; + mod grisu; +} + +pub fn decode_finite(v: T) -> Decoded { + match decode(v).1 { + FullDecoded::Finite(decoded) => decoded, + full_decoded => panic!("expected finite, got {:?} instead", full_decoded) + } +} + +macro_rules! check_shortest { + ($f:ident($v:expr) => $buf:expr, $exp:expr) => ( + check_shortest!($f($v) => $buf, $exp; + "shortest mismatch for v={v}: actual {actual:?}, expected {expected:?}", + v = stringify!($v)) + ); + + ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr) => ( + check_shortest!($f{$($k: $v),+} => $buf, $exp; + "shortest mismatch for {v:?}: actual {actual:?}, expected {expected:?}", + v = Decoded { $($k: $v),+ }) + ); + + ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ + let mut buf = [b'_'; MAX_SIG_DIGITS]; + let (len, k) = $f(&decode_finite($v), &mut buf); + assert!((&buf[..len], k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k), + expected = (str::from_utf8($buf).unwrap(), $exp), + $($key = $val),*); + }); + + ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let mut buf = [b'_'; MAX_SIG_DIGITS]; + let (len, k) = $f(&Decoded { $($k: $v),+ }, &mut buf); + assert!((&buf[..len], k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k), + expected = (str::from_utf8($buf).unwrap(), $exp), + $($key = $val),*); + }) +} + +macro_rules! try_exact { + ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let (len, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN); + assert!((&$buf[..len], k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k), + expected = (str::from_utf8($expected).unwrap(), $expectedk), + $($key = $val),*); + }) +} + +macro_rules! try_fixed { + ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let (len, k) = $f($decoded, &mut $buf[..], $request); + assert!((&$buf[..len], k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k), + expected = (str::from_utf8($expected).unwrap(), $expectedk), + $($key = $val),*); + }) +} + +fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16) + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + // use a large enough buffer + let mut buf = [b'_'; 1024]; + let mut expected_ = [b'_'; 1024]; + + let decoded = decode_finite(v); + let cut = expected.iter().position(|&c| c == b' '); + + // check significant digits + for i in 1..cut.unwrap_or(expected.len() - 1) { + bytes::copy_memory(&expected[..i], &mut expected_); + let mut expectedk_ = expectedk; + if expected[i] >= b'5' { + // if this returns true, expected_[..i] is all `9`s and being rounded up. + // we should always return `100..00` (`i` digits) instead, since that's + // what we can came up with `i` digits anyway. `round_up` assumes that + // the adjustment to the length is done by caller, which we simply ignore. + if let Some(_) = round_up(&mut expected_, i) { expectedk_ += 1; } + } + + try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_; + "exact sigdigit mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + try_fixed!(f(&decoded) => &mut buf, expectedk_ - i as i16, &expected_[..i], expectedk_; + "fixed sigdigit mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + } + + // check exact rounding for zero- and negative-width cases + let start; + if expected[0] >= b'5' { + try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1; + "zero-width rounding-up mismatch for v={v}: \ + actual {actual:?}, expected {expected:?}", + v = vstr); + start = 1; + } else { + start = 0; + } + for i in start..-10 { + try_fixed!(f(&decoded) => &mut buf, expectedk - i, b"", expectedk; + "rounding-down mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = -i); + } + + // check infinite zero digits + if let Some(cut) = cut { + for i in cut..expected.len()-1 { + bytes::copy_memory(&expected[..cut], &mut expected_); + for c in &mut expected_[cut..i] { *c = b'0'; } + + try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk; + "exact infzero mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + try_fixed!(f(&decoded) => &mut buf, expectedk - i as i16, &expected_[..i], expectedk; + "fixed infzero mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + } + } +} + +fn check_exact_one(mut f: F, x: T, e: isize, tstr: &str, expected: &[u8], expectedk: i16) + where T: DecodableFloat + StdFloat + fmt::Display, + F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + // use a large enough buffer + let mut buf = [b'_'; 1024]; + let v: T = StdFloat::ldexp(x, e); + let decoded = decode_finite(v); + + try_exact!(f(&decoded) => &mut buf, &expected, expectedk; + "exact mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", + x = x, e = e, t = tstr); + try_fixed!(f(&decoded) => &mut buf, expectedk - expected.len() as i16, &expected, expectedk; + "fixed mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", + x = x, e = e, t = tstr); +} + +macro_rules! check_exact { + ($f:ident($v:expr) => $buf:expr, $exp:expr) => ( + check_exact(|d,b,k| $f(d,b,k), $v, stringify!($v), $buf, $exp) + ) +} + +macro_rules! check_exact_one { + ($f:ident($x:expr, $e:expr; $t:ty) => $buf:expr, $exp:expr) => ( + check_exact_one::<_, $t>(|d,b,k| $f(d,b,k), $x, $e, stringify!($t), $buf, $exp) + ) +} + +// in the following comments, three numbers are spaced by 1 ulp apart, +// and the second one is being formatted. +// +// some tests are derived from [1]. +// +// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion +// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z + +pub fn f32_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + // 0.0999999940395355224609375 + // 0.100000001490116119384765625 + // 0.10000000894069671630859375 + check_shortest!(f(0.1f32) => b"1", 0); + + // 0.333333313465118408203125 + // 0.3333333432674407958984375 (1/3 in the default rounding) + // 0.33333337306976318359375 + check_shortest!(f(1.0f32/3.0) => b"33333334", 0); + + // 10^1 * 0.31415917873382568359375 + // 10^1 * 0.31415920257568359375 + // 10^1 * 0.31415922641754150390625 + check_shortest!(f(3.141592f32) => b"3141592", 1); + + // 10^18 * 0.31415916243714048 + // 10^18 * 0.314159196796878848 + // 10^18 * 0.314159231156617216 + check_shortest!(f(3.141592e17f32) => b"3141592", 18); + + // 10^39 * 0.340282326356119256160033759537265639424 + // 10^39 * 0.34028234663852885981170418348451692544 + // 10^39 * 0.340282366920938463463374607431768211456 + check_shortest!(f(f32::MAX) => b"34028235", 39); + + // 10^-37 * 0.1175494210692441075487029444849287348827... + // 10^-37 * 0.1175494350822287507968736537222245677818... + // 10^-37 * 0.1175494490952133940450443629595204006810... + check_shortest!(f(f32::MIN_POSITIVE) => b"11754944", -37); + + // 10^-44 * 0 + // 10^-44 * 0.1401298464324817070923729583289916131280... + // 10^-44 * 0.2802596928649634141847459166579832262560... + let minf32: f32 = StdFloat::ldexp(1.0, -149); + check_shortest!(f(minf32) => b"1", -44); +} + +pub fn f32_exact_sanity_test(mut f: F) + where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + let minf32: f32 = StdFloat::ldexp(1.0, -149); + + check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0); + check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0); + check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1); + check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18); + check_exact!(f(f32::MAX) => b"34028234663852885981170418348451692544 ", 39); + check_exact!(f(f32::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37); + check_exact!(f(minf32) => b"1401298464324817070923729583289916131280", -44); + + // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP + check_exact_one!(f(12676506.0, -102; f32) => b"2", -23); + check_exact_one!(f(12676506.0, -103; f32) => b"12", -23); + check_exact_one!(f(15445013.0, 86; f32) => b"119", 34); + check_exact_one!(f(13734123.0, -138; f32) => b"3941", -34); + check_exact_one!(f(12428269.0, -130; f32) => b"91308", -32); + check_exact_one!(f(15334037.0, -146; f32) => b"171900", -36); + check_exact_one!(f(11518287.0, -41; f32) => b"5237910", -5); + check_exact_one!(f(12584953.0, -145; f32) => b"28216440", -36); + check_exact_one!(f(15961084.0, -125; f32) => b"375243281", -30); + check_exact_one!(f(14915817.0, -146; f32) => b"1672120916", -36); + check_exact_one!(f(10845484.0, -102; f32) => b"21388945814", -23); + check_exact_one!(f(16431059.0, -61; f32) => b"712583594561", -11); + + // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP + check_exact_one!(f(16093626.0, 69; f32) => b"1", 29); + check_exact_one!(f( 9983778.0, 25; f32) => b"34", 15); + check_exact_one!(f(12745034.0, 104; f32) => b"259", 39); + check_exact_one!(f(12706553.0, 72; f32) => b"6001", 29); + check_exact_one!(f(11005028.0, 45; f32) => b"38721", 21); + check_exact_one!(f(15059547.0, 71; f32) => b"355584", 29); + check_exact_one!(f(16015691.0, -99; f32) => b"2526831", -22); + check_exact_one!(f( 8667859.0, 56; f32) => b"62458507", 24); + check_exact_one!(f(14855922.0, -82; f32) => b"307213267", -17); + check_exact_one!(f(14855922.0, -83; f32) => b"1536066333", -17); + check_exact_one!(f(10144164.0, -110; f32) => b"78147796834", -26); + check_exact_one!(f(13248074.0, 95; f32) => b"524810279937", 36); +} + +pub fn f64_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + // 0.0999999999999999777955395074968691915273... + // 0.1000000000000000055511151231257827021181... + // 0.1000000000000000333066907387546962127089... + check_shortest!(f(0.1f64) => b"1", 0); + + // this example is explicitly mentioned in the paper. + // 10^3 * 0.0999999999999999857891452847979962825775... + // 10^3 * 0.1 (exact) + // 10^3 * 0.1000000000000000142108547152020037174224... + check_shortest!(f(100.0f64) => b"1", 3); + + // 0.3333333333333332593184650249895639717578... + // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding) + // 0.3333333333333333703407674875052180141210... + check_shortest!(f(1.0f64/3.0) => b"3333333333333333", 0); + + // explicit test case for equally closest representations. + // Dragon has its own tie-breaking rule; Grisu should fall back. + // 10^1 * 0.1000007629394531027955395074968691915273... + // 10^1 * 0.100000762939453125 (exact) + // 10^1 * 0.1000007629394531472044604925031308084726... + check_shortest!(f(1.00000762939453125f64) => b"10000076293945313", 1); + + // 10^1 * 0.3141591999999999718085064159822650253772... + // 10^1 * 0.3141592000000000162174274009885266423225... + // 10^1 * 0.3141592000000000606263483859947882592678... + check_shortest!(f(3.141592f64) => b"3141592", 1); + + // 10^18 * 0.314159199999999936 + // 10^18 * 0.3141592 (exact) + // 10^18 * 0.314159200000000064 + check_shortest!(f(3.141592e17f64) => b"3141592", 18); + + // pathological case: high = 10^23 (exact). tie breaking should always prefer that. + // 10^24 * 0.099999999999999974834176 + // 10^24 * 0.099999999999999991611392 + // 10^24 * 0.100000000000000008388608 + check_shortest!(f(1.0e23f64) => b"1", 24); + + // 10^309 * 0.1797693134862315508561243283845062402343... + // 10^309 * 0.1797693134862315708145274237317043567980... + // 10^309 * 0.1797693134862315907729305190789024733617... + check_shortest!(f(f64::MAX) => b"17976931348623157", 309); + + // 10^-307 * 0.2225073858507200889024586876085859887650... + // 10^-307 * 0.2225073858507201383090232717332404064219... + // 10^-307 * 0.2225073858507201877155878558578948240788... + check_shortest!(f(f64::MIN_POSITIVE) => b"22250738585072014", -307); + + // 10^-323 * 0 + // 10^-323 * 0.4940656458412465441765687928682213723650... + // 10^-323 * 0.9881312916824930883531375857364427447301... + let minf64: f64 = StdFloat::ldexp(1.0, -1074); + check_shortest!(f(minf64) => b"5", -323); +} + +pub fn f64_exact_sanity_test(mut f: F) + where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + let minf64: f64 = StdFloat::ldexp(1.0, -1074); + + check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0); + check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0); + check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0); + check_exact!(f(100.0f64) => b"1 ", 3); + check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3); + check_exact!(f(1.0f64/3.0) => b"3333333333333333148296162562473909929394", 0); + check_exact!(f(3.141592f64) => b"3141592000000000162174274009885266423225", 1); + check_exact!(f(3.141592e17f64) => b"3141592 ", 18); + check_exact!(f(1.0e23f64) => b"99999999999999991611392 ", 23); + check_exact!(f(f64::MAX) => b"1797693134862315708145274237317043567980", 309); + check_exact!(f(f64::MIN_POSITIVE) => b"2225073858507201383090232717332404064219", -307); + check_exact!(f(minf64) => b"4940656458412465441765687928682213723650\ + 5980261432476442558568250067550727020875\ + 1865299836361635992379796564695445717730\ + 9266567103559397963987747960107818781263\ + 0071319031140452784581716784898210368871\ + 8636056998730723050006387409153564984387\ + 3124733972731696151400317153853980741262\ + 3856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480\ + 1297099995419319894090804165633245247571\ + 4786901472678015935523861155013480352649\ + 3472019379026810710749170333222684475333\ + 5720832431936092382893458368060106011506\ + 1698097530783422773183292479049825247307\ + 7637592724787465608477820373446969953364\ + 7017972677717585125660551199131504891101\ + 4510378627381672509558373897335989936648\ + 0994116420570263709027924276754456522908\ + 7538682506419718265533447265625 ", -323); + + // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP + check_exact_one!(f(8511030020275656.0, -342; f64) => b"9", -87); + check_exact_one!(f(5201988407066741.0, -824; f64) => b"46", -232); + check_exact_one!(f(6406892948269899.0, 237; f64) => b"141", 88); + check_exact_one!(f(8431154198732492.0, 72; f64) => b"3981", 38); + check_exact_one!(f(6475049196144587.0, 99; f64) => b"41040", 46); + check_exact_one!(f(8274307542972842.0, 726; f64) => b"292084", 235); + check_exact_one!(f(5381065484265332.0, -456; f64) => b"2891946", -121); + check_exact_one!(f(6761728585499734.0, -1057; f64) => b"43787718", -302); + check_exact_one!(f(7976538478610756.0, 376; f64) => b"122770163", 130); + check_exact_one!(f(5982403858958067.0, 377; f64) => b"1841552452", 130); + check_exact_one!(f(5536995190630837.0, 93; f64) => b"54835744350", 44); + check_exact_one!(f(7225450889282194.0, 710; f64) => b"389190181146", 230); + check_exact_one!(f(7225450889282194.0, 709; f64) => b"1945950905732", 230); + check_exact_one!(f(8703372741147379.0, 117; f64) => b"14460958381605", 52); + check_exact_one!(f(8944262675275217.0, -1001; f64) => b"417367747458531", -285); + check_exact_one!(f(7459803696087692.0, -707; f64) => b"1107950772878888", -196); + check_exact_one!(f(6080469016670379.0, -381; f64) => b"12345501366327440", -98); + check_exact_one!(f(8385515147034757.0, 721; f64) => b"925031711960365024", 233); + check_exact_one!(f(7514216811389786.0, -828; f64) => b"4198047150284889840", -233); + check_exact_one!(f(8397297803260511.0, -345; f64) => b"11716315319786511046", -87); + check_exact_one!(f(6733459239310543.0, 202; f64) => b"432810072844612493629", 77); + check_exact_one!(f(8091450587292794.0, -473; f64) => b"3317710118160031081518", -126); + + // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP + check_exact_one!(f(6567258882077402.0, 952; f64) => b"3", 303); + check_exact_one!(f(6712731423444934.0, 535; f64) => b"76", 177); + check_exact_one!(f(6712731423444934.0, 534; f64) => b"378", 177); + check_exact_one!(f(5298405411573037.0, -957; f64) => b"4350", -272); + check_exact_one!(f(5137311167659507.0, -144; f64) => b"23037", -27); + check_exact_one!(f(6722280709661868.0, 363; f64) => b"126301", 126); + check_exact_one!(f(5344436398034927.0, -169; f64) => b"7142211", -35); + check_exact_one!(f(8369123604277281.0, -853; f64) => b"13934574", -240); + check_exact_one!(f(8995822108487663.0, -780; f64) => b"141463449", -218); + check_exact_one!(f(8942832835564782.0, -383; f64) => b"4539277920", -99); + check_exact_one!(f(8942832835564782.0, -384; f64) => b"22696389598", -99); + check_exact_one!(f(8942832835564782.0, -385; f64) => b"113481947988", -99); + check_exact_one!(f(6965949469487146.0, -249; f64) => b"7700366561890", -59); + check_exact_one!(f(6965949469487146.0, -250; f64) => b"38501832809448", -59); + check_exact_one!(f(6965949469487146.0, -251; f64) => b"192509164047238", -59); + check_exact_one!(f(7487252720986826.0, 548; f64) => b"6898586531774201", 181); + check_exact_one!(f(5592117679628511.0, 164; f64) => b"13076622631878654", 66); + check_exact_one!(f(8887055249355788.0, 665; f64) => b"136052020756121240", 217); + check_exact_one!(f(6994187472632449.0, 690; f64) => b"3592810217475959676", 224); + check_exact_one!(f(8797576579012143.0, 588; f64) => b"89125197712484551899", 193); + check_exact_one!(f(7363326733505337.0, 272; f64) => b"558769757362301140950", 98); + check_exact_one!(f(8549497411294502.0, -448; f64) => b"1176257830728540379990", -118); +} + +pub fn more_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, + exp: 0, inclusive: true} => b"1", 18); + check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, + exp: 0, inclusive: false} => b"99999999999999999", 17); +} + +fn iterate(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + V: FnMut(usize) -> Decoded { + assert!(k <= 1024); + + let mut npassed = 0; // f(x) = Some(g(x)) + let mut nignored = 0; // f(x) = None + + for i in 0..n { + if (i & 0xfffff) == 0 { + println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})", + i, n, nignored, npassed, i - nignored - npassed); + } + + let decoded = v(i); + let mut buf1 = [0; 1024]; + if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) { + let mut buf2 = [0; 1024]; + let (len2, e2) = g(&decoded, &mut buf2[..k]); + if e1 == e2 && &buf1[..len1] == &buf2[..len2] { + npassed += 1; + } else { + println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}", + i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1, + str::from_utf8(&buf2[..len2]).unwrap(), e2); + } + } else { + nignored += 1; + } + } + println!("{}({}): done, ignored={} passed={} failed={}", + func, k, nignored, npassed, n - nignored - npassed); + assert!(nignored + npassed == n, + "{}({}): {} out of {} values returns an incorrect value!", + func, k, n - nignored - npassed, n); + (npassed, nignored) +} + +pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); + let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000); + iterate("f32_random_equivalence_test", k, n, f, g, |_| { + let i: u32 = f32_range.ind_sample(&mut rng); + let x: f32 = unsafe {mem::transmute(i)}; + decode_finite(x) + }); +} + +pub fn f64_random_equivalence_test(f: F, g: G, k: usize, n: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); + let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); + iterate("f64_random_equivalence_test", k, n, f, g, |_| { + let i: u64 = f64_range.ind_sample(&mut rng); + let x: f64 = unsafe {mem::transmute(i)}; + decode_finite(x) + }); +} + +pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values, + // so why not simply testing all of them? + // + // this is of course very stressful (and thus should be behind an `#[ignore]` attribute), + // but with `-O3 -C lto` this only takes about two hours or so. + + // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges + let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test", + k, 0x7f7f_ffff, f, g, |i: usize| { + let x: f32 = unsafe {mem::transmute(i as u32 + 1)}; + decode_finite(x) + }); + assert_eq!((npassed, nignored), (2121451879, 17643160)); +} + +fn to_string_with_parts(mut f: F) -> String + where F: for<'a> FnMut(&'a mut [u8], &'a mut [Part<'a>]) -> Formatted<'a> { + let mut buf = [0; 1024]; + let mut parts = [Part::Zero(0); 16]; + let formatted = f(&mut buf, &mut parts); + let mut ret = vec![0; formatted.len()]; + assert_eq!(formatted.write(&mut ret), Some(ret.len())); + String::from_utf8(ret).unwrap() +} + +pub fn to_shortest_str_test(mut f_: F) + where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String + where T: DecodableFloat + StdFloat, + F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + to_string_with_parts(|buf, parts| to_shortest_str(|d,b| f(d,b), v, sign, + frac_digits, upper, buf, parts)) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 0, false), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 0, false), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0, false), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0, false), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0, false), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 0, false), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0, false), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0, false), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1, true), "0.0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1, true), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0.0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, true), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.00000000"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8, true), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, true), "-0.00000000"); + + assert_eq!(to_string(f, 1.0/0.0, Minus, 0, false), "inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 0, true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlus, 0, false), "+inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 0, true), "+INF"); + assert_eq!(to_string(f, 0.0/0.0, Minus, 0, false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 1, true), "NAN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 64, true), "NAN"); + assert_eq!(to_string(f, -1.0/0.0, Minus, 0, false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 1, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlus, 8, false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-INF"); + + assert_eq!(to_string(f, 3.14, Minus, 0, false), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, 0, false), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0, false), "+3.14"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0, false), "+3.14"); + assert_eq!(to_string(f, -3.14, Minus, 0, false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusRaw, 0, false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0, false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0, false), "-3.14"); + assert_eq!(to_string(f, 3.14, Minus, 1, true), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, 2, true), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.140"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4, true), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8, true), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusRaw, 8, true), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8, true), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8, true), "-3.14000000"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0, false), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3, false), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 12, false), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13, false), "0.0000000000750"); + + assert_eq!(to_string(f, 1.9971e20, Minus, 0, false), "199710000000000000000"); + assert_eq!(to_string(f, 1.9971e20, Minus, 1, false), "199710000000000000000.0"); + assert_eq!(to_string(f, 1.9971e20, Minus, 8, false), "199710000000000000000.00000000"); + + assert_eq!(to_string(f, f32::MAX, Minus, 0, false), format!("34028235{:0>31}", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), format!("34028235{:0>31}.0", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 8, false), format!("34028235{:0>31}.00000000", "")); + + let minf32: f32 = StdFloat::ldexp(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0, false), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 45, false), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 46, false), format!("0.{:0>44}10", "")); + + assert_eq!(to_string(f, f64::MAX, Minus, 0, false), + format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 1, false), + format!("17976931348623157{:0>292}.0", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 8, false), + format!("17976931348623157{:0>292}.00000000", "")); + + let minf64: f64 = StdFloat::ldexp(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0, false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 324, false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 325, false), format!("0.{:0>323}50", "")); + + // very large output + assert_eq!(to_string(f, 1.1, Minus, 80000, false), format!("1.1{:0>79999}", "")); +} + +pub fn to_shortest_exp_str_test(mut f_: F) + where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String + where T: DecodableFloat + StdFloat, + F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + to_string_with_parts(|buf, parts| to_shortest_exp_str(|d,b| f(d,b), v, sign, + exp_bounds, upper, buf, parts)) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, (-4, 16), false), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, (-4, 16), false), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, (-4, 16), false), "+0"); + assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, (-4, 16), false), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, (-4, 16), false), "-0"); + assert_eq!(to_string(f, 0.0, Minus, ( 0, 0), true), "0E0"); + assert_eq!(to_string(f, 0.0, MinusRaw, ( 0, 0), false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, (-9, -5), true), "+0E0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, ( 5, 9), false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, ( 0, 0), true), "0E0"); + assert_eq!(to_string(f, -0.0, MinusRaw, ( 0, 0), false), "-0e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (-9, -5), true), "+0E0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, ( 5, 9), false), "-0e0"); + + assert_eq!(to_string(f, 1.0/0.0, Minus, (-4, 16), false), "inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, (-4, 16), true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlus, (-4, 16), false), "+inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, (-4, 16), true), "+INF"); + assert_eq!(to_string(f, 0.0/0.0, Minus, ( 0, 0), false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, ( 0, 0), true), "NAN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, (-9, -5), false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, ( 5, 9), true), "NAN"); + assert_eq!(to_string(f, -1.0/0.0, Minus, ( 0, 0), false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, ( 0, 0), true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlus, (-9, -5), false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, ( 5, 9), true), "-INF"); + + assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, (-4, 16), false), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, (-4, 16), false), "+3.14"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, (-4, 16), false), "+3.14"); + assert_eq!(to_string(f, -3.14, Minus, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusRaw, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlus, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, 3.14, Minus, ( 0, 0), true), "3.14E0"); + assert_eq!(to_string(f, 3.14, MinusRaw, ( 0, 0), false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, (-9, -5), true), "+3.14E0"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, ( 5, 9), false), "+3.14e0"); + assert_eq!(to_string(f, -3.14, Minus, ( 0, 0), true), "-3.14E0"); + assert_eq!(to_string(f, -3.14, MinusRaw, ( 0, 0), false), "-3.14e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, (-9, -5), true), "-3.14E0"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, ( 5, 9), false), "-3.14e0"); + + assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); + assert_eq!(to_string(f, 0.1, MinusRaw, (-4, 16), false), "0.1"); + assert_eq!(to_string(f, 0.1, MinusPlus, (-4, 16), false), "+0.1"); + assert_eq!(to_string(f, 0.1, MinusPlusRaw, (-4, 16), false), "+0.1"); + assert_eq!(to_string(f, -0.1, Minus, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, -0.1, MinusRaw, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, -0.1, MinusPlus, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, -0.1, MinusPlusRaw, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, 0.1, Minus, ( 0, 0), true), "1E-1"); + assert_eq!(to_string(f, 0.1, MinusRaw, ( 0, 0), false), "1e-1"); + assert_eq!(to_string(f, 0.1, MinusPlus, (-9, -5), true), "+1E-1"); + assert_eq!(to_string(f, 0.1, MinusPlusRaw, ( 5, 9), false), "+1e-1"); + assert_eq!(to_string(f, -0.1, Minus, ( 0, 0), true), "-1E-1"); + assert_eq!(to_string(f, -0.1, MinusRaw, ( 0, 0), false), "-1e-1"); + assert_eq!(to_string(f, -0.1, MinusPlus, (-9, -5), true), "-1E-1"); + assert_eq!(to_string(f, -0.1, MinusPlusRaw, ( 5, 9), false), "-1e-1"); + + assert_eq!(to_string(f, 7.5e-11, Minus, ( -4, 16), false), "7.5e-11"); + assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, (-10, 11), false), "7.5e-11"); + + assert_eq!(to_string(f, 1.9971e20, Minus, ( -4, 16), false), "1.9971e20"); + assert_eq!(to_string(f, 1.9971e20, Minus, (-20, 21), false), "199710000000000000000"); + assert_eq!(to_string(f, 1.9971e20, Minus, (-21, 20), false), "1.9971e20"); + + // the true value of 1.0e23f64 is less than 10^23, but that shouldn't matter here + assert_eq!(to_string(f, 1.0e23, Minus, (22, 23), false), "1e23"); + assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); + assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); + + assert_eq!(to_string(f, f32::MAX, Minus, ( -4, 16), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); + + let minf32: f32 = StdFloat::ldexp(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, ( -4, 16), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + + assert_eq!(to_string(f, f64::MAX, Minus, ( -4, 16), false), + "1.7976931348623157e308"); + assert_eq!(to_string(f, f64::MAX, Minus, (-308, 309), false), + format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), + "1.7976931348623157e308"); + + let minf64: f64 = StdFloat::ldexp(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, ( -4, 16), false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + + assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); +} + +pub fn to_exact_exp_str_test(mut f_: F) + where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String + where T: DecodableFloat + StdFloat, + F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + to_string_with_parts(|buf, parts| to_exact_exp_str(|d,b,l| f(d,b,l), v, sign, + ndigits, upper, buf, parts)) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 1, true), "0E0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1, false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0E0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, 1, true), "0E0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 1, false), "-0e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 1, true), "+0E0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 1, false), "-0e0"); + assert_eq!(to_string(f, 0.0, Minus, 2, true), "0.0E0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 2, false), "0.0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 2, true), "+0.0E0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 2, false), "+0.0e0"); + assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.0000000E0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8, false), "-0.0000000e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.0000000E0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, false), "-0.0000000e0"); + + assert_eq!(to_string(f, 1.0/0.0, Minus, 1, false), "inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 1, true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlus, 1, false), "+inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 1, true), "+INF"); + assert_eq!(to_string(f, 0.0/0.0, Minus, 8, false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 8, true), "NAN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 8, true), "NAN"); + assert_eq!(to_string(f, -1.0/0.0, Minus, 64, false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 64, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlus, 64, false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-INF"); + + assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0"); + assert_eq!(to_string(f, 3.14, MinusRaw, 1, false), "3e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 1, true), "+3E0"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 1, false), "+3e0"); + assert_eq!(to_string(f, -3.14, Minus, 2, true), "-3.1E0"); + assert_eq!(to_string(f, -3.14, MinusRaw, 2, false), "-3.1e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 2, true), "-3.1E0"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 2, false), "-3.1e0"); + assert_eq!(to_string(f, 3.14, Minus, 3, true), "3.14E0"); + assert_eq!(to_string(f, 3.14, MinusRaw, 3, false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.14E0"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 3, false), "+3.14e0"); + assert_eq!(to_string(f, -3.14, Minus, 4, true), "-3.140E0"); + assert_eq!(to_string(f, -3.14, MinusRaw, 4, false), "-3.140e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 4, true), "-3.140E0"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 4, false), "-3.140e0"); + + assert_eq!(to_string(f, 0.195, Minus, 1, false), "2e-1"); + assert_eq!(to_string(f, 0.195, MinusRaw, 1, true), "2E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 1, false), "+2e-1"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 1, true), "+2E-1"); + assert_eq!(to_string(f, -0.195, Minus, 2, false), "-2.0e-1"); + assert_eq!(to_string(f, -0.195, MinusRaw, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 2, false), "-2.0e-1"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, 0.195, Minus, 3, false), "1.95e-1"); + assert_eq!(to_string(f, 0.195, MinusRaw, 3, true), "1.95E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 3, false), "+1.95e-1"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 3, true), "+1.95E-1"); + assert_eq!(to_string(f, -0.195, Minus, 4, false), "-1.950e-1"); + assert_eq!(to_string(f, -0.195, MinusRaw, 4, true), "-1.950E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 4, false), "-1.950e-1"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 4, true), "-1.950E-1"); + + assert_eq!(to_string(f, 9.5, Minus, 1, false), "1e1"); + assert_eq!(to_string(f, 9.5, Minus, 2, false), "9.5e0"); + assert_eq!(to_string(f, 9.5, Minus, 3, false), "9.50e0"); + assert_eq!(to_string(f, 9.5, Minus, 30, false), "9.50000000000000000000000000000e0"); + + assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "1e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 2, false), "1.0e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 15, false), "1.00000000000000e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 16, false), "1.000000000000000e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 17, false), "1.0000000000000001e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 18, false), "1.00000000000000009e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 19, false), "1.000000000000000091e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 20, false), "1.0000000000000000906e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 21, false), "1.00000000000000009060e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 22, false), "1.000000000000000090597e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 23, false), "1.0000000000000000905970e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 24, false), "1.00000000000000009059697e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 25, false), "1.000000000000000090596966e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 26, false), "1.0000000000000000905969664e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 27, false), "1.00000000000000009059696640e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 30, false), "1.00000000000000009059696640000e25"); + + assert_eq!(to_string(f, 1.0e-6, Minus, 1, false), "1e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 2, false), "1.0e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 16, false), "1.000000000000000e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 17, false), "9.9999999999999995e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 18, false), "9.99999999999999955e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 19, false), "9.999999999999999547e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 20, false), "9.9999999999999995475e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 30, false), "9.99999999999999954748111825886e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 40, false), + "9.999999999999999547481118258862586856139e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 50, false), + "9.9999999999999995474811182588625868561393872369081e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 60, false), + "9.99999999999999954748111825886258685613938723690807819366455e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 70, false), + "9.999999999999999547481118258862586856139387236908078193664550781250000e-7"); + + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 8, false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 16, false), "3.402823466385289e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 32, false), "3.4028234663852885981170418348452e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 64, false), + "3.402823466385288598117041834845169254400000000000000000000000000e38"); + + let minf32: f32 = StdFloat::ldexp(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 1, false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, 2, false), "1.4e-45"); + assert_eq!(to_string(f, minf32, Minus, 4, false), "1.401e-45"); + assert_eq!(to_string(f, minf32, Minus, 8, false), "1.4012985e-45"); + assert_eq!(to_string(f, minf32, Minus, 16, false), "1.401298464324817e-45"); + assert_eq!(to_string(f, minf32, Minus, 32, false), "1.4012984643248170709237295832899e-45"); + assert_eq!(to_string(f, minf32, Minus, 64, false), + "1.401298464324817070923729583289916131280261941876515771757068284e-45"); + assert_eq!(to_string(f, minf32, Minus, 128, false), + "1.401298464324817070923729583289916131280261941876515771757068283\ + 8897910826858606014866381883621215820312500000000000000000000000e-45"); + + assert_eq!(to_string(f, f64::MAX, Minus, 1, false), "2e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 2, false), "1.8e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 4, false), "1.798e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 8, false), "1.7976931e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 16, false), "1.797693134862316e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 32, false), "1.7976931348623157081452742373170e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 64, false), + "1.797693134862315708145274237317043567980705675258449965989174768e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 128, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432133e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 256, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 512, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 2620414472316873817718091929988125040402618412485836800000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000e308"); + + // okay, this is becoming tough. fortunately for us, this is almost the worst case. + let minf64: f64 = StdFloat::ldexp(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 1, false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, 2, false), "4.9e-324"); + assert_eq!(to_string(f, minf64, Minus, 4, false), "4.941e-324"); + assert_eq!(to_string(f, minf64, Minus, 8, false), "4.9406565e-324"); + assert_eq!(to_string(f, minf64, Minus, 16, false), "4.940656458412465e-324"); + assert_eq!(to_string(f, minf64, Minus, 32, false), "4.9406564584124654417656879286822e-324"); + assert_eq!(to_string(f, minf64, Minus, 64, false), + "4.940656458412465441765687928682213723650598026143247644255856825e-324"); + assert_eq!(to_string(f, minf64, Minus, 128, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671e-324"); + assert_eq!(to_string(f, minf64, Minus, 256, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696e-324"); + assert_eq!(to_string(f, minf64, Minus, 512, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696\ + 1514003171538539807412623856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480129709999541931989409080\ + 4165633245247571478690147267801593552386115501348035264934720193\ + 7902681071074917033322268447533357208324319360923828934583680601e-324"); + assert_eq!(to_string(f, minf64, Minus, 1024, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696\ + 1514003171538539807412623856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480129709999541931989409080\ + 4165633245247571478690147267801593552386115501348035264934720193\ + 7902681071074917033322268447533357208324319360923828934583680601\ + 0601150616980975307834227731832924790498252473077637592724787465\ + 6084778203734469699533647017972677717585125660551199131504891101\ + 4510378627381672509558373897335989936648099411642057026370902792\ + 4276754456522908753868250641971826553344726562500000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000e-324"); + + // very large output + assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>79999}e0", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("1.{:0>79999}e1", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>79999}e0", "")); + assert_eq!(to_string(f, 1.0e-1, Minus, 80000, false), + format!("1.000000000000000055511151231257827021181583404541015625{:0>79945}\ + e-1", "")); + assert_eq!(to_string(f, 1.0e-20, Minus, 80000, false), + format!("9.999999999999999451532714542095716517295037027873924471077157760\ + 66783064379706047475337982177734375{:0>79901}e-21", "")); +} + +pub fn to_exact_fixed_str_test(mut f_: F) + where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String + where T: DecodableFloat + StdFloat, + F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + to_string_with_parts(|buf, parts| to_exact_fixed_str(|d,b,l| f(d,b,l), v, sign, + frac_digits, upper, buf, parts)) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 0, false), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 0, false), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0, false), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0, false), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0, false), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 0, false), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0, false), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0, false), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1, true), "0.0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1, true), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0.0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, true), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.00000000"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8, true), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, true), "-0.00000000"); + + assert_eq!(to_string(f, 1.0/0.0, Minus, 0, false), "inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 1, true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlus, 8, false), "+inf"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 64, true), "+INF"); + assert_eq!(to_string(f, 0.0/0.0, Minus, 0, false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 1, true), "NAN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "nan"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 64, true), "NAN"); + assert_eq!(to_string(f, -1.0/0.0, Minus, 0, false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 1, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlus, 8, false), "-inf"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-INF"); + + assert_eq!(to_string(f, 3.14, Minus, 0, false), "3"); + assert_eq!(to_string(f, 3.14, MinusRaw, 0, false), "3"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0, false), "+3"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0, false), "+3"); + assert_eq!(to_string(f, -3.14, Minus, 0, false), "-3"); + assert_eq!(to_string(f, -3.14, MinusRaw, 0, false), "-3"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0, false), "-3"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0, false), "-3"); + assert_eq!(to_string(f, 3.14, Minus, 1, true), "3.1"); + assert_eq!(to_string(f, 3.14, MinusRaw, 2, true), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.140"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4, true), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8, true), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusRaw, 8, true), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8, true), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8, true), "-3.14000000"); + + assert_eq!(to_string(f, 0.195, Minus, 0, false), "0"); + assert_eq!(to_string(f, 0.195, MinusRaw, 0, false), "0"); + assert_eq!(to_string(f, 0.195, MinusPlus, 0, false), "+0"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 0, false), "+0"); + assert_eq!(to_string(f, -0.195, Minus, 0, false), "-0"); + assert_eq!(to_string(f, -0.195, MinusRaw, 0, false), "-0"); + assert_eq!(to_string(f, -0.195, MinusPlus, 0, false), "-0"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 0, false), "-0"); + assert_eq!(to_string(f, 0.195, Minus, 1, true), "0.2"); + assert_eq!(to_string(f, 0.195, MinusRaw, 2, true), "0.20"); + assert_eq!(to_string(f, 0.195, MinusPlus, 3, true), "+0.195"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 4, true), "+0.1950"); + assert_eq!(to_string(f, -0.195, Minus, 5, true), "-0.19500"); + assert_eq!(to_string(f, -0.195, MinusRaw, 6, true), "-0.195000"); + assert_eq!(to_string(f, -0.195, MinusPlus, 7, true), "-0.1950000"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 8, true), "-0.19500000"); + + assert_eq!(to_string(f, 999.5, Minus, 0, false), "1000"); + assert_eq!(to_string(f, 999.5, Minus, 1, false), "999.5"); + assert_eq!(to_string(f, 999.5, Minus, 2, false), "999.50"); + assert_eq!(to_string(f, 999.5, Minus, 3, false), "999.500"); + assert_eq!(to_string(f, 999.5, Minus, 30, false), "999.500000000000000000000000000000"); + + assert_eq!(to_string(f, 0.95, Minus, 0, false), "1"); + assert_eq!(to_string(f, 0.95, Minus, 1, false), "0.9"); // because it really is less than 0.95 + assert_eq!(to_string(f, 0.95, Minus, 2, false), "0.95"); + assert_eq!(to_string(f, 0.95, Minus, 3, false), "0.950"); + assert_eq!(to_string(f, 0.95, Minus, 10, false), "0.9500000000"); + assert_eq!(to_string(f, 0.95, Minus, 30, false), "0.949999999999999955591079014994"); + + assert_eq!(to_string(f, 0.095, Minus, 0, false), "0"); + assert_eq!(to_string(f, 0.095, Minus, 1, false), "0.1"); + assert_eq!(to_string(f, 0.095, Minus, 2, false), "0.10"); + assert_eq!(to_string(f, 0.095, Minus, 3, false), "0.095"); + assert_eq!(to_string(f, 0.095, Minus, 4, false), "0.0950"); + assert_eq!(to_string(f, 0.095, Minus, 10, false), "0.0950000000"); + assert_eq!(to_string(f, 0.095, Minus, 30, false), "0.095000000000000001110223024625"); + + assert_eq!(to_string(f, 0.0095, Minus, 0, false), "0"); + assert_eq!(to_string(f, 0.0095, Minus, 1, false), "0.0"); + assert_eq!(to_string(f, 0.0095, Minus, 2, false), "0.01"); + assert_eq!(to_string(f, 0.0095, Minus, 3, false), "0.009"); // really is less than 0.0095 + assert_eq!(to_string(f, 0.0095, Minus, 4, false), "0.0095"); + assert_eq!(to_string(f, 0.0095, Minus, 5, false), "0.00950"); + assert_eq!(to_string(f, 0.0095, Minus, 10, false), "0.0095000000"); + assert_eq!(to_string(f, 0.0095, Minus, 30, false), "0.009499999999999999764077607267"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0, false), "0"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3, false), "0.000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 10, false), "0.0000000001"); + assert_eq!(to_string(f, 7.5e-11, Minus, 11, false), "0.00000000007"); // ditto + assert_eq!(to_string(f, 7.5e-11, Minus, 12, false), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13, false), "0.0000000000750"); + assert_eq!(to_string(f, 7.5e-11, Minus, 20, false), "0.00000000007500000000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 30, false), "0.000000000074999999999999999501"); + + assert_eq!(to_string(f, 1.0e25, Minus, 0, false), "10000000000000000905969664"); + assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "10000000000000000905969664.0"); + assert_eq!(to_string(f, 1.0e25, Minus, 3, false), "10000000000000000905969664.000"); + + assert_eq!(to_string(f, 1.0e-6, Minus, 0, false), "0"); + assert_eq!(to_string(f, 1.0e-6, Minus, 3, false), "0.000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 6, false), "0.000001"); + assert_eq!(to_string(f, 1.0e-6, Minus, 9, false), "0.000001000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 12, false), "0.000001000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 22, false), "0.0000010000000000000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 23, false), "0.00000099999999999999995"); + assert_eq!(to_string(f, 1.0e-6, Minus, 24, false), "0.000000999999999999999955"); + assert_eq!(to_string(f, 1.0e-6, Minus, 25, false), "0.0000009999999999999999547"); + assert_eq!(to_string(f, 1.0e-6, Minus, 35, false), "0.00000099999999999999995474811182589"); + assert_eq!(to_string(f, 1.0e-6, Minus, 45, false), + "0.000000999999999999999954748111825886258685614"); + assert_eq!(to_string(f, 1.0e-6, Minus, 55, false), + "0.0000009999999999999999547481118258862586856139387236908"); + assert_eq!(to_string(f, 1.0e-6, Minus, 65, false), + "0.00000099999999999999995474811182588625868561393872369080781936646"); + assert_eq!(to_string(f, 1.0e-6, Minus, 75, false), + "0.000000999999999999999954748111825886258685613938723690807819366455078125000"); + + assert_eq!(to_string(f, f32::MAX, Minus, 0, false), + "340282346638528859811704183484516925440"); + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), + "340282346638528859811704183484516925440.0"); + assert_eq!(to_string(f, f32::MAX, Minus, 2, false), + "340282346638528859811704183484516925440.00"); + + let minf32: f32 = StdFloat::ldexp(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0, false), "0"); + assert_eq!(to_string(f, minf32, Minus, 1, false), "0.0"); + assert_eq!(to_string(f, minf32, Minus, 2, false), "0.00"); + assert_eq!(to_string(f, minf32, Minus, 4, false), "0.0000"); + assert_eq!(to_string(f, minf32, Minus, 8, false), "0.00000000"); + assert_eq!(to_string(f, minf32, Minus, 16, false), "0.0000000000000000"); + assert_eq!(to_string(f, minf32, Minus, 32, false), "0.00000000000000000000000000000000"); + assert_eq!(to_string(f, minf32, Minus, 64, false), + "0.0000000000000000000000000000000000000000000014012984643248170709"); + assert_eq!(to_string(f, minf32, Minus, 128, false), + "0.0000000000000000000000000000000000000000000014012984643248170709\ + 2372958328991613128026194187651577175706828388979108268586060149"); + assert_eq!(to_string(f, minf32, Minus, 256, false), + "0.0000000000000000000000000000000000000000000014012984643248170709\ + 2372958328991613128026194187651577175706828388979108268586060148\ + 6638188362121582031250000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000"); + + assert_eq!(to_string(f, f64::MAX, Minus, 0, false), + "1797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 26204144723168738177180919299881250404026184124858368"); + assert_eq!(to_string(f, f64::MAX, Minus, 10, false), + "1797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 26204144723168738177180919299881250404026184124858368.0000000000"); + + let minf64: f64 = StdFloat::ldexp(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0, false), "0"); + assert_eq!(to_string(f, minf64, Minus, 1, false), "0.0"); + assert_eq!(to_string(f, minf64, Minus, 10, false), "0.0000000000"); + assert_eq!(to_string(f, minf64, Minus, 100, false), + "0.0000000000000000000000000000000000000000000000000000000000000000\ + 000000000000000000000000000000000000"); + assert_eq!(to_string(f, minf64, Minus, 1000, false), + "0.0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0004940656458412465441765687928682213723650598026143247644255856\ + 8250067550727020875186529983636163599237979656469544571773092665\ + 6710355939796398774796010781878126300713190311404527845817167848\ + 9821036887186360569987307230500063874091535649843873124733972731\ + 6961514003171538539807412623856559117102665855668676818703956031\ + 0624931945271591492455329305456544401127480129709999541931989409\ + 0804165633245247571478690147267801593552386115501348035264934720\ + 1937902681071074917033322268447533357208324319360923828934583680\ + 6010601150616980975307834227731832924790498252473077637592724787\ + 4656084778203734469699533647017972677717585125660551199131504891\ + 1014510378627381672509558373897335989937"); + + // very large output + assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("10.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e-1, Minus, 80000, false), + format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "")); + assert_eq!(to_string(f, 1.0e-20, Minus, 80000, false), + format!("0.0000000000000000000099999999999999994515327145420957165172950370\ + 2787392447107715776066783064379706047475337982177734375{:0>79881}", "")); +} + diff --git a/src/libcoretest/num/flt2dec/strategy/dragon.rs b/src/libcoretest/num/flt2dec/strategy/dragon.rs new file mode 100644 index 0000000000000..f2397f6b48037 --- /dev/null +++ b/src/libcoretest/num/flt2dec/strategy/dragon.rs @@ -0,0 +1,117 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::prelude::v1::*; +use std::{i16, f64}; +use super::super::*; +use core::num::flt2dec::*; +use core::num::flt2dec::bignum::Big32x36 as Big; +use core::num::flt2dec::strategy::dragon::*; + +#[test] +fn test_mul_pow10() { + let mut prevpow10 = Big::from_small(1); + for i in 1..340 { + let mut curpow10 = Big::from_small(1); + mul_pow10(&mut curpow10, i); + assert_eq!(curpow10, *prevpow10.clone().mul_small(10)); + prevpow10 = curpow10; + } +} + +#[test] +fn shortest_sanity_test() { + f64_shortest_sanity_test(format_shortest); + f32_shortest_sanity_test(format_shortest); + more_shortest_sanity_test(format_shortest); +} + +#[test] +fn exact_sanity_test() { + f64_exact_sanity_test(format_exact); + f32_exact_sanity_test(format_exact); +} + +#[bench] +fn bench_small_shortest(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; MAX_SIG_DIGITS]; + b.iter(|| format_shortest(&decoded, &mut buf)); +} + +#[bench] +fn bench_big_shortest(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; MAX_SIG_DIGITS]; + b.iter(|| format_shortest(&decoded, &mut buf)); +} + +#[bench] +fn bench_small_exact_3(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; 3]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_big_exact_3(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; 3]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_small_exact_12(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; 12]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_big_exact_12(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; 12]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_small_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; 1024]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_big_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; 1024]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[test] +fn test_to_shortest_str() { + to_shortest_str_test(format_shortest); +} + +#[test] +fn test_to_shortest_exp_str() { + to_shortest_exp_str_test(format_shortest); +} + +#[test] +fn test_to_exact_exp_str() { + to_exact_exp_str_test(format_exact); +} + +#[test] +fn test_to_exact_fixed_str() { + to_exact_fixed_str_test(format_exact); +} + diff --git a/src/libcoretest/num/flt2dec/strategy/grisu.rs b/src/libcoretest/num/flt2dec/strategy/grisu.rs new file mode 100644 index 0000000000000..ff282ea507c01 --- /dev/null +++ b/src/libcoretest/num/flt2dec/strategy/grisu.rs @@ -0,0 +1,177 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::{i16, f64}; +use super::super::*; +use core::num::flt2dec::*; +use core::num::flt2dec::strategy::grisu::*; + +#[test] +fn test_cached_power() { + assert_eq!(CACHED_POW10.first().unwrap().1, CACHED_POW10_FIRST_E); + assert_eq!(CACHED_POW10.last().unwrap().1, CACHED_POW10_LAST_E); + + for e in -1137..961 { // full range for f64 + let low = ALPHA - e - 64; + let high = GAMMA - e - 64; + let (_k, cached) = cached_power(low, high); + assert!(low <= cached.e && cached.e <= high, + "cached_power({}, {}) = {:?} is incorrect", low, high, cached); + } +} + +#[test] +fn test_max_pow10_no_more_than() { + let mut prevtenk = 1; + for k in 1..10 { + let tenk = prevtenk * 10; + assert_eq!(max_pow10_no_more_than(tenk - 1), (k - 1, prevtenk)); + assert_eq!(max_pow10_no_more_than(tenk), (k, tenk)); + prevtenk = tenk; + } +} + + +#[test] +fn shortest_sanity_test() { + f64_shortest_sanity_test(format_shortest); + f32_shortest_sanity_test(format_shortest); + more_shortest_sanity_test(format_shortest); +} + +#[test] +fn shortest_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); + f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); +} + +#[test] #[ignore] // it is too expensive +fn shortest_f32_exhaustive_equivalence_test() { + // it is hard to directly test the optimality of the output, but we can at least test if + // two different algorithms agree to each other. + // + // this reports the progress and the number of f32 values returned `None`. + // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print: + // `done, ignored=17643160 passed=2121451879 failed=0`. + + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); +} + +#[test] #[ignore] // is is too expensive +fn shortest_f64_hard_random_equivalence_test() { + // this again probably has to use appropriate rustc flags. + + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f64_random_equivalence_test(format_shortest_opt, fallback, + MAX_SIG_DIGITS, 100_000_000); +} + +#[test] +fn exact_sanity_test() { + f64_exact_sanity_test(format_exact); + f32_exact_sanity_test(format_exact); +} + +#[test] +fn exact_f32_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + for k in 1..21 { + f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), k, 1_000); + } +} + +#[test] +fn exact_f64_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + for k in 1..21 { + f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), k, 1_000); + } +} + +#[bench] +fn bench_small_shortest(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; MAX_SIG_DIGITS]; + b.iter(|| format_shortest(&decoded, &mut buf)); +} + +#[bench] +fn bench_big_shortest(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; MAX_SIG_DIGITS]; + b.iter(|| format_shortest(&decoded, &mut buf)); +} + +#[bench] +fn bench_small_exact_3(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; 3]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_big_exact_3(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; 3]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_small_exact_12(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; 12]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_big_exact_12(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; 12]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_small_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [0; 1024]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[bench] +fn bench_big_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [0; 1024]; + b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); +} + +#[test] +fn test_to_shortest_str() { + to_shortest_str_test(format_shortest); +} + +#[test] +fn test_to_shortest_exp_str() { + to_shortest_exp_str_test(format_shortest); +} + +#[test] +fn test_to_exact_exp_str() { + to_exact_exp_str_test(format_exact); +} + +#[test] +fn test_to_exact_fixed_str() { + to_exact_fixed_str_test(format_exact); +} + diff --git a/src/libcoretest/num/mod.rs b/src/libcoretest/num/mod.rs index 0ea9f8afb4e53..998f4b21ece7f 100644 --- a/src/libcoretest/num/mod.rs +++ b/src/libcoretest/num/mod.rs @@ -29,6 +29,8 @@ mod u16; mod u32; mod u64; +mod flt2dec; + /// Helper function for testing numeric operations pub fn test_num(ten: T, two: T) where T: PartialEq From 5aa9f38285b47e2eadaaac0e343da90b922994f1 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 20 Apr 2015 01:36:55 +0900 Subject: [PATCH 2/9] core: made the core formatter to use a new flt2dec. As a side effect `core::fmt::float` is gone now. This also slightly changes the float output, so this is: [breaking-change] --- src/libcore/fmt/float.rs | 289 --------------------------------------- src/libcore/fmt/mod.rs | 168 +++++++++++++++++------ 2 files changed, 125 insertions(+), 332 deletions(-) delete mode 100644 src/libcore/fmt/float.rs diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs deleted file mode 100644 index 4b75bd5f67e3d..0000000000000 --- a/src/libcore/fmt/float.rs +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub use self::ExponentFormat::*; -pub use self::SignificantDigits::*; - -use prelude::*; - -use char; -use fmt; -use num::Float; -use num::FpCategory as Fp; -use ops::{Div, Rem, Mul}; -use slice; -use str; - -/// A flag that specifies whether to use exponential (scientific) notation. -pub enum ExponentFormat { - /// Do not use exponential notation. - ExpNone, - /// Use exponential notation with the exponent having a base of 10 and the - /// exponent sign being `e` or `E`. For example, 1000 would be printed - /// 1e3. - ExpDec -} - -/// The number of digits used for emitting the fractional part of a number, if -/// any. -pub enum SignificantDigits { - /// At most the given number of digits will be printed, truncating any - /// trailing zeroes. - DigMax(usize), - - /// Precisely the given number of digits will be printed. - DigExact(usize) -} - -#[doc(hidden)] -pub trait MyFloat: Float + PartialEq + PartialOrd + Div + - Mul + Rem + Copy { - fn from_u32(u: u32) -> Self; - fn to_i32(&self) -> i32; -} - -macro_rules! doit { - ($($t:ident)*) => ($(impl MyFloat for $t { - fn from_u32(u: u32) -> $t { u as $t } - fn to_i32(&self) -> i32 { *self as i32 } - })*) -} -doit! { f32 f64 } - -/// Converts a float number to its string representation. -/// This is meant to be a common base implementation for various formatting styles. -/// The number is assumed to be non-negative, callers use `Formatter::pad_integral` -/// to add the right sign, if any. -/// -/// # Arguments -/// -/// - `num` - The number to convert (non-negative). Accepts any number that -/// implements the numeric traits. -/// - `digits` - The amount of digits to use for emitting the fractional -/// part, if any. See `SignificantDigits`. -/// - `exp_format` - Whether or not to use the exponential (scientific) notation. -/// See `ExponentFormat`. -/// - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if -/// exponential notation is desired. -/// - `f` - A closure to invoke with the string representing the -/// float. -/// -/// # Panics -/// -/// - Panics if `num` is negative. -pub fn float_to_str_bytes_common( - num: T, - digits: SignificantDigits, - exp_format: ExponentFormat, - exp_upper: bool, - f: F -) -> U where - F: FnOnce(&str) -> U, -{ - let _0: T = T::zero(); - let _1: T = T::one(); - let radix: u32 = 10; - let radix_f = T::from_u32(radix); - - assert!(num.is_nan() || num >= _0, "float_to_str_bytes_common: number is negative"); - - match num.classify() { - Fp::Nan => return f("NaN"), - Fp::Infinite if num > _0 => { - return f("inf"); - } - Fp::Infinite if num < _0 => { - return f("-inf"); - } - _ => {} - } - - // For an f64 the (decimal) exponent is roughly in the range of [-307, 308], so - // we may have up to that many digits. We err on the side of caution and - // add 50% extra wiggle room. - let mut buf = [0; 462]; - let mut end = 0; - - let (num, exp) = match exp_format { - ExpDec if num != _0 => { - let exp = num.log10().floor(); - (num / radix_f.powf(exp), exp.to_i32()) - } - _ => (num, 0) - }; - - // First emit the non-fractional part, looping at least once to make - // sure at least a `0` gets emitted. - let mut deccum = num.trunc(); - loop { - let current_digit = deccum % radix_f; - - // Decrease the deccumulator one digit at a time - deccum = deccum / radix_f; - deccum = deccum.trunc(); - - let c = char::from_digit(current_digit.to_i32() as u32, radix); - buf[end] = c.unwrap() as u8; - end += 1; - - // No more digits to calculate for the non-fractional part -> break - if deccum == _0 { break; } - } - - // If limited digits, calculate one digit more for rounding. - let (limit_digits, digit_count, exact) = match digits { - DigMax(count) => (true, count + 1, false), - DigExact(count) => (true, count + 1, true) - }; - - buf[..end].reverse(); - - // Remember start of the fractional digits. - // Points one beyond end of buf if none get generated, - // or at the '.' otherwise. - let start_fractional_digits = end; - - // Now emit the fractional part, if any - deccum = num.fract(); - if deccum != _0 || (limit_digits && exact && digit_count > 0) { - buf[end] = b'.'; - end += 1; - let mut dig = 0; - - // calculate new digits while - // - there is no limit and there are digits left - // - or there is a limit, it's not reached yet and - // - it's exact - // - or it's a maximum, and there are still digits left - while (!limit_digits && deccum != _0) - || (limit_digits && dig < digit_count && ( - exact - || (!exact && deccum != _0) - ) - ) { - // Shift first fractional digit into the integer part - deccum = deccum * radix_f; - - let current_digit = deccum.trunc(); - - let c = char::from_digit(current_digit.to_i32() as u32, radix); - buf[end] = c.unwrap() as u8; - end += 1; - - // Decrease the deccumulator one fractional digit at a time - deccum = deccum.fract(); - dig += 1; - } - - // If digits are limited, and that limit has been reached, - // cut off the one extra digit, and depending on its value - // round the remaining ones. - if limit_digits && dig == digit_count { - let ascii2value = |chr: u8| { - (chr as char).to_digit(radix).unwrap() - }; - let value2ascii = |val: u32| { - char::from_digit(val, radix).unwrap() as u8 - }; - - let extra_digit = ascii2value(buf[end - 1]); - end -= 1; - if extra_digit >= radix / 2 { // -> need to round - let mut i: isize = end as isize - 1; - loop { - // If reached left end of number, have to - // insert additional digit: - if i < 0 - || buf[i as usize] == b'-' - || buf[i as usize] == b'+' { - for j in ((i + 1) as usize..end).rev() { - buf[j + 1] = buf[j]; - } - buf[(i + 1) as usize] = value2ascii(1); - end += 1; - break; - } - - // Skip the '.' - if buf[i as usize] == b'.' { i -= 1; continue; } - - // Either increment the digit, - // or set to 0 if max and carry the 1. - let current_digit = ascii2value(buf[i as usize]); - if current_digit < (radix - 1) { - buf[i as usize] = value2ascii(current_digit+1); - break; - } else { - buf[i as usize] = value2ascii(0); - i -= 1; - } - } - } - } - } - - // if number of digits is not exact, remove all trailing '0's up to - // and including the '.' - if !exact { - let buf_max_i = end - 1; - - // index to truncate from - let mut i = buf_max_i; - - // discover trailing zeros of fractional part - while i > start_fractional_digits && buf[i] == b'0' { - i -= 1; - } - - // Only attempt to truncate digits if buf has fractional digits - if i >= start_fractional_digits { - // If buf ends with '.', cut that too. - if buf[i] == b'.' { i -= 1 } - - // only resize buf if we actually remove digits - if i < buf_max_i { - end = i + 1; - } - } - } // If exact and trailing '.', just cut that - else { - let max_i = end - 1; - if buf[max_i] == b'.' { - end = max_i; - } - } - - match exp_format { - ExpNone => {}, - ExpDec => { - buf[end] = if exp_upper { b'E' } else { b'e' }; - end += 1; - - struct Filler<'a> { - buf: &'a mut [u8], - end: &'a mut usize, - } - - impl<'a> fmt::Write for Filler<'a> { - fn write_str(&mut self, s: &str) -> fmt::Result { - slice::bytes::copy_memory(s.as_bytes(), - &mut self.buf[(*self.end)..]); - *self.end += s.len(); - Ok(()) - } - } - - let mut filler = Filler { buf: &mut buf, end: &mut end }; - let _ = fmt::write(&mut filler, format_args!("{:-}", exp)); - } - } - - f(unsafe { str::from_utf8_unchecked(&buf[..end]) }) -} diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index f8a1ef96bcc33..a87f6619fe877 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -17,9 +17,9 @@ use prelude::*; use cell::{Cell, RefCell, Ref, RefMut, BorrowState}; use marker::PhantomData; use mem; +use num::flt2dec; use ops::Deref; use result; -use num::Float; use slice; use str; use self::rt::v1::Alignment; @@ -31,7 +31,6 @@ pub use self::num::RadixFmt; pub use self::builders::{DebugStruct, DebugTuple, DebugSet, DebugList, DebugMap}; mod num; -mod float; mod builders; #[unstable(feature = "core", reason = "internal to format_args!")] @@ -604,6 +603,83 @@ impl<'a> Formatter<'a> { Ok(()) } + /// Takes the formatted parts and applies the padding. + /// Assumes that the caller already has rendered the parts with required precision, + /// so that `self.precision` can be ignored. + fn pad_formatted_parts(&mut self, formatted: &flt2dec::Formatted) -> Result { + if let Some(mut width) = self.width { + // for the sign-aware zero padding, we render the sign first and + // behave as if we had no sign from the beginning. + let mut formatted = formatted.clone(); + let mut align = self.align; + let old_fill = self.fill; + if self.flags & (1 << (FlagV1::SignAwareZeroPad as u32)) != 0 { + // a sign always goes first + let sign = unsafe { str::from_utf8_unchecked(formatted.sign) }; + try!(self.buf.write_str(sign)); + + // remove the sign from the formatted parts + formatted.sign = b""; + width = if width < sign.len() { 0 } else { width - sign.len() }; + align = Alignment::Right; + self.fill = '0'; + } + + // remaining parts go through the ordinary padding process. + let len = formatted.len(); + let ret = if width <= len { // no padding + self.write_formatted_parts(&formatted) + } else { + self.with_padding(width - len, align, |f| { + f.write_formatted_parts(&formatted) + }) + }; + self.fill = old_fill; + ret + } else { + // this is the common case and we take a shortcut + self.write_formatted_parts(formatted) + } + } + + fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted) -> Result { + fn write_bytes(buf: &mut Write, s: &[u8]) -> Result { + buf.write_str(unsafe { str::from_utf8_unchecked(s) }) + } + + if !formatted.sign.is_empty() { + try!(write_bytes(self.buf, formatted.sign)); + } + for part in formatted.parts { + match *part { + flt2dec::Part::Zero(mut nzeroes) => { + const ZEROES: &'static str = // 64 zeroes + "0000000000000000000000000000000000000000000000000000000000000000"; + while nzeroes > ZEROES.len() { + try!(self.buf.write_str(ZEROES)); + nzeroes -= ZEROES.len(); + } + if nzeroes > 0 { + try!(self.buf.write_str(&ZEROES[..nzeroes])); + } + } + flt2dec::Part::Num(mut v) => { + let mut s = [0; 5]; + let len = part.len(); + for c in s[..len].iter_mut().rev() { + *c = b'0' + (v % 10) as u8; + v /= 10; + } + try!(write_bytes(self.buf, &s[..len])); + } + flt2dec::Part::Copy(buf) => { + try!(write_bytes(self.buf, buf)); + } + } + } + Ok(()) + } + /// Writes some data to the underlying buffer contained within this /// formatter. #[stable(feature = "rust1", since = "1.0.0")] @@ -918,18 +994,50 @@ impl<'a, T> Pointer for &'a mut T { } // Common code of floating point Debug and Display. -fn float_to_str_common(num: &T, precision: Option, - post: F) -> Result - where F : FnOnce(&str) -> Result { - let digits = match precision { - Some(i) => float::DigExact(i), - None => float::DigMax(6), +fn float_to_decimal_common(fmt: &mut Formatter, num: &T, negative_zero: bool) -> Result + where T: flt2dec::DecodableFloat +{ + let force_sign = fmt.flags & (1 << (FlagV1::SignPlus as u32)) != 0; + let sign = match (force_sign, negative_zero) { + (false, false) => flt2dec::Sign::Minus, + (false, true) => flt2dec::Sign::MinusRaw, + (true, false) => flt2dec::Sign::MinusPlus, + (true, true) => flt2dec::Sign::MinusPlusRaw, + }; + + let mut buf = [0; 1024]; // enough for f32 and f64 + let mut parts = [flt2dec::Part::Zero(0); 16]; + let formatted = if let Some(precision) = fmt.precision { + flt2dec::to_exact_fixed_str(flt2dec::strategy::grisu::format_exact, *num, sign, + precision, false, &mut buf, &mut parts) + } else { + flt2dec::to_shortest_str(flt2dec::strategy::grisu::format_shortest, *num, sign, + 0, false, &mut buf, &mut parts) + }; + fmt.pad_formatted_parts(&formatted) +} + +// Common code of floating point LowerExp and UpperExp. +fn float_to_exponential_common(fmt: &mut Formatter, num: &T, upper: bool) -> Result + where T: flt2dec::DecodableFloat +{ + let force_sign = fmt.flags & (1 << (FlagV1::SignPlus as u32)) != 0; + let sign = match force_sign { + false => flt2dec::Sign::Minus, + true => flt2dec::Sign::MinusPlus, }; - float::float_to_str_bytes_common(num.abs(), - digits, - float::ExpNone, - false, - post) + + let mut buf = [0; 1024]; // enough for f32 and f64 + let mut parts = [flt2dec::Part::Zero(0); 16]; + let formatted = if let Some(precision) = fmt.precision { + // 1 integral digit + `precision` fractional digits = `precision + 1` total digits + flt2dec::to_exact_exp_str(flt2dec::strategy::grisu::format_exact, *num, sign, + precision + 1, upper, &mut buf, &mut parts) + } else { + flt2dec::to_shortest_exp_str(flt2dec::strategy::grisu::format_shortest, *num, sign, + (0, 0), upper, &mut buf, &mut parts) + }; + fmt.pad_formatted_parts(&formatted) } macro_rules! floating { ($ty:ident) => { @@ -937,54 +1045,28 @@ macro_rules! floating { ($ty:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for $ty { fn fmt(&self, fmt: &mut Formatter) -> Result { - float_to_str_common(self, fmt.precision, |absolute| { - // is_positive() counts -0.0 as negative - fmt.pad_integral(self.is_nan() || self.is_positive(), "", absolute) - }) + float_to_decimal_common(fmt, self, true) } } #[stable(feature = "rust1", since = "1.0.0")] impl Display for $ty { fn fmt(&self, fmt: &mut Formatter) -> Result { - float_to_str_common(self, fmt.precision, |absolute| { - // simple comparison counts -0.0 as positive - fmt.pad_integral(self.is_nan() || *self >= 0.0, "", absolute) - }) + float_to_decimal_common(fmt, self, false) } } #[stable(feature = "rust1", since = "1.0.0")] impl LowerExp for $ty { fn fmt(&self, fmt: &mut Formatter) -> Result { - let digits = match fmt.precision { - Some(i) => float::DigExact(i), - None => float::DigMax(6), - }; - float::float_to_str_bytes_common(self.abs(), - digits, - float::ExpDec, - false, - |bytes| { - fmt.pad_integral(self.is_nan() || *self >= 0.0, "", bytes) - }) + float_to_exponential_common(fmt, self, false) } } #[stable(feature = "rust1", since = "1.0.0")] impl UpperExp for $ty { fn fmt(&self, fmt: &mut Formatter) -> Result { - let digits = match fmt.precision { - Some(i) => float::DigExact(i), - None => float::DigMax(6), - }; - float::float_to_str_bytes_common(self.abs(), - digits, - float::ExpDec, - true, - |bytes| { - fmt.pad_integral(self.is_nan() || *self >= 0.0, "", bytes) - }) + float_to_exponential_common(fmt, self, true) } } } } From f9bfda0a6f3853e48255b5559ccba76fbcbfcc5a Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 21 Apr 2015 19:55:43 +0900 Subject: [PATCH 3/9] core: tweaked flt2dec to match the casing of the older formatting code. --- src/libcore/num/flt2dec/mod.rs | 36 ++++++++--------- src/libcoretest/num/flt2dec/mod.rs | 64 +++++++++++++++--------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/libcore/num/flt2dec/mod.rs b/src/libcore/num/flt2dec/mod.rs index 8b4b3a45e9b33..74b190085b3d3 100644 --- a/src/libcore/num/flt2dec/mod.rs +++ b/src/libcore/num/flt2dec/mod.rs @@ -396,10 +396,10 @@ fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static /// Formats given floating point number into the decimal form with at least /// given number of fractional digits. The result is stored to the supplied parts -/// array while utilizing given byte buffer as a scratch. `upper` is only used to -/// determine the case of non-finite values, i.e. `inf` and `nan`. The first part -/// to be rendered is always a `Part::Sign` (which can be an empty string -/// if no sign is rendered). +/// array while utilizing given byte buffer as a scratch. `upper` is currently +/// unused but left for the future decision to change the case of non-finite values, +/// i.e. `inf` and `nan`. The first part to be rendered is always a `Part::Sign` +/// (which can be an empty string if no sign is rendered). /// /// `format_shortest` should be the underlying digit-generation function. /// You probably would want `strategy::grisu::format_shortest` for this. @@ -413,7 +413,7 @@ fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static /// There should be at least 5 parts available, due to the worst case like /// `[+][0.][0000][45][0000]` with `frac_digits = 10`. pub fn to_shortest_str<'a, T, F>(mut format_shortest: F, v: T, - sign: Sign, frac_digits: usize, upper: bool, + sign: Sign, frac_digits: usize, _upper: bool, buf: &'a mut [u8], parts: &'a mut [Part<'a>]) -> Formatted<'a> where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { assert!(parts.len() >= 4); @@ -423,11 +423,11 @@ pub fn to_shortest_str<'a, T, F>(mut format_shortest: F, v: T, let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + parts[0] = Part::Copy(b"NaN"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Infinite => { - parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + parts[0] = Part::Copy(b"inf"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Zero => { @@ -479,11 +479,11 @@ pub fn to_shortest_exp_str<'a, T, F>(mut format_shortest: F, v: T, let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + parts[0] = Part::Copy(b"NaN"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Infinite => { - parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + parts[0] = Part::Copy(b"inf"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Zero => { @@ -557,11 +557,11 @@ pub fn to_exact_exp_str<'a, T, F>(mut format_exact: F, v: T, let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + parts[0] = Part::Copy(b"NaN"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Infinite => { - parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + parts[0] = Part::Copy(b"inf"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Zero => { @@ -589,10 +589,10 @@ pub fn to_exact_exp_str<'a, T, F>(mut format_exact: F, v: T, /// Formats given floating point number into the decimal form with exactly /// given number of fractional digits. The result is stored to the supplied parts -/// array while utilizing given byte buffer as a scratch. `upper` is only used to -/// determine the case of non-finite values, i.e. `inf` and `nan`. The first part -/// to be rendered is always a `Part::Sign` (which can be an empty string -/// if no sign is rendered). +/// array while utilizing given byte buffer as a scratch. `upper` is currently +/// unused but left for the future decision to change the case of non-finite values, +/// i.e. `inf` and `nan`. The first part to be rendered is always a `Part::Sign` +/// (which can be an empty string if no sign is rendered). /// /// `format_exact` should be the underlying digit-generation function. /// You probably would want `strategy::grisu::format_exact` for this. @@ -603,7 +603,7 @@ pub fn to_exact_exp_str<'a, T, F>(mut format_exact: F, v: T, /// There should be at least 5 parts available, due to the worst case like /// `[+][0.][0000][45][0000]` with `frac_digits = 10`. pub fn to_exact_fixed_str<'a, T, F>(mut format_exact: F, v: T, - sign: Sign, frac_digits: usize, upper: bool, + sign: Sign, frac_digits: usize, _upper: bool, buf: &'a mut [u8], parts: &'a mut [Part<'a>]) -> Formatted<'a> where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { assert!(parts.len() >= 4); @@ -612,11 +612,11 @@ pub fn to_exact_fixed_str<'a, T, F>(mut format_exact: F, v: T, let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(if upper { b"NAN" } else { b"nan" }); + parts[0] = Part::Copy(b"NaN"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Infinite => { - parts[0] = Part::Copy(if upper { b"INF" } else { b"inf" }); + parts[0] = Part::Copy(b"inf"); Formatted { sign: sign, parts: &parts[..1] } } FullDecoded::Zero => { diff --git a/src/libcoretest/num/flt2dec/mod.rs b/src/libcoretest/num/flt2dec/mod.rs index 07880b22ce5a8..3ffec7c007e5f 100644 --- a/src/libcoretest/num/flt2dec/mod.rs +++ b/src/libcoretest/num/flt2dec/mod.rs @@ -544,17 +544,17 @@ pub fn to_shortest_str_test(mut f_: F) assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, true), "-0.00000000"); assert_eq!(to_string(f, 1.0/0.0, Minus, 0, false), "inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 0, true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 0, true), "inf"); assert_eq!(to_string(f, 1.0/0.0, MinusPlus, 0, false), "+inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 0, true), "+INF"); - assert_eq!(to_string(f, 0.0/0.0, Minus, 0, false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 1, true), "NAN"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 64, true), "NAN"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 0, true), "+inf"); + assert_eq!(to_string(f, 0.0/0.0, Minus, 0, false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 1, true), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 64, true), "NaN"); assert_eq!(to_string(f, -1.0/0.0, Minus, 0, false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 1, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 1, true), "-inf"); assert_eq!(to_string(f, -1.0/0.0, MinusPlus, 8, false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-inf"); assert_eq!(to_string(f, 3.14, Minus, 0, false), "3.14"); assert_eq!(to_string(f, 3.14, MinusRaw, 0, false), "3.14"); @@ -638,17 +638,17 @@ pub fn to_shortest_exp_str_test(mut f_: F) assert_eq!(to_string(f, -0.0, MinusPlusRaw, ( 5, 9), false), "-0e0"); assert_eq!(to_string(f, 1.0/0.0, Minus, (-4, 16), false), "inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusRaw, (-4, 16), true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, (-4, 16), true), "inf"); assert_eq!(to_string(f, 1.0/0.0, MinusPlus, (-4, 16), false), "+inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, (-4, 16), true), "+INF"); - assert_eq!(to_string(f, 0.0/0.0, Minus, ( 0, 0), false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusRaw, ( 0, 0), true), "NAN"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlus, (-9, -5), false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, ( 5, 9), true), "NAN"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, (-4, 16), true), "+inf"); + assert_eq!(to_string(f, 0.0/0.0, Minus, ( 0, 0), false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, ( 0, 0), true), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, (-9, -5), false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, ( 5, 9), true), "NaN"); assert_eq!(to_string(f, -1.0/0.0, Minus, ( 0, 0), false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusRaw, ( 0, 0), true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, ( 0, 0), true), "-inf"); assert_eq!(to_string(f, -1.0/0.0, MinusPlus, (-9, -5), false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, ( 5, 9), true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, ( 5, 9), true), "-inf"); assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14"); assert_eq!(to_string(f, 3.14, MinusRaw, (-4, 16), false), "3.14"); @@ -752,17 +752,17 @@ pub fn to_exact_exp_str_test(mut f_: F) assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, false), "-0.0000000e0"); assert_eq!(to_string(f, 1.0/0.0, Minus, 1, false), "inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 1, true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 1, true), "inf"); assert_eq!(to_string(f, 1.0/0.0, MinusPlus, 1, false), "+inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 1, true), "+INF"); - assert_eq!(to_string(f, 0.0/0.0, Minus, 8, false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 8, true), "NAN"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 8, true), "NAN"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 1, true), "+inf"); + assert_eq!(to_string(f, 0.0/0.0, Minus, 8, false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 8, true), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 8, true), "NaN"); assert_eq!(to_string(f, -1.0/0.0, Minus, 64, false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 64, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 64, true), "-inf"); assert_eq!(to_string(f, -1.0/0.0, MinusPlus, 64, false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-inf"); assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0"); assert_eq!(to_string(f, 3.14, MinusRaw, 1, false), "3e0"); @@ -973,17 +973,17 @@ pub fn to_exact_fixed_str_test(mut f_: F) assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, true), "-0.00000000"); assert_eq!(to_string(f, 1.0/0.0, Minus, 0, false), "inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 1, true), "INF"); + assert_eq!(to_string(f, 1.0/0.0, MinusRaw, 1, true), "inf"); assert_eq!(to_string(f, 1.0/0.0, MinusPlus, 8, false), "+inf"); - assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 64, true), "+INF"); - assert_eq!(to_string(f, 0.0/0.0, Minus, 0, false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 1, true), "NAN"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "nan"); - assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 64, true), "NAN"); + assert_eq!(to_string(f, 1.0/0.0, MinusPlusRaw, 64, true), "+inf"); + assert_eq!(to_string(f, 0.0/0.0, Minus, 0, false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusRaw, 1, true), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlus, 8, false), "NaN"); + assert_eq!(to_string(f, 0.0/0.0, MinusPlusRaw, 64, true), "NaN"); assert_eq!(to_string(f, -1.0/0.0, Minus, 0, false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 1, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusRaw, 1, true), "-inf"); assert_eq!(to_string(f, -1.0/0.0, MinusPlus, 8, false), "-inf"); - assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-INF"); + assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64, true), "-inf"); assert_eq!(to_string(f, 3.14, Minus, 0, false), "3"); assert_eq!(to_string(f, 3.14, MinusRaw, 0, false), "3"); From 85424c4baedd689796560fcdd7b1496cd90fa2db Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 20 Apr 2015 02:47:38 +0900 Subject: [PATCH 4/9] test: update tests for flt2dec fallouts. --- src/libserialize/json.rs | 4 ++-- src/test/run-pass/ifmt.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index baa6493533d29..adc4f69334f17 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -3011,9 +3011,9 @@ mod tests { let v: i64 = super::decode("9223372036854775807").unwrap(); assert_eq!(v, i64::MAX); - let res: DecodeResult = super::decode("765.25252"); + let res: DecodeResult = super::decode("765.25"); assert_eq!(res, Err(ExpectedError("Integer".to_string(), - "765.25252".to_string()))); + "765.25".to_string()))); } #[test] diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 240b6286c8cfc..01a38b374cc40 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -138,10 +138,10 @@ pub fn main() { t!(format!("{:e}", 1.2345e6f32), "1.2345e6"); t!(format!("{:e}", 1.2345e6f64), "1.2345e6"); t!(format!("{:E}", 1.2345e6f64), "1.2345E6"); - t!(format!("{:.3e}", 1.2345e6f64), "1.234e6"); - t!(format!("{:10.3e}", 1.2345e6f64), " 1.234e6"); - t!(format!("{:+10.3e}", 1.2345e6f64), " +1.234e6"); - t!(format!("{:+10.3e}", -1.2345e6f64), " -1.234e6"); + t!(format!("{:.3e}", 1.2345e6f64), "1.235e6"); + t!(format!("{:10.3e}", 1.2345e6f64), " 1.235e6"); + t!(format!("{:+10.3e}", 1.2345e6f64), " +1.235e6"); + t!(format!("{:+10.3e}", -1.2345e6f64), " -1.235e6"); // Float edge cases t!(format!("{}", -0.0), "0"); From 8a195f075417ad78084ef2e1c5e294ac35d6cafa Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 21 Apr 2015 01:57:39 +0900 Subject: [PATCH 5/9] core: fixed typos and revised comments in flt2dec. --- src/libcore/num/flt2dec/bignum.rs | 14 ++++++++------ src/libcore/num/flt2dec/estimator.rs | 2 ++ src/libcore/num/flt2dec/mod.rs | 15 +++++++-------- src/libcoretest/num/flt2dec/strategy/grisu.rs | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/libcore/num/flt2dec/bignum.rs b/src/libcore/num/flt2dec/bignum.rs index 449d080dc5cb6..d6a5e44a1fb00 100644 --- a/src/libcore/num/flt2dec/bignum.rs +++ b/src/libcore/num/flt2dec/bignum.rs @@ -88,7 +88,7 @@ impl_full_ops! { u8: add(intrinsics::u8_add_with_overflow), mul/div(u16); u16: add(intrinsics::u16_add_with_overflow), mul/div(u32); u32: add(intrinsics::u32_add_with_overflow), mul/div(u64); -// u64: add(intrinsics::u64_add_with_overflow), mul/div(u128); // damn! +// u64: add(intrinsics::u64_add_with_overflow), mul/div(u128); // see RFC #521 for enabling this. } macro_rules! define_bignum { @@ -103,11 +103,12 @@ macro_rules! define_bignum { /// All operations available to bignums panic in the case of over/underflows. /// The caller is responsible to use large enough bignum types. pub struct $name { - /// One plus the offset to the maximum "digit" in the use. + /// One plus the offset to the maximum "digit" in use. /// This does not decrease, so be aware of the computation order. /// `base[size..]` should be zero. size: usize, - /// Digits. `[a, b, c, ...]` represents `a + b*n + c*n^2 + ...`. + /// Digits. `[a, b, c, ...]` represents `a + b*2^W + c*2^(2W) + ...` + /// where `W` is the number of bits in the digit type. base: [$ty; $n] } @@ -215,7 +216,7 @@ macro_rules! define_bignum { self.base[i] = 0; } - // shift by `nbits` bits + // shift by `bits` bits let mut sz = self.size + digits; if bits > 0 { let last = sz; @@ -236,8 +237,9 @@ macro_rules! define_bignum { self } - /// Multiplies itself by a number described by `other[0] + other[1] * n + - /// other[2] * n^2 + ...` and returns its own mutable reference. + /// Multiplies itself by a number described by `other[0] + other[1] * 2^W + + /// other[2] * 2^(2W) + ...` (where `W` is the number of bits in the digit type) + /// and returns its own mutable reference. pub fn mul_digits<'a>(&'a mut self, other: &[$ty]) -> &'a mut $name { // the internal routine. works best when aa.len() <= bb.len(). fn mul_inner(ret: &mut [$ty; $n], aa: &[$ty], bb: &[$ty]) -> usize { diff --git a/src/libcore/num/flt2dec/estimator.rs b/src/libcore/num/flt2dec/estimator.rs index d3e06e406dbea..d42e05a91f140 100644 --- a/src/libcore/num/flt2dec/estimator.rs +++ b/src/libcore/num/flt2dec/estimator.rs @@ -18,6 +18,8 @@ pub fn estimate_scaling_factor(mant: u64, exp: i16) -> i16 { // 2^(nbits-1) < mant <= 2^nbits if mant > 0 let nbits = 64 - (mant - 1).leading_zeros() as i64; + // 1292913986 = floor(2^32 * log_10 2) + // therefore this always underestimates (or is exact), but not much. (((nbits + exp as i64) * 1292913986) >> 32) as i16 } diff --git a/src/libcore/num/flt2dec/mod.rs b/src/libcore/num/flt2dec/mod.rs index 74b190085b3d3..f51dcf54a1959 100644 --- a/src/libcore/num/flt2dec/mod.rs +++ b/src/libcore/num/flt2dec/mod.rs @@ -93,7 +93,7 @@ Both implementations expose two public functions: They try to fill the `u8` buffer with digits and returns the number of digits written and the exponent `k`. They are total for all finite `f32` and `f64` -inputs (Grisu internally falls back to Dragon if possible). +inputs (Grisu internally falls back to Dragon if necessary). The rendered digits are formatted into the actual string form with four functions: @@ -114,8 +114,8 @@ four functions: They all return a slice of preallocated `Part` array, which corresponds to the individual part of strings: a fixed string, a part of rendered digits, a number of zeroes or a small (`u16`) number. The caller is expected to -provide an enough buffer and `Part` array, and to assemble the final -string from parts itself. +provide a large enough buffer and `Part` array, and to assemble the final +string from resulting `Part`s itself. All algorithms and formatting functions are accompanied by extensive tests in `coretest::num::flt2dec` module. It also shows how to use individual @@ -274,7 +274,7 @@ fn digits_to_dec_str<'a>(buf: &'a [u8], exp: i16, frac_digits: usize, // if there is the restriction on the last digit position, `buf` is assumed to be // left-padded with the virtual zeroes. the number of virtual zeroes, `nzeroes`, - // equals to `max(0, exp + frag_digits - buf.len())`, so that the position of + // equals to `max(0, exp + frac_digits - buf.len())`, so that the position of // the last digit `exp - buf.len() - nzeroes` is no more than `-frac_digits`: // // |<-virtual->| @@ -373,7 +373,7 @@ pub enum Sign { /// Prints `-` only for the negative non-zero values. Minus, // -inf -1 0 0 1 inf nan /// Prints `-` only for any negative values (including the negative zero). - MinusRaw, // -inf -1 0 0 1 inf nan + MinusRaw, // -inf -1 -0 0 1 inf nan /// Prints `-` for the negative non-zero values, or `+` otherwise. MinusPlus, // -inf -1 +0 +0 +1 +inf nan /// Prints `-` for any negative values (including the negative zero), or `+` otherwise. @@ -639,9 +639,8 @@ pub fn to_exact_fixed_str<'a, T, F>(mut format_exact: F, v: T, let limit = if frac_digits < 0x8000 { -(frac_digits as i16) } else { i16::MIN }; let (len, exp) = format_exact(decoded, &mut buf[..maxlen], limit); if exp <= limit { - // `format_exact` always returns at least one digit even though the restriction - // hasn't been met, so we catch this condition and treats as like zeroes. - // this does not include the case that the restriction has been met + // the restriction couldn't been met, so this should render like zero no matter + // `exp` was. this does not include the case that the restriction has been met // only after the final rounding-up; it's a regular case with `exp = limit + 1`. debug_assert_eq!(len, 0); if frac_digits > 0 { // [0.][0000] diff --git a/src/libcoretest/num/flt2dec/strategy/grisu.rs b/src/libcoretest/num/flt2dec/strategy/grisu.rs index ff282ea507c01..3d798c8726cdd 100644 --- a/src/libcoretest/num/flt2dec/strategy/grisu.rs +++ b/src/libcoretest/num/flt2dec/strategy/grisu.rs @@ -66,7 +66,7 @@ fn shortest_f32_exhaustive_equivalence_test() { f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); } -#[test] #[ignore] // is is too expensive +#[test] #[ignore] // it is too expensive fn shortest_f64_hard_random_equivalence_test() { // this again probably has to use appropriate rustc flags. From 97ea7c14bae496d6444752be570dd41cf1a507bd Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 21 Apr 2015 20:32:25 +0900 Subject: [PATCH 6/9] core: fixed a slight bug. The bug involves the incorrect logic for `core::num::flt2dec::decoder`. This makes some numbers in the form of 2^n missing one final digits, which breaks the bijectivity criterion. The regression tests have been added, and f32 exhaustive test is rerun to get the updated result. --- src/libcore/num/flt2dec/decoder.rs | 12 ++++++------ src/libcoretest/num/flt2dec/mod.rs | 18 ++++++++++++++++-- src/libcoretest/num/flt2dec/strategy/grisu.rs | 2 +- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/libcore/num/flt2dec/decoder.rs b/src/libcore/num/flt2dec/decoder.rs index 4b7d00f80e1f1..d4473e6bc0c4a 100644 --- a/src/libcore/num/flt2dec/decoder.rs +++ b/src/libcore/num/flt2dec/decoder.rs @@ -75,7 +75,7 @@ pub fn decode(v: T) -> (/*negative?*/ bool, FullDecoded) { FpCategory::Infinite => FullDecoded::Infinite, FpCategory::Zero => FullDecoded::Zero, FpCategory::Subnormal => { - // (mant - 2, exp) -- (mant, exp) -- (mant + 2, exp) + // neighbors: (mant - 2, exp) -- (mant, exp) -- (mant + 2, exp) // Float::integer_decode always preserves the exponent, // so the mantissa is scaled for subnormals. FullDecoded::Finite(Decoded { mant: mant, minus: 1, plus: 1, @@ -83,13 +83,13 @@ pub fn decode(v: T) -> (/*negative?*/ bool, FullDecoded) { } FpCategory::Normal => { let minnorm = ::min_pos_norm_value().integer_decode(); - if mant == minnorm.0 && exp == minnorm.1 { - // (maxmant, exp - 1) -- (minnormmant, exp) -- (minnormmant + 1, exp) + if mant == minnorm.0 { + // neighbors: (maxmant, exp - 1) -- (minnormmant, exp) -- (minnormmant + 1, exp) // where maxmant = minnormmant * 2 - 1 - FullDecoded::Finite(Decoded { mant: mant << 1, minus: 1, plus: 2, - exp: exp - 1, inclusive: even }) + FullDecoded::Finite(Decoded { mant: mant << 2, minus: 1, plus: 2, + exp: exp - 2, inclusive: even }) } else { - // (mant - 1, exp) -- (mant, exp) -- (mant + 1, exp) + // neighbors: (mant - 1, exp) -- (mant, exp) -- (mant + 1, exp) FullDecoded::Finite(Decoded { mant: mant << 1, minus: 1, plus: 1, exp: exp - 1, inclusive: even }) } diff --git a/src/libcoretest/num/flt2dec/mod.rs b/src/libcoretest/num/flt2dec/mod.rs index 3ffec7c007e5f..95f9eeec2c87f 100644 --- a/src/libcoretest/num/flt2dec/mod.rs +++ b/src/libcoretest/num/flt2dec/mod.rs @@ -216,6 +216,13 @@ pub fn f32_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) // 10^18 * 0.314159231156617216 check_shortest!(f(3.141592e17f32) => b"3141592", 18); + // regression test for decoders + // 10^8 * 0.3355443 + // 10^8 * 0.33554432 + // 10^8 * 0.33554436 + let twoto25: f32 = StdFloat::ldexp(1.0, 25); + check_shortest!(f(twoto25) => b"33554432", 8); + // 10^39 * 0.340282326356119256160033759537265639424 // 10^39 * 0.34028234663852885981170418348451692544 // 10^39 * 0.340282366920938463463374607431768211456 @@ -308,6 +315,13 @@ pub fn f64_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) // 10^18 * 0.314159200000000064 check_shortest!(f(3.141592e17f64) => b"3141592", 18); + // regression test for decoders + // 10^20 * 0.18446744073709549568 + // 10^20 * 0.18446744073709551616 + // 10^20 * 0.18446744073709555712 + let twoto64: f64 = StdFloat::ldexp(1.0, 64); + check_shortest!(f(twoto64) => b"18446744073709552", 20); + // pathological case: high = 10^23 (exact). tie breaking should always prefer that. // 10^24 * 0.099999999999999974834176 // 10^24 * 0.099999999999999991611392 @@ -492,7 +506,7 @@ pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) // so why not simply testing all of them? // // this is of course very stressful (and thus should be behind an `#[ignore]` attribute), - // but with `-O3 -C lto` this only takes about two hours or so. + // but with `-C opt-level=3 -C lto` this only takes about an hour or so. // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test", @@ -500,7 +514,7 @@ pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) let x: f32 = unsafe {mem::transmute(i as u32 + 1)}; decode_finite(x) }); - assert_eq!((npassed, nignored), (2121451879, 17643160)); + assert_eq!((npassed, nignored), (2121451881, 17643158)); } fn to_string_with_parts(mut f: F) -> String diff --git a/src/libcoretest/num/flt2dec/strategy/grisu.rs b/src/libcoretest/num/flt2dec/strategy/grisu.rs index 3d798c8726cdd..e5b8a9dcc38fc 100644 --- a/src/libcoretest/num/flt2dec/strategy/grisu.rs +++ b/src/libcoretest/num/flt2dec/strategy/grisu.rs @@ -60,7 +60,7 @@ fn shortest_f32_exhaustive_equivalence_test() { // // this reports the progress and the number of f32 values returned `None`. // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print: - // `done, ignored=17643160 passed=2121451879 failed=0`. + // `done, ignored=17643158 passed=2121451881 failed=0`. use core::num::flt2dec::strategy::dragon::format_shortest as fallback; f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); From a641b05fda448d3388347838076e3c583a5f8fa4 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 23 Apr 2015 18:37:39 +0900 Subject: [PATCH 7/9] core: updated for the master changes. The master no longer has `std::num::Float`, so a generic `ldexp` is not readily available. `DecodableFloat::ldexpi` works around this. --- src/libcore/num/flt2dec/decoder.rs | 5 + src/libcoretest/num/flt2dec/estimator.rs | 4 +- src/libcoretest/num/flt2dec/mod.rs | 185 +++++++++++------------ 3 files changed, 96 insertions(+), 98 deletions(-) diff --git a/src/libcore/num/flt2dec/decoder.rs b/src/libcore/num/flt2dec/decoder.rs index d4473e6bc0c4a..f98bc11a315f0 100644 --- a/src/libcore/num/flt2dec/decoder.rs +++ b/src/libcore/num/flt2dec/decoder.rs @@ -53,15 +53,20 @@ pub enum FullDecoded { /// A floating point type which can be `decode`d. pub trait DecodableFloat: Float + Copy { + /// Returns `x * 2^exp`. Almost same to `std::{f32,f64}::ldexp`. + /// This is used for testing. + fn ldexpi(f: i64, exp: isize) -> Self; /// The minimum positive normalized value. fn min_pos_norm_value() -> Self; } impl DecodableFloat for f32 { + fn ldexpi(f: i64, exp: isize) -> Self { f as Self * (exp as Self).exp2() } fn min_pos_norm_value() -> Self { f32::MIN_POSITIVE } } impl DecodableFloat for f64 { + fn ldexpi(f: i64, exp: isize) -> Self { f as Self * (exp as Self).exp2() } fn min_pos_norm_value() -> Self { f64::MIN_POSITIVE } } diff --git a/src/libcoretest/num/flt2dec/estimator.rs b/src/libcoretest/num/flt2dec/estimator.rs index 3b9d777a98095..21260c520f623 100644 --- a/src/libcoretest/num/flt2dec/estimator.rs +++ b/src/libcoretest/num/flt2dec/estimator.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::num::Float; +use std::f64; use core::num::flt2dec::estimator::*; #[test] @@ -54,7 +54,7 @@ fn test_estimate_scaling_factor() { assert_almost_eq!(estimate_scaling_factor(0x1fffffffffffff, 971), 309); for i in -1074..972 { - let expected = Float::ldexp(1.0, i).log10().ceil(); + let expected = f64::ldexp(1.0, i).log10().ceil(); assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16); } } diff --git a/src/libcoretest/num/flt2dec/mod.rs b/src/libcoretest/num/flt2dec/mod.rs index 95f9eeec2c87f..7b076e8053203 100644 --- a/src/libcoretest/num/flt2dec/mod.rs +++ b/src/libcoretest/num/flt2dec/mod.rs @@ -10,7 +10,6 @@ use std::prelude::v1::*; use std::{str, mem, i16, f32, f64, fmt}; -use std::num::Float as StdFloat; use std::slice::bytes; use std::__rand as rand; use rand::{Rand, XorShiftRng}; @@ -159,12 +158,12 @@ fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16 } } -fn check_exact_one(mut f: F, x: T, e: isize, tstr: &str, expected: &[u8], expectedk: i16) - where T: DecodableFloat + StdFloat + fmt::Display, +fn check_exact_one(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16) + where T: DecodableFloat + fmt::Display, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { // use a large enough buffer let mut buf = [b'_'; 1024]; - let v: T = StdFloat::ldexp(x, e); + let v: T = DecodableFloat::ldexpi(x, e); let decoded = decode_finite(v); try_exact!(f(&decoded) => &mut buf, &expected, expectedk; @@ -220,8 +219,7 @@ pub fn f32_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) // 10^8 * 0.3355443 // 10^8 * 0.33554432 // 10^8 * 0.33554436 - let twoto25: f32 = StdFloat::ldexp(1.0, 25); - check_shortest!(f(twoto25) => b"33554432", 8); + check_shortest!(f(f32::ldexp(1.0, 25)) => b"33554432", 8); // 10^39 * 0.340282326356119256160033759537265639424 // 10^39 * 0.34028234663852885981170418348451692544 @@ -236,13 +234,13 @@ pub fn f32_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) // 10^-44 * 0 // 10^-44 * 0.1401298464324817070923729583289916131280... // 10^-44 * 0.2802596928649634141847459166579832262560... - let minf32: f32 = StdFloat::ldexp(1.0, -149); + let minf32 = f32::ldexp(1.0, -149); check_shortest!(f(minf32) => b"1", -44); } pub fn f32_exact_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { - let minf32: f32 = StdFloat::ldexp(1.0, -149); + let minf32 = f32::ldexp(1.0, -149); check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0); check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0); @@ -253,32 +251,32 @@ pub fn f32_exact_sanity_test(mut f: F) check_exact!(f(minf32) => b"1401298464324817070923729583289916131280", -44); // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP - check_exact_one!(f(12676506.0, -102; f32) => b"2", -23); - check_exact_one!(f(12676506.0, -103; f32) => b"12", -23); - check_exact_one!(f(15445013.0, 86; f32) => b"119", 34); - check_exact_one!(f(13734123.0, -138; f32) => b"3941", -34); - check_exact_one!(f(12428269.0, -130; f32) => b"91308", -32); - check_exact_one!(f(15334037.0, -146; f32) => b"171900", -36); - check_exact_one!(f(11518287.0, -41; f32) => b"5237910", -5); - check_exact_one!(f(12584953.0, -145; f32) => b"28216440", -36); - check_exact_one!(f(15961084.0, -125; f32) => b"375243281", -30); - check_exact_one!(f(14915817.0, -146; f32) => b"1672120916", -36); - check_exact_one!(f(10845484.0, -102; f32) => b"21388945814", -23); - check_exact_one!(f(16431059.0, -61; f32) => b"712583594561", -11); + check_exact_one!(f(12676506, -102; f32) => b"2", -23); + check_exact_one!(f(12676506, -103; f32) => b"12", -23); + check_exact_one!(f(15445013, 86; f32) => b"119", 34); + check_exact_one!(f(13734123, -138; f32) => b"3941", -34); + check_exact_one!(f(12428269, -130; f32) => b"91308", -32); + check_exact_one!(f(15334037, -146; f32) => b"171900", -36); + check_exact_one!(f(11518287, -41; f32) => b"5237910", -5); + check_exact_one!(f(12584953, -145; f32) => b"28216440", -36); + check_exact_one!(f(15961084, -125; f32) => b"375243281", -30); + check_exact_one!(f(14915817, -146; f32) => b"1672120916", -36); + check_exact_one!(f(10845484, -102; f32) => b"21388945814", -23); + check_exact_one!(f(16431059, -61; f32) => b"712583594561", -11); // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP - check_exact_one!(f(16093626.0, 69; f32) => b"1", 29); - check_exact_one!(f( 9983778.0, 25; f32) => b"34", 15); - check_exact_one!(f(12745034.0, 104; f32) => b"259", 39); - check_exact_one!(f(12706553.0, 72; f32) => b"6001", 29); - check_exact_one!(f(11005028.0, 45; f32) => b"38721", 21); - check_exact_one!(f(15059547.0, 71; f32) => b"355584", 29); - check_exact_one!(f(16015691.0, -99; f32) => b"2526831", -22); - check_exact_one!(f( 8667859.0, 56; f32) => b"62458507", 24); - check_exact_one!(f(14855922.0, -82; f32) => b"307213267", -17); - check_exact_one!(f(14855922.0, -83; f32) => b"1536066333", -17); - check_exact_one!(f(10144164.0, -110; f32) => b"78147796834", -26); - check_exact_one!(f(13248074.0, 95; f32) => b"524810279937", 36); + check_exact_one!(f(16093626, 69; f32) => b"1", 29); + check_exact_one!(f( 9983778, 25; f32) => b"34", 15); + check_exact_one!(f(12745034, 104; f32) => b"259", 39); + check_exact_one!(f(12706553, 72; f32) => b"6001", 29); + check_exact_one!(f(11005028, 45; f32) => b"38721", 21); + check_exact_one!(f(15059547, 71; f32) => b"355584", 29); + check_exact_one!(f(16015691, -99; f32) => b"2526831", -22); + check_exact_one!(f( 8667859, 56; f32) => b"62458507", 24); + check_exact_one!(f(14855922, -82; f32) => b"307213267", -17); + check_exact_one!(f(14855922, -83; f32) => b"1536066333", -17); + check_exact_one!(f(10144164, -110; f32) => b"78147796834", -26); + check_exact_one!(f(13248074, 95; f32) => b"524810279937", 36); } pub fn f64_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { @@ -319,8 +317,7 @@ pub fn f64_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) // 10^20 * 0.18446744073709549568 // 10^20 * 0.18446744073709551616 // 10^20 * 0.18446744073709555712 - let twoto64: f64 = StdFloat::ldexp(1.0, 64); - check_shortest!(f(twoto64) => b"18446744073709552", 20); + check_shortest!(f(f64::ldexp(1.0, 64)) => b"18446744073709552", 20); // pathological case: high = 10^23 (exact). tie breaking should always prefer that. // 10^24 * 0.099999999999999974834176 @@ -341,13 +338,13 @@ pub fn f64_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) // 10^-323 * 0 // 10^-323 * 0.4940656458412465441765687928682213723650... // 10^-323 * 0.9881312916824930883531375857364427447301... - let minf64: f64 = StdFloat::ldexp(1.0, -1074); + let minf64 = f64::ldexp(1.0, -1074); check_shortest!(f(minf64) => b"5", -323); } pub fn f64_exact_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { - let minf64: f64 = StdFloat::ldexp(1.0, -1074); + let minf64 = f64::ldexp(1.0, -1074); check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0); check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0); @@ -381,52 +378,52 @@ pub fn f64_exact_sanity_test(mut f: F) 7538682506419718265533447265625 ", -323); // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP - check_exact_one!(f(8511030020275656.0, -342; f64) => b"9", -87); - check_exact_one!(f(5201988407066741.0, -824; f64) => b"46", -232); - check_exact_one!(f(6406892948269899.0, 237; f64) => b"141", 88); - check_exact_one!(f(8431154198732492.0, 72; f64) => b"3981", 38); - check_exact_one!(f(6475049196144587.0, 99; f64) => b"41040", 46); - check_exact_one!(f(8274307542972842.0, 726; f64) => b"292084", 235); - check_exact_one!(f(5381065484265332.0, -456; f64) => b"2891946", -121); - check_exact_one!(f(6761728585499734.0, -1057; f64) => b"43787718", -302); - check_exact_one!(f(7976538478610756.0, 376; f64) => b"122770163", 130); - check_exact_one!(f(5982403858958067.0, 377; f64) => b"1841552452", 130); - check_exact_one!(f(5536995190630837.0, 93; f64) => b"54835744350", 44); - check_exact_one!(f(7225450889282194.0, 710; f64) => b"389190181146", 230); - check_exact_one!(f(7225450889282194.0, 709; f64) => b"1945950905732", 230); - check_exact_one!(f(8703372741147379.0, 117; f64) => b"14460958381605", 52); - check_exact_one!(f(8944262675275217.0, -1001; f64) => b"417367747458531", -285); - check_exact_one!(f(7459803696087692.0, -707; f64) => b"1107950772878888", -196); - check_exact_one!(f(6080469016670379.0, -381; f64) => b"12345501366327440", -98); - check_exact_one!(f(8385515147034757.0, 721; f64) => b"925031711960365024", 233); - check_exact_one!(f(7514216811389786.0, -828; f64) => b"4198047150284889840", -233); - check_exact_one!(f(8397297803260511.0, -345; f64) => b"11716315319786511046", -87); - check_exact_one!(f(6733459239310543.0, 202; f64) => b"432810072844612493629", 77); - check_exact_one!(f(8091450587292794.0, -473; f64) => b"3317710118160031081518", -126); + check_exact_one!(f(8511030020275656, -342; f64) => b"9", -87); + check_exact_one!(f(5201988407066741, -824; f64) => b"46", -232); + check_exact_one!(f(6406892948269899, 237; f64) => b"141", 88); + check_exact_one!(f(8431154198732492, 72; f64) => b"3981", 38); + check_exact_one!(f(6475049196144587, 99; f64) => b"41040", 46); + check_exact_one!(f(8274307542972842, 726; f64) => b"292084", 235); + check_exact_one!(f(5381065484265332, -456; f64) => b"2891946", -121); + check_exact_one!(f(6761728585499734, -1057; f64) => b"43787718", -302); + check_exact_one!(f(7976538478610756, 376; f64) => b"122770163", 130); + check_exact_one!(f(5982403858958067, 377; f64) => b"1841552452", 130); + check_exact_one!(f(5536995190630837, 93; f64) => b"54835744350", 44); + check_exact_one!(f(7225450889282194, 710; f64) => b"389190181146", 230); + check_exact_one!(f(7225450889282194, 709; f64) => b"1945950905732", 230); + check_exact_one!(f(8703372741147379, 117; f64) => b"14460958381605", 52); + check_exact_one!(f(8944262675275217, -1001; f64) => b"417367747458531", -285); + check_exact_one!(f(7459803696087692, -707; f64) => b"1107950772878888", -196); + check_exact_one!(f(6080469016670379, -381; f64) => b"12345501366327440", -98); + check_exact_one!(f(8385515147034757, 721; f64) => b"925031711960365024", 233); + check_exact_one!(f(7514216811389786, -828; f64) => b"4198047150284889840", -233); + check_exact_one!(f(8397297803260511, -345; f64) => b"11716315319786511046", -87); + check_exact_one!(f(6733459239310543, 202; f64) => b"432810072844612493629", 77); + check_exact_one!(f(8091450587292794, -473; f64) => b"3317710118160031081518", -126); // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP - check_exact_one!(f(6567258882077402.0, 952; f64) => b"3", 303); - check_exact_one!(f(6712731423444934.0, 535; f64) => b"76", 177); - check_exact_one!(f(6712731423444934.0, 534; f64) => b"378", 177); - check_exact_one!(f(5298405411573037.0, -957; f64) => b"4350", -272); - check_exact_one!(f(5137311167659507.0, -144; f64) => b"23037", -27); - check_exact_one!(f(6722280709661868.0, 363; f64) => b"126301", 126); - check_exact_one!(f(5344436398034927.0, -169; f64) => b"7142211", -35); - check_exact_one!(f(8369123604277281.0, -853; f64) => b"13934574", -240); - check_exact_one!(f(8995822108487663.0, -780; f64) => b"141463449", -218); - check_exact_one!(f(8942832835564782.0, -383; f64) => b"4539277920", -99); - check_exact_one!(f(8942832835564782.0, -384; f64) => b"22696389598", -99); - check_exact_one!(f(8942832835564782.0, -385; f64) => b"113481947988", -99); - check_exact_one!(f(6965949469487146.0, -249; f64) => b"7700366561890", -59); - check_exact_one!(f(6965949469487146.0, -250; f64) => b"38501832809448", -59); - check_exact_one!(f(6965949469487146.0, -251; f64) => b"192509164047238", -59); - check_exact_one!(f(7487252720986826.0, 548; f64) => b"6898586531774201", 181); - check_exact_one!(f(5592117679628511.0, 164; f64) => b"13076622631878654", 66); - check_exact_one!(f(8887055249355788.0, 665; f64) => b"136052020756121240", 217); - check_exact_one!(f(6994187472632449.0, 690; f64) => b"3592810217475959676", 224); - check_exact_one!(f(8797576579012143.0, 588; f64) => b"89125197712484551899", 193); - check_exact_one!(f(7363326733505337.0, 272; f64) => b"558769757362301140950", 98); - check_exact_one!(f(8549497411294502.0, -448; f64) => b"1176257830728540379990", -118); + check_exact_one!(f(6567258882077402, 952; f64) => b"3", 303); + check_exact_one!(f(6712731423444934, 535; f64) => b"76", 177); + check_exact_one!(f(6712731423444934, 534; f64) => b"378", 177); + check_exact_one!(f(5298405411573037, -957; f64) => b"4350", -272); + check_exact_one!(f(5137311167659507, -144; f64) => b"23037", -27); + check_exact_one!(f(6722280709661868, 363; f64) => b"126301", 126); + check_exact_one!(f(5344436398034927, -169; f64) => b"7142211", -35); + check_exact_one!(f(8369123604277281, -853; f64) => b"13934574", -240); + check_exact_one!(f(8995822108487663, -780; f64) => b"141463449", -218); + check_exact_one!(f(8942832835564782, -383; f64) => b"4539277920", -99); + check_exact_one!(f(8942832835564782, -384; f64) => b"22696389598", -99); + check_exact_one!(f(8942832835564782, -385; f64) => b"113481947988", -99); + check_exact_one!(f(6965949469487146, -249; f64) => b"7700366561890", -59); + check_exact_one!(f(6965949469487146, -250; f64) => b"38501832809448", -59); + check_exact_one!(f(6965949469487146, -251; f64) => b"192509164047238", -59); + check_exact_one!(f(7487252720986826, 548; f64) => b"6898586531774201", 181); + check_exact_one!(f(5592117679628511, 164; f64) => b"13076622631878654", 66); + check_exact_one!(f(8887055249355788, 665; f64) => b"136052020756121240", 217); + check_exact_one!(f(6994187472632449, 690; f64) => b"3592810217475959676", 224); + check_exact_one!(f(8797576579012143, 588; f64) => b"89125197712484551899", 193); + check_exact_one!(f(7363326733505337, 272; f64) => b"558769757362301140950", 98); + check_exact_one!(f(8549497411294502, -448; f64) => b"1176257830728540379990", -118); } pub fn more_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { @@ -532,8 +529,7 @@ pub fn to_shortest_str_test(mut f_: F) use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String - where T: DecodableFloat + StdFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { to_string_with_parts(|buf, parts| to_shortest_str(|d,b| f(d,b), v, sign, frac_digits, upper, buf, parts)) } @@ -600,7 +596,7 @@ pub fn to_shortest_str_test(mut f_: F) assert_eq!(to_string(f, f32::MAX, Minus, 1, false), format!("34028235{:0>31}.0", "")); assert_eq!(to_string(f, f32::MAX, Minus, 8, false), format!("34028235{:0>31}.00000000", "")); - let minf32: f32 = StdFloat::ldexp(1.0, -149); + let minf32 = f32::ldexp(1.0, -149); assert_eq!(to_string(f, minf32, Minus, 0, false), format!("0.{:0>44}1", "")); assert_eq!(to_string(f, minf32, Minus, 45, false), format!("0.{:0>44}1", "")); assert_eq!(to_string(f, minf32, Minus, 46, false), format!("0.{:0>44}10", "")); @@ -612,7 +608,7 @@ pub fn to_shortest_str_test(mut f_: F) assert_eq!(to_string(f, f64::MAX, Minus, 8, false), format!("17976931348623157{:0>292}.00000000", "")); - let minf64: f64 = StdFloat::ldexp(1.0, -1074); + let minf64 = f64::ldexp(1.0, -1074); assert_eq!(to_string(f, minf64, Minus, 0, false), format!("0.{:0>323}5", "")); assert_eq!(to_string(f, minf64, Minus, 324, false), format!("0.{:0>323}5", "")); assert_eq!(to_string(f, minf64, Minus, 325, false), format!("0.{:0>323}50", "")); @@ -626,8 +622,7 @@ pub fn to_shortest_exp_str_test(mut f_: F) use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String - where T: DecodableFloat + StdFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { to_string_with_parts(|buf, parts| to_shortest_exp_str(|d,b| f(d,b), v, sign, exp_bounds, upper, buf, parts)) } @@ -715,7 +710,7 @@ pub fn to_shortest_exp_str_test(mut f_: F) assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); - let minf32: f32 = StdFloat::ldexp(1.0, -149); + let minf32 = f32::ldexp(1.0, -149); assert_eq!(to_string(f, minf32, Minus, ( -4, 16), false), "1e-45"); assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); @@ -727,7 +722,7 @@ pub fn to_shortest_exp_str_test(mut f_: F) assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); - let minf64: f64 = StdFloat::ldexp(1.0, -1074); + let minf64 = f64::ldexp(1.0, -1074); assert_eq!(to_string(f, minf64, Minus, ( -4, 16), false), "5e-324"); assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); @@ -740,8 +735,7 @@ pub fn to_exact_exp_str_test(mut f_: F) use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String - where T: DecodableFloat + StdFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { to_string_with_parts(|buf, parts| to_exact_exp_str(|d,b,l| f(d,b,l), v, sign, ndigits, upper, buf, parts)) } @@ -860,7 +854,7 @@ pub fn to_exact_exp_str_test(mut f_: F) assert_eq!(to_string(f, f32::MAX, Minus, 64, false), "3.402823466385288598117041834845169254400000000000000000000000000e38"); - let minf32: f32 = StdFloat::ldexp(1.0, -149); + let minf32 = f32::ldexp(1.0, -149); assert_eq!(to_string(f, minf32, Minus, 1, false), "1e-45"); assert_eq!(to_string(f, minf32, Minus, 2, false), "1.4e-45"); assert_eq!(to_string(f, minf32, Minus, 4, false), "1.401e-45"); @@ -900,7 +894,7 @@ pub fn to_exact_exp_str_test(mut f_: F) 0000000000000000000000000000000000000000000000000000000000000000e308"); // okay, this is becoming tough. fortunately for us, this is almost the worst case. - let minf64: f64 = StdFloat::ldexp(1.0, -1074); + let minf64 = f64::ldexp(1.0, -1074); assert_eq!(to_string(f, minf64, Minus, 1, false), "5e-324"); assert_eq!(to_string(f, minf64, Minus, 2, false), "4.9e-324"); assert_eq!(to_string(f, minf64, Minus, 4, false), "4.941e-324"); @@ -961,8 +955,7 @@ pub fn to_exact_fixed_str_test(mut f_: F) use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String - where T: DecodableFloat + StdFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { + where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) { to_string_with_parts(|buf, parts| to_exact_fixed_str(|d,b,l| f(d,b,l), v, sign, frac_digits, upper, buf, parts)) } @@ -1102,7 +1095,7 @@ pub fn to_exact_fixed_str_test(mut f_: F) assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "340282346638528859811704183484516925440.00"); - let minf32: f32 = StdFloat::ldexp(1.0, -149); + let minf32 = f32::ldexp(1.0, -149); assert_eq!(to_string(f, minf32, Minus, 0, false), "0"); assert_eq!(to_string(f, minf32, Minus, 1, false), "0.0"); assert_eq!(to_string(f, minf32, Minus, 2, false), "0.00"); @@ -1134,7 +1127,7 @@ pub fn to_exact_fixed_str_test(mut f_: F) 9440758685084551339423045832369032229481658085593321233482747978\ 26204144723168738177180919299881250404026184124858368.0000000000"); - let minf64: f64 = StdFloat::ldexp(1.0, -1074); + let minf64 = f64::ldexp(1.0, -1074); assert_eq!(to_string(f, minf64, Minus, 0, false), "0"); assert_eq!(to_string(f, minf64, Minus, 1, false), "0.0"); assert_eq!(to_string(f, minf64, Minus, 10, false), "0.0000000000"); From 3d34e177dd19bfbc6885b03eac0632adc9c621b2 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Wed, 6 May 2015 21:11:14 +0900 Subject: [PATCH 8/9] core: use banker's rounding for the exact mode in flt2dec. For the shortest mode the IEEE 754 decoder already provides an exact rounding range accounting for banker's rounding, but it was not the case for the exact mode. This commit alters the exact mode algorithm for Dragon so that any number ending at `...x5000...` with even `x` and infinite zeroes will round to `...x` instead of `...(x+1)` as it was. Grisu is not affected by this change because this halfway case always results in the failure for Grisu. --- src/libcore/num/flt2dec/strategy/dragon.rs | 6 +++++- src/libcoretest/num/flt2dec/mod.rs | 23 +++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/libcore/num/flt2dec/strategy/dragon.rs b/src/libcore/num/flt2dec/strategy/dragon.rs index bd735594e8225..a819932525bd1 100644 --- a/src/libcore/num/flt2dec/strategy/dragon.rs +++ b/src/libcore/num/flt2dec/strategy/dragon.rs @@ -307,7 +307,11 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi } // rounding up if we stop in the middle of digits - if mant >= *scale.mul_small(5) { + // if the following digits are exactly 5000..., check the prior digit and try to + // round to even (i.e. avoid rounding up when the prior digit is even). + let order = mant.cmp(scale.mul_small(5)); + if order == Ordering::Greater || (order == Ordering::Equal && + (len == 0 || buf[len-1] & 1 == 1)) { // if rounding up changes the length, the exponent should also change. // but we've been requested a fixed number of digits, so do not alter the buffer... if let Some(c) = round_up(buf, len) { diff --git a/src/libcoretest/num/flt2dec/mod.rs b/src/libcoretest/num/flt2dec/mod.rs index 7b076e8053203..488cd3a779ca3 100644 --- a/src/libcoretest/num/flt2dec/mod.rs +++ b/src/libcoretest/num/flt2dec/mod.rs @@ -105,11 +105,17 @@ fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16 bytes::copy_memory(&expected[..i], &mut expected_); let mut expectedk_ = expectedk; if expected[i] >= b'5' { - // if this returns true, expected_[..i] is all `9`s and being rounded up. - // we should always return `100..00` (`i` digits) instead, since that's - // what we can came up with `i` digits anyway. `round_up` assumes that - // the adjustment to the length is done by caller, which we simply ignore. - if let Some(_) = round_up(&mut expected_, i) { expectedk_ += 1; } + // check if this is a rounding-to-even case. + // we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even. + if !(i+1 < expected.len() && expected[i-1] & 1 == 0 && + expected[i] == b'5' && + expected[i+1] == b' ') { + // if this returns true, expected_[..i] is all `9`s and being rounded up. + // we should always return `100..00` (`i` digits) instead, since that's + // what we can came up with `i` digits anyway. `round_up` assumes that + // the adjustment to the length is done by caller, which we simply ignore. + if let Some(_) = round_up(&mut expected_, i) { expectedk_ += 1; } + } } try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_; @@ -243,6 +249,7 @@ pub fn f32_exact_sanity_test(mut f: F) let minf32 = f32::ldexp(1.0, -149); check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0); + check_exact!(f(0.5f32) => b"5 ", 0); check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0); check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1); check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18); @@ -348,6 +355,7 @@ pub fn f64_exact_sanity_test(mut f: F) check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0); check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0); + check_exact!(f(0.5f64) => b"5 ", 0); check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0); check_exact!(f(100.0f64) => b"1 ", 3); check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3); @@ -1032,6 +1040,11 @@ pub fn to_exact_fixed_str_test(mut f_: F) assert_eq!(to_string(f, 999.5, Minus, 3, false), "999.500"); assert_eq!(to_string(f, 999.5, Minus, 30, false), "999.500000000000000000000000000000"); + assert_eq!(to_string(f, 0.5, Minus, 0, false), "1"); + assert_eq!(to_string(f, 0.5, Minus, 1, false), "0.5"); + assert_eq!(to_string(f, 0.5, Minus, 2, false), "0.50"); + assert_eq!(to_string(f, 0.5, Minus, 3, false), "0.500"); + assert_eq!(to_string(f, 0.95, Minus, 0, false), "1"); assert_eq!(to_string(f, 0.95, Minus, 1, false), "0.9"); // because it really is less than 0.95 assert_eq!(to_string(f, 0.95, Minus, 2, false), "0.95"); From 1aecd17463bb16d03462cf31edb920b9f47ddf2c Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Sat, 9 May 2015 22:52:47 +0900 Subject: [PATCH 9/9] test: fixed some tests affected by banker's rounding. --- src/test/run-pass/ifmt.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 01a38b374cc40..240b6286c8cfc 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -138,10 +138,10 @@ pub fn main() { t!(format!("{:e}", 1.2345e6f32), "1.2345e6"); t!(format!("{:e}", 1.2345e6f64), "1.2345e6"); t!(format!("{:E}", 1.2345e6f64), "1.2345E6"); - t!(format!("{:.3e}", 1.2345e6f64), "1.235e6"); - t!(format!("{:10.3e}", 1.2345e6f64), " 1.235e6"); - t!(format!("{:+10.3e}", 1.2345e6f64), " +1.235e6"); - t!(format!("{:+10.3e}", -1.2345e6f64), " -1.235e6"); + t!(format!("{:.3e}", 1.2345e6f64), "1.234e6"); + t!(format!("{:10.3e}", 1.2345e6f64), " 1.234e6"); + t!(format!("{:+10.3e}", 1.2345e6f64), " +1.234e6"); + t!(format!("{:+10.3e}", -1.2345e6f64), " -1.234e6"); // Float edge cases t!(format!("{}", -0.0), "0");