Skip to content

Commit 369dfae

Browse files
committed
v0.4.5
2 parents 609cd3d + 682343b commit 369dfae

File tree

6 files changed

+219
-30
lines changed

6 files changed

+219
-30
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
command: scripts/bigdecimal-property-tests enable
3737
- run:
3838
name: Generate cargo.lock
39-
command: cargo generate-lockfile
39+
command: if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi
4040
- restore_cache:
4141
keys:
4242
- bigdecimal-cargo-<< parameters.rust-version >>-{{ checksum "Cargo.lock" }}

Cargo.lock

+157
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bigdecimal"
3-
version = "0.4.4"
3+
version = "0.4.5"
44
authors = ["Andrew Kubera"]
55
description = "Arbitrary precision decimal numbers"
66
documentation = "https://docs.rs/bigdecimal"
@@ -21,9 +21,9 @@ bench = false
2121

2222
[dependencies]
2323
libm = "0.2.6"
24-
num-bigint = { version = ">=0.3.0,<0.4.5", default-features = false }
25-
num-integer = { version = "^0.1", default-features = false }
26-
num-traits = { version = ">0.2.0,<0.2.19", default-features = false }
24+
num-bigint = { version = "0.4", default-features = false }
25+
num-integer = { version = "0.1", default-features = false }
26+
num-traits = { version = "0.2", default-features = false }
2727
serde = { version = "1.0", optional = true, default-features = false }
2828
# Allow direct parsing of JSON floats, for full arbitrary precision
2929
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc", "arbitrary_precision"]}

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ use serde_json;
8787
#[derive(Debug,Serialize,Deserialize)]
8888
struct MyStruct {
8989
name: String,
90-
// this will be witten to json as string
90+
// this will be written to json as string
9191
value: BigDecimal,
92-
// this will be witten to json as number
92+
// this will be written to json as number
9393
#[serde(with = "bigdecimal::serde::json_num")]
9494
number: BigDecimal,
9595
}

src/impl_cmp.rs

+14
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,20 @@ mod test {
378378
impl_test!(case_1116xx459_759xx717e2: "1116386634271380982470843247639640260491505327092723527088459" < "759522625769651746138617259189939751893902453291243506584717" e 2);
379379
}
380380

