Skip to content

Commit

Permalink
v0.4.5
Browse files Browse the repository at this point in the history
  • Loading branch information
akubera committed Jun 17, 2024
2 parents 609cd3d + 682343b commit 369dfae
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
command: scripts/bigdecimal-property-tests enable
- run:
name: Generate cargo.lock
command: cargo generate-lockfile
command: if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi
- restore_cache:
keys:
- bigdecimal-cargo-<< parameters.rust-version >>-{{ checksum "Cargo.lock" }}
Expand Down
157 changes: 157 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bigdecimal"
version = "0.4.4"
version = "0.4.5"
authors = ["Andrew Kubera"]
description = "Arbitrary precision decimal numbers"
documentation = "https://docs.rs/bigdecimal"
Expand All @@ -21,9 +21,9 @@ bench = false

[dependencies]
libm = "0.2.6"
num-bigint = { version = ">=0.3.0,<0.4.5", default-features = false }
num-integer = { version = "^0.1", default-features = false }
num-traits = { version = ">0.2.0,<0.2.19", default-features = false }
num-bigint = { version = "0.4", default-features = false }
num-integer = { version = "0.1", default-features = false }
num-traits = { version = "0.2", default-features = false }
serde = { version = "1.0", optional = true, default-features = false }
# Allow direct parsing of JSON floats, for full arbitrary precision
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc", "arbitrary_precision"]}
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ use serde_json;
#[derive(Debug,Serialize,Deserialize)]
struct MyStruct {
name: String,
// this will be witten to json as string
// this will be written to json as string
value: BigDecimal,
// this will be witten to json as number
// this will be written to json as number
#[serde(with = "bigdecimal::serde::json_num")]
number: BigDecimal,
}
Expand Down
14 changes: 14 additions & 0 deletions src/impl_cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,20 @@ mod test {
impl_test!(case_1116xx459_759xx717e2: "1116386634271380982470843247639640260491505327092723527088459" < "759522625769651746138617259189939751893902453291243506584717" e 2);
}

/// Test that large-magnitidue exponentials will not crash
#[test]
fn test_cmp_on_exp_boundaries() {
let a = BigDecimal::new(1.into(), i64::MAX);
let z = BigDecimal::new(1.into(), i64::MIN);
assert_ne!(a, z);
assert_ne!(z, a);

assert!(a < z);

assert_eq!(a, a);
assert_eq!(z, z);
}

