New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add exponential formatting to BigInt #214
base: master
Are you sure you want to change the base?
Changes from 2 commits
cbfb0e8
bb8095e
1bfdd2f
c778394
dd658db
f182b63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ use crate::std_alloc::{String, Vec}; | |
use core::cmp; | ||
use core::cmp::Ordering; | ||
use core::default::Default; | ||
use core::fmt; | ||
use core::fmt::{self, Write}; | ||
use core::hash; | ||
use core::mem; | ||
use core::str; | ||
|
@@ -33,6 +33,7 @@ mod serde; | |
|
||
pub(crate) use self::convert::to_str_radix_reversed; | ||
pub use self::iter::{U32Digits, U64Digits}; | ||
use crate::Sign; | ||
|
||
/// A big unsigned integer type. | ||
pub struct BigUint { | ||
|
@@ -143,6 +144,72 @@ impl fmt::Octal for BigUint { | |
} | ||
} | ||
|
||
impl fmt::LowerExp for BigUint { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
if let Option::Some(small) = self.to_u64() { | ||
fmt::LowerExp::fmt(&small, f) | ||
} else { | ||
// The max digits that can fit into a f64, which ensures | ||
// that printing won't have rounding errors. | ||
const MAX_FLOAT_DIGITS: usize = 14; | ||
if f.precision().filter(|x| x < &MAX_FLOAT_DIGITS).is_some() { | ||
let as_f64 = self.to_f64().unwrap(); | ||
if as_f64.is_finite() { | ||
fmt::LowerExp::fmt(&as_f64, f) | ||
} else { | ||
fmt_exp_slow(self, f, false, true) | ||
} | ||
} else { | ||
fmt_exp_slow(self, f, false, true) | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl fmt::UpperExp for BigUint { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
if let Option::Some(small) = self.to_u64() { | ||
fmt::UpperExp::fmt(&small, f) | ||
} else { | ||
// The max digits that can fit into a f64, which ensures | ||
// that printing won't have rounding errors. | ||
const MAX_FLOAT_DIGITS: usize = 14; | ||
if f.precision().filter(|&x| x < MAX_FLOAT_DIGITS).is_some() { | ||
let as_f64 = self.to_f64().unwrap(); | ||
if as_f64.is_finite() { | ||
fmt::UpperExp::fmt(&as_f64, f) | ||
} else { | ||
fmt_exp_slow(self, f, true, true) | ||
} | ||
} else { | ||
fmt_exp_slow(self, f, true, true) | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub(crate) fn fmt_exp_slow(x: &BigUint, f: &mut fmt::Formatter<'_>, upper: bool, is_nonneg: bool) -> fmt::Result { | ||
let precision = f.precision().unwrap_or(16); | ||
let digits = to_str_radix_reversed(x, 10); | ||
let mut buffer = String::with_capacity(precision + 5); | ||
debug_assert!(digits.len() > 1, "Values with fewer digits should use direct formatting"); | ||
let mut iter = digits.iter().rev(); | ||
buffer.push(*iter.next().unwrap() as char); | ||
buffer.push('.'); | ||
for &ch in iter.take(precision) { | ||
buffer.push(ch as char); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we need rounding if there were more digits than the precision required? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! Added. |
||
// Add trailing zeroes when explicit precision given | ||
if f.precision().is_some() { | ||
for _ in 0..precision.saturating_sub(digits.len() - 1) { | ||
buffer.push('0'); | ||
} | ||
} | ||
buffer.push(if upper { 'E' } else { 'e' }); | ||
write!(buffer, "{}", digits.len() - 1)?; | ||
f.pad_integral(is_nonneg, "", &buffer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's definitely weird, but it does seem to do the right thing. I've added a comment to that effect. |
||
} | ||
|
||
impl Zero for BigUint { | ||
#[inline] | ||
fn zero() -> BigUint { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f64::DIGITS
is 15, though it does say "approximate". Perhaps we should us that, minus one?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I subtracted 1 to ensure the rounding wouldn't turn out weird, but forgot to update the docs. I didn't know
f64::DIGITS
existed, so I'll update to take that into account.