381+
/// Test that large-magnitidue exponentials will not crash
382+
#[test]
383+
fn test_cmp_on_exp_boundaries() {
384+
let a = BigDecimal::new(1.into(), i64::MAX);
385+
let z = BigDecimal::new(1.into(), i64::MIN);
386+
assert_ne!(a, z);
387+
assert_ne!(z, a);
388+
389+
assert!(a < z);
390+
391+
assert_eq!(a, a);
392+
assert_eq!(z, z);
393+
}
394+
381395
mod ord {
382396
use super::*;
383397

src/impl_fmt.rs

+41-23
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,23 @@ fn dynamically_format_decimal(
8787

8888
// number of zeros between most significant digit and decimal point
8989
let leading_zero_count = this.scale
90-
.to_usize()
91-
.and_then(|scale| scale.checked_sub(abs_int.len()))
90+
.to_u64()
91+
.and_then(|scale| scale.checked_sub(abs_int.len() as u64))
9292
.unwrap_or(0);
9393

9494
// number of zeros between least significant digit and decimal point
9595
let trailing_zero_count = this.scale
9696
.checked_neg()
97-
.and_then(|d| d.to_usize());
97+
.and_then(|d| d.to_u64());
9898

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

104+
let leading_zero_threshold = leading_zero_threshold as u64;
105+
let trailing_zero_threshold = trailing_zero_threshold as u64;
106+
104107
// use exponential form if decimal point is outside
105108
// the upper and lower thresholds of the decimal
106109
if leading_zero_threshold < leading_zero_count {
@@ -128,14 +131,14 @@ fn format_full_scale(
128131
debug_assert_ne!(digits.len(), 0);
129132

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

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

152155
// add exp part to buffer (if not zero)
153156
if exp != 0 {
154-
write!(buf, "E{:+}", exp)?;
157+
write!(buf, "e{:+}", exp)?;
155158
}
156159

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

172-
let trailing_zero_count = exp.to_usize().unwrap();
175+
let trailing_zero_count = match exp.to_usize() {
176+
Some(n) => n,
177+
None => { return; }
178+
};
173179
let total_additional_zeros = trailing_zero_count.saturating_add(precision.unwrap_or(0));
174180
if total_additional_zeros > FMT_MAX_INTEGER_PADDING {
175181
return;
@@ -195,16 +201,20 @@ fn zero_right_pad_integer_ascii_digits(
195201
/// Fill zeros into utf-8 digits
196202
fn trim_ascii_digits(
197203
digits: &mut Vec<u8>,
198-
scale: usize,
199-
prec: usize,
204+
scale: u64,
205+
prec: u64,
200206
exp: &mut i128,
201207
sign: Sign,
202208
) {
203-
debug_assert!(scale < digits.len());
209+
debug_assert!(scale < digits.len() as u64);
204210
// there are both integer and fractional digits
205-
let integer_digit_count = digits.len() - scale;
211+
let integer_digit_count = (digits.len() as u64 - scale)
212+
.to_usize()
213+
.expect("Number of digits exceeds maximum usize");
206214

207215
if prec < scale {
216+
let prec = prec.to_usize()
217+
.expect("Precision exceeds maximum usize");
208218
apply_rounding_to_ascii_digits(
209219
digits, exp, integer_digit_count + prec, sign
210220
);
@@ -215,30 +225,34 @@ fn trim_ascii_digits(
215225
}
216226

217227
if scale < prec {
228+
let trailing_zero_count = (prec - scale)
229+
.to_usize()
230+
.expect("Too Big");
231+
218232
// precision required beyond scale
219-
digits.resize(digits.len() + (prec - scale), b'0');
233+
digits.resize(digits.len() + trailing_zero_count, b'0');
220234
}
221235
}
222236

223237

224238
fn shift_or_trim_fractional_digits(
225239
digits: &mut Vec<u8>,
226-
scale: usize,
227-
prec: usize,
240+
scale: u64,
241+
prec: u64,
228242
exp: &mut i128,
229243
sign: Sign,
230244
) {
231-
debug_assert!(scale >= digits.len());
245+
debug_assert!(scale >= digits.len() as u64);
232246
// there are no integer digits
233-
let leading_zeros = scale - digits.len();
247+
let leading_zeros = scale - digits.len() as u64;
234248

235249
match prec.checked_sub(leading_zeros) {
236250
None => {
237251
digits.clear();
238252
digits.push(b'0');
239253
if prec > 0 {
240254
digits.push(b'.');
241-
digits.resize(2 + prec, b'0');
255+
digits.resize(2 + prec as usize, b'0');
242256
}
243257
}
244258
Some(0) => {
@@ -251,11 +265,15 @@ fn shift_or_trim_fractional_digits(
251265
if leading_zeros != 0 {
252266
digits.push(b'0');
253267
digits.push(b'.');
254-
digits.resize(1 + leading_zeros, b'0');
268+
digits.resize(1 + leading_zeros as usize, b'0');
255269
}
256270
digits.push(rounded_value + b'0');
257271
}
258272
Some(digit_prec) => {
273+
let digit_prec = digit_prec as usize;
274+
let leading_zeros = leading_zeros
275+
.to_usize()
276+
.expect("Number of leading zeros exceeds max usize");
259277
let trailing_zeros = digit_prec.saturating_sub(digits.len());
260278
if digit_prec < digits.len() {
261279
apply_rounding_to_ascii_digits(digits, exp, digit_prec, sign);
@@ -863,8 +881,8 @@ mod test {
863881
}
864882

865883
impl_case!(fmt_default: "{}" => "1e+100000");
866-
impl_case!(fmt_d1: "{:.1}" => "1E+100000");
867-
impl_case!(fmt_d4: "{:.4}" => "1E+100000");
884+
impl_case!(fmt_d1: "{:.1}" => "1e+100000");
885+
impl_case!(fmt_d4: "{:.4}" => "1e+100000");
868886
}
869887

870888

@@ -905,7 +923,7 @@ mod test {
905923
}
906924

907925
impl_case!(fmt_default: "{}" => "13400476439814628800e+2502");
908-
impl_case!(fmt_d1: "{:.1}" => "13400476439814628800E+2502");
926+
impl_case!(fmt_d1: "{:.1}" => "13400476439814628800e+2502");
909927
}
910928
}
911929

0 commit comments

Comments
 (0)