Skip to content

Commit

Permalink
v0.4.7
Browse files Browse the repository at this point in the history
  • Loading branch information
akubera committed Dec 8, 2024
2 parents 148b4cf + ec91b68 commit b60d353
Show file tree
Hide file tree
Showing 19 changed files with 506 additions and 103 deletions.
2 changes: 2 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ cargo-publish:
- master
allow_failure: true
cache: []
# disable downloading artifacts
dependencies: []
variables:
CARGO_HOME: /usr/local/cargo
script:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bigdecimal"
version = "0.4.6"
version = "0.4.7"
authors = ["Andrew Kubera"]
description = "Arbitrary precision decimal numbers"
documentation = "https://docs.rs/bigdecimal"
Expand All @@ -15,6 +15,7 @@ keywords = [
categories = [ "mathematics", "science", "no-std" ]
license = "MIT/Apache-2.0"
autobenches = false
edition = "2015"

[lib]
bench = false
Expand Down
43 changes: 43 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

SED := `command -v gsed || command -v sed`

help:
@just --list

# git tasks to start next development version
prepare-dev-version v:
git checkout trunk
{{SED}} -zE -i 's/(name = "bigdecimal"\nversion )= [^\n]*/\1= "{{v}}+dev"/' Cargo.toml Cargo.lock
git add Cargo.toml Cargo.lock
git commit -m 'Begin v{{v}} development'


# git tasks to run to merge trunk into master
prepare-release v:
git checkout trunk
cargo clippy
{{SED}} -zE -i 's/(name = "bigdecimal"\nversion )= [^\n]*/\1= "{{v}}"/' Cargo.toml Cargo.lock
git add Cargo.toml Cargo.lock
git commit -m 'Version {{v}}'
git checkout master
git merge trunk --no-ff -m 'v{{v}}'
# git tag 'v{{v}}'

# enable and run benchmarks
benchmark *args:
scripts/benchmark-bigdecimal {{args}}

# enable and run property-tests
run-property-tests:
scripts/bigdecimal-property-tests test


# enable property test dependencies
enable-property-tests:
scripts/bigdecimal-property-tests enable


# print decimals with various formatting rules
run-formatting-example:
cargo run --example formatting-examples

3 changes: 3 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ fn main() {
let ac = autocfg::new();
ac.emit_rustc_version(1, 70);

// abs_diff
ac.emit_rustc_version(1, 60);

// slice::fill
ac.emit_rustc_version(1, 50);

Expand Down
15 changes: 10 additions & 5 deletions src/arithmetic/cbrt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@ pub(crate) fn impl_cbrt_uint_scale(

let (mut new_scale, remainder) = shifted_scale.div_rem(&3);

if remainder > 0 {
new_scale += 1;
exp_shift += (3 - remainder) as u64;
} else if remainder < 0 {
exp_shift += remainder.neg() as u64;
match remainder.cmp(&0) {
Ordering::Greater => {
new_scale += 1;
exp_shift += (3 - remainder) as u64;
}
Ordering::Less => {
exp_shift += remainder.neg() as u64;
}
Ordering::Equal => {
}
}

// clone-on-write copy of digits
Expand Down
14 changes: 14 additions & 0 deletions src/arithmetic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,20 @@ pub(crate) fn diff_usize(a: usize, b: usize) -> (Ordering, usize) {
}
}

/// Return absolute difference between two numbers
#[cfg(rustc_1_60)]
#[allow(clippy::incompatible_msrv)]
#[allow(dead_code)]
pub(crate) fn abs_diff(x: i64, y: i64) -> u64 {
x.abs_diff(y)
}

#[cfg(not(rustc_1_60))]
#[allow(dead_code)]
pub(crate) fn abs_diff(x: i64, y: i64) -> u64 {
(x as i128 - y as i128).to_u64().unwrap_or(0)
}


/// Add carry to given number, returning trimmed value and storing overflow back in carry
///
Expand Down
2 changes: 1 addition & 1 deletion src/arithmetic/sqrt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::*;

pub(crate) fn impl_sqrt(n: &BigUint, scale: i64, ctx: &Context) -> BigDecimal {
// Calculate the number of digits and the difference compared to the scale
let num_digits = count_decimal_digits_uint(&n);
let num_digits = count_decimal_digits_uint(n);
let scale_diff = BigInt::from(num_digits) - scale;

// Calculate the number of wanted digits and the exponent we need to raise the original value to
Expand Down
102 changes: 87 additions & 15 deletions src/impl_fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ fn dynamically_format_decimal(
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 {
// the upper and lower thresholds of the decimal,
// and precision was not requested
if f.precision().is_none() && leading_zero_threshold < leading_zero_count {
format_exponential(this, f, abs_int, "E")
} else if trailing_zero_threshold < trailing_zeros {
// non-scientific notation
Expand All @@ -119,6 +120,39 @@ fn dynamically_format_decimal(
}


pub(crate) struct FullScaleFormatter<'a>(pub BigDecimalRef<'a>);

impl fmt::Display for FullScaleFormatter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let n = self.0;
let non_negative = matches!(n.sign, Sign::Plus | Sign::NoSign);

let mut digits = n.digits.to_string();

if n.scale <= 0 {
digits.extend(stdlib::iter::repeat('0').take(n.scale.neg() as usize));
} else if n.scale < digits.len() as i64 {
digits.insert(digits.len() - n.scale as usize, '.');
} else {
let mut digit_vec = digits.into_bytes();

let dest_str_size = n.scale as usize + 2;
let digit_count = digit_vec.len();
let leading_char_idx = dest_str_size - digit_count;

digit_vec.resize(dest_str_size, b'0');
digit_vec.copy_within(0..digit_count, leading_char_idx);
fill_slice(&mut digit_vec[..digit_count.min(leading_char_idx)], b'0');

digit_vec[1] = b'.';
digits = String::from_utf8(digit_vec).unwrap();
}

f.pad_integral(non_negative, "", &digits)
}
}


fn format_full_scale(
this: BigDecimalRef,
f: &mut fmt::Formatter,
Expand Down Expand Up @@ -188,7 +222,7 @@ fn zero_right_pad_integer_ascii_digits(

// did not explicitly request precision, so we'll only
// implicitly right-pad if less than this threshold.
if matches!(target_scale, None) && integer_zero_count > 20 {
if target_scale.is_none() && integer_zero_count > 20 {
// no padding
return;
}
Expand Down Expand Up @@ -253,7 +287,6 @@ fn format_ascii_digits_with_integer_and_fraction(
digit_scale -= scale_diff as u64;
}
Some(zeros_to_add) => {
debug_assert_eq!(zeros_to_add, 1);
digits_ascii_be.resize(digits_ascii_be.len() + zeros_to_add, b'0');
digit_scale = 0;
}
Expand Down Expand Up @@ -862,6 +895,7 @@ mod test {
}

impl_case!(fmt_default: "{}" => "9999999");
impl_case!(fmt_d1: "{:.1}" => "9999999.0");
impl_case!(fmt_d8: "{:.8}" => "9999999.00000000");

impl_case!(fmt_e: "{:e}" => "9.999999e+6");
Expand Down Expand Up @@ -893,6 +927,21 @@ mod test {
impl_case!(fmt_010d3: "{:010.3}" => "019073.972");
}

mod dec_10950633712399d557 {
use super::*;

fn test_input() -> BigDecimal {
"10950633712399.557".parse().unwrap()
}

impl_case!(fmt_default: "{}" => "10950633712399.557");
impl_case!(fmt_d0: "{:.0}" => "10950633712400");
impl_case!(fmt_d1: "{:.1}" => "10950633712399.6");
impl_case!(fmt_d2: "{:.2}" => "10950633712399.56");
impl_case!(fmt_d3: "{:.3}" => "10950633712399.557");
impl_case!(fmt_d4: "{:.4}" => "10950633712399.5570");
}

mod dec_n90037659d6902 {
use super::*;

Expand Down Expand Up @@ -981,15 +1030,20 @@ mod test {
"491326e-12".parse().unwrap()
}

impl_case!(fmt_default: "{}" => "4.91326E-7");
impl_case!(fmt_d0: "{:.0}" => "5E-7");
impl_case!(fmt_d1: "{:.1}" => "4.9E-7");
impl_case!(fmt_d3: "{:.3}" => "4.913E-7");
impl_case!(fmt_d5: "{:.5}" => "4.91326E-7");
impl_case!(fmt_d6: "{:.6}" => "4.913260E-7");

impl_case!(fmt_d9: "{:.9}" => "4.913260000E-7");
impl_case!(fmt_d20: "{:.20}" => "4.91326000000000000000E-7");
impl_case!(fmt_default: "{}" => "4.91326E-7");
impl_case!(fmt_d0: "{:.0}" => "0");
impl_case!(fmt_d1: "{:.1}" => "0.0");
impl_case!(fmt_d3: "{:.3}" => "0.000");
impl_case!(fmt_d5: "{:.5}" => "0.00000");
impl_case!(fmt_d6: "{:.7}" => "0.0000005");
impl_case!(fmt_d9: "{:.9}" => "0.000000491");
impl_case!(fmt_d20: "{:.20}" => "0.00000049132600000000");

impl_case!(fmt_d0e: "{:.0E}" => "5E-7");
impl_case!(fmt_d1e: "{:.1E}" => "4.9E-7");
impl_case!(fmt_d3e: "{:.3E}" => "4.913E-7");
impl_case!(fmt_d5e: "{:.5E}" => "4.91326E-7");
impl_case!(fmt_d6e: "{:.6E}" => "4.913260E-7");
}

mod dec_0d00003102564500 {
Expand Down Expand Up @@ -1024,8 +1078,12 @@ mod test {
}

impl_case!(fmt_default: "{}" => "1E-10000");
impl_case!(fmt_d1: "{:.1}" => "1.0E-10000");
impl_case!(fmt_d4: "{:.4}" => "1.0000E-10000");
impl_case!(fmt_d: "{:.0}" => "0");
impl_case!(fmt_d1: "{:.1}" => "0.0");
impl_case!(fmt_d4: "{:.4}" => "0.0000");

impl_case!(fmt_d1E: "{:.1E}" => "1.0E-10000");
impl_case!(fmt_d4E: "{:.4E}" => "1.0000E-10000");
}

mod dec_1e100000 {
Expand Down Expand Up @@ -1320,6 +1378,8 @@ mod proptests {
use super::*;
use paste::paste;
use proptest::prelude::*;
use proptest::num::f64::NORMAL as NormalF64;


macro_rules! impl_parsing_test {
($t:ty) => {
Expand Down Expand Up @@ -1363,6 +1423,18 @@ mod proptests {
impl_parsing_test!(from-float f32);
impl_parsing_test!(from-float f64);

proptest! {
#![proptest_config(ProptestConfig::with_cases(32_000))]

#[test]
fn float_formatting(f in NormalF64, prec in 0..21usize) {
let d = BigDecimal::from_f64(f).unwrap();
let f_fmt = format!("{f:.prec$}");
let d_fmt = format!("{d:.prec$}").replace("+", "");
prop_assert_eq!(f_fmt, d_fmt);
}
}

proptest! {
#![proptest_config(ProptestConfig::with_cases(1000))]

Expand Down
Loading

0 comments on commit b60d353

Please sign in to comment.