mod ord {
use super::*;

Expand Down
64 changes: 41 additions & 23 deletions src/impl_fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,23 @@ fn dynamically_format_decimal(

// number of zeros between most significant digit and decimal point
let leading_zero_count = this.scale
.to_usize()
.and_then(|scale| scale.checked_sub(abs_int.len()))
.to_u64()
.and_then(|scale| scale.checked_sub(abs_int.len() as u64))
.unwrap_or(0);

// number of zeros between least significant digit and decimal point
let trailing_zero_count = this.scale
.checked_neg()
.and_then(|d| d.to_usize());
.and_then(|d| d.to_u64());

// this ignores scientific-formatting if precision is requested
let trailing_zeros = f.precision().map(|_| 0)
.or(trailing_zero_count)
.unwrap_or(0);

let leading_zero_threshold = leading_zero_threshold as u64;
let trailing_zero_threshold = trailing_zero_threshold as u64;

// use exponential form if decimal point is outside
// the upper and lower thresholds of the decimal
if leading_zero_threshold < leading_zero_count {
Expand Down Expand Up @@ -128,14 +131,14 @@ fn format_full_scale(
debug_assert_ne!(digits.len(), 0);

if this.scale <= 0 {
// formating an integer value (add trailing zeros to the right)
// formatting an integer value (add trailing zeros to the right)
zero_right_pad_integer_ascii_digits(&mut digits, &mut exp, f.precision());
} else {
let scale = this.scale as usize;
let scale = this.scale as u64;
// no-precision behaves the same as precision matching scale (i.e. no padding or rounding)
let prec = f.precision().unwrap_or(scale);
let prec = f.precision().and_then(|prec| prec.to_u64()).unwrap_or(scale);

if scale < digits.len() {
if scale < digits.len() as u64 {
// format both integer and fractional digits (always 'trim' to precision)
trim_ascii_digits(&mut digits, scale, prec, &mut exp, this.sign);
} else {
Expand All @@ -151,7 +154,7 @@ fn format_full_scale(

// add exp part to buffer (if not zero)
if exp != 0 {
write!(buf, "E{:+}", exp)?;
write!(buf, "e{:+}", exp)?;
}

// write buffer to formatter
Expand All @@ -169,7 +172,10 @@ fn zero_right_pad_integer_ascii_digits(
) {
debug_assert!(*exp >= 0);

let trailing_zero_count = exp.to_usize().unwrap();
let trailing_zero_count = match exp.to_usize() {
Some(n) => n,
None => { return; }
};
let total_additional_zeros = trailing_zero_count.saturating_add(precision.unwrap_or(0));
if total_additional_zeros > FMT_MAX_INTEGER_PADDING {
return;
Expand All @@ -195,16 +201,20 @@ fn zero_right_pad_integer_ascii_digits(
/// Fill zeros into utf-8 digits
fn trim_ascii_digits(
digits: &mut Vec<u8>,
scale: usize,
prec: usize,
scale: u64,
prec: u64,
exp: &mut i128,
sign: Sign,
) {
debug_assert!(scale < digits.len());
debug_assert!(scale < digits.len() as u64);
// there are both integer and fractional digits
let integer_digit_count = digits.len() - scale;
let integer_digit_count = (digits.len() as u64 - scale)
.to_usize()
.expect("Number of digits exceeds maximum usize");

if prec < scale {
let prec = prec.to_usize()
.expect("Precision exceeds maximum usize");
apply_rounding_to_ascii_digits(
digits, exp, integer_digit_count + prec, sign
);
Expand All @@ -215,30 +225,34 @@ fn trim_ascii_digits(
}

if scale < prec {
let trailing_zero_count = (prec - scale)
.to_usize()
.expect("Too Big");

// precision required beyond scale
digits.resize(digits.len() + (prec - scale), b'0');
digits.resize(digits.len() + trailing_zero_count, b'0');
}
}


fn shift_or_trim_fractional_digits(
digits: &mut Vec<u8>,
scale: usize,
prec: usize,
scale: u64,
prec: u64,
exp: &mut i128,
sign: Sign,
) {
debug_assert!(scale >= digits.len());
debug_assert!(scale >= digits.len() as u64);
// there are no integer digits
let leading_zeros = scale - digits.len();
let leading_zeros = scale - digits.len() as u64;

match prec.checked_sub(leading_zeros) {
None => {
digits.clear();
digits.push(b'0');
if prec > 0 {
digits.push(b'.');
digits.resize(2 + prec, b'0');
digits.resize(2 + prec as usize, b'0');
}
}
Some(0) => {
Expand All @@ -251,11 +265,15 @@ fn shift_or_trim_fractional_digits(
if leading_zeros != 0 {
digits.push(b'0');
digits.push(b'.');
digits.resize(1 + leading_zeros, b'0');
digits.resize(1 + leading_zeros as usize, b'0');
}
digits.push(rounded_value + b'0');
}
Some(digit_prec) => {
let digit_prec = digit_prec as usize;
let leading_zeros = leading_zeros
.to_usize()
.expect("Number of leading zeros exceeds max usize");
let trailing_zeros = digit_prec.saturating_sub(digits.len());
if digit_prec < digits.len() {
apply_rounding_to_ascii_digits(digits, exp, digit_prec, sign);
Expand Down Expand Up @@ -863,8 +881,8 @@ mod test {
}

impl_case!(fmt_default: "{}" => "1e+100000");
impl_case!(fmt_d1: "{:.1}" => "1E+100000");
impl_case!(fmt_d4: "{:.4}" => "1E+100000");
impl_case!(fmt_d1: "{:.1}" => "1e+100000");
impl_case!(fmt_d4: "{:.4}" => "1e+100000");
}


Expand Down Expand Up @@ -905,7 +923,7 @@ mod test {
}

impl_case!(fmt_default: "{}" => "13400476439814628800e+2502");
impl_case!(fmt_d1: "{:.1}" => "13400476439814628800E+2502");
impl_case!(fmt_d1: "{:.1}" => "13400476439814628800e+2502");
}
}

Expand Down

0 comments on commit 369dfae

Please sign in to comment.