-
Notifications
You must be signed in to change notification settings - Fork 12.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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/
- Loading branch information
1 parent
7bd7163
commit c82da7a
Showing
14 changed files
with
3,895 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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); | ||
} | ||
|
Oops, something went wrong.