Skip to content

Commit

Permalink
v0.4.3
Browse files Browse the repository at this point in the history
  • Loading branch information
akubera committed Mar 5, 2024
2 parents 25b9d65 + ea6eb2f commit f84ba98
Show file tree
Hide file tree
Showing 21 changed files with 2,032 additions and 780 deletions.
111 changes: 81 additions & 30 deletions .circleci/config.yml
@@ -1,20 +1,23 @@
version: 2.1
orbs:
# codecov: codecov/codecov@3.2.4
rust: circleci/rust@1.6.0
# codecov: codecov/codecov@3.3.0
rust: circleci/rust@1.6.1

jobs:
build-and-test:
parameters:
rust-version:
type: string
default: "1.69.0"
default: "1.75.0"
debian-version:
type: string
default: "buster"
default: "bookworm"
rust-features:
type: string
default: "--all-targets"
proptest-enable:
type: boolean
default: false
docker:
- image: rust:<< parameters.rust-version >>-<< parameters.debian-version >>
environment:
Expand All @@ -25,17 +28,26 @@ jobs:
- run:
name: Rust Version
command: rustc --version; cargo --version
- when:
condition: << parameters.proptest-enable >>
steps:
- run:
name: Enable Running Property Tests
command: scripts/bigdecimal-property-tests enable
- run:
name: Generate cargo.lock
command: cargo generate-lockfile
- restore_cache:
keys:
- bigdecimal-cargo-<< parameters.rust-version >>-{{ checksum "Cargo.toml" }}
- bigdecimal-cargo-<< parameters.rust-version >>-{{ checksum "Cargo.lock" }}
- bigdecimal-cargo-
- run:
name: Check
command: cargo check << parameters.rust-features >>
- save_cache:
paths:
- /usr/local/cargo
key: bigdecimal-cargo-<< parameters.rust-version >>-{{ checksum "Cargo.toml" }}
key: bigdecimal-cargo-<< parameters.rust-version >>-{{ checksum "Cargo.lock" }}
- run:
name: Build
command: cargo build << parameters.rust-features >>
Expand All @@ -49,7 +61,7 @@ jobs:
type: string
debian-version:
type: string
default: "bullseye"
default: "bookworm"
machine: true
steps:
- checkout
Expand All @@ -65,54 +77,93 @@ jobs:
sh -c 'cargo test -q --no-run && kcov-rust && upload-kcov-results-to-codecov'
- store_artifacts:
path: target/cov
# - store_test_results:
# path: target

lint-check:
docker:
- image: cimg/rust:1.69
- image: cimg/rust:1.75
steps:
- checkout
- run:
name: Generate cargo.lock
command: cargo generate-lockfile
- rust/build:
with_cache: false
# - rust/format
# - rust/clippy
- rust/clippy
- rust/test
- run:
name: Build examples
command: cargo build --examples

cargo-semver-check:
docker:
- image: "akubera/rust:stable"
steps:
- checkout
- run:
name: Tool Versions
command: >
rustc --version
&& cargo --version
&& cargo semver-checks --version
- run:
name: cargo semver-checks
command: cargo semver-checks --verbose
- run:
name: cargo semver-checks (no-std)
command: cargo semver-checks --verbose --only-explicit-features

workflows:
version: 2
cargo:build-and-test:
jobs:
- rust/lint-test-build:
name: "lint-test-build:stable"
release: true
version: "1.75"
pre-steps:
- checkout
- run:
command: cargo generate-lockfile
- rust/lint-test-build:
name: "lint-test-build:1.56"
release: true
version: "1.56"

- lint-check

- build-and-test:
name: build-and-test:MSRV
rust-version: "1.43.1"
debian-version: "buster"

- build-and-test:
matrix:
parameters:
rust-version:
- "1.43.1"
- "1.54.0"
name: build-and-test:MSRV:serde
rust-version: "1.43.1"
debian-version: "buster"
rust-features: "--all-targets --features='serde'"

- build-and-test:
name: build-and-test:latest
debian-version: "bullseye"
name: build-and-test:latest

- build-and-test:
matrix:
parameters:
rust-version:
- "1.43.1"
- "1.69.0"
rust-features:
- "--features='serde'"
- "--features='serde,string-only'"
name: build-and-test:latest:serde
rust-features: "--all-targets --features='serde'"
proptest-enable: true

- build-and-test:
name: build-and-test:no-default-features
rust-features: "--no-default-features"
name: build-and-test:no_std
rust-features: "--no-default-features"

- build-and-test:
name: build-and-test:serde+no_std
rust-features: "--no-default-features --features='serde'"

- cargo-semver-check:
requires:
- build-and-test:latest:serde

- upload-coverage:
rust-version: "1.69.0"
rust-version: "1.75.0"
requires:
- build-and-test:latest
- build-and-test:latest:serde
4 changes: 2 additions & 2 deletions Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bigdecimal"
version = "0.4.2"
version = "0.4.3"
authors = ["Andrew Kubera"]
description = "Arbitrary precision decimal numbers"
documentation = "https://docs.rs/bigdecimal"
Expand Down Expand Up @@ -28,7 +28,7 @@ serde = { version = "1.0", optional = true, default-features = false }

[dev-dependencies]
paste = "1"
serde_json = "<1.0.101"
serde_test = "<1.0.176"
siphasher = { version = "0.3.10", default-features = false }
# The following dev-dependencies are only required for benchmarking
# (use the `benchmark-bigdecimal` script to uncomment these and run benchmarks)
Expand Down
107 changes: 92 additions & 15 deletions README.md
Expand Up @@ -12,7 +12,17 @@



Arbitary-precision decimal numbers implemented in pure Rust.
Arbitrary-precision decimal numbers implemented in pure Rust.

## Community

Join the conversation on Zulip: https://bigdecimal-rs.zulipchat.com

Please share important stuff like use-cases, issues, benchmarks, and
naming-convention preferences.

This project is currently being re-written, so if performance or flexibility
is lacking, check again soon and it may be fixed.

## Usage

Expand Down Expand Up @@ -41,25 +51,97 @@ sqrt(2) = 1.41421356237309504880168872420969807856967187537694807317667973799073
```


### Compile-Time Configuration

You can set a few default parameters at _compile-time_ via environment variables:

| Environment Variable | Default |
|-------------------------------------------------|------------|
| `RUST_BIGDECIMAL_DEFAULT_PRECISION` | 100 |
| `RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE` | `HalfEven` |
| `RUST_BIGDECIMAL_EXPONENTIAL_FORMAT_THRESHOLD` | 5 |

These allow setting the default [Context] fields globally without incurring a runtime lookup,
or having to pass Context parameters through all calculations.
(If you want runtime control over these fields, you will have to pass Contexts to your functions.)
Examine [build.rs] for how those are converted to constants in the code (if interested).

[Context]: https://docs.rs/bigdecimal/latest/bigdecimal/struct.Context.html
[build.rs]: ./build.rs


#### Default precision

Default precision may be set at compile time with the environment variable `RUST_BIGDECIMAL_DEFAULT_PRECISION`.
The default value of this variable is 100.

This will be used as maximum precision for operations which may produce infinite digits (inverse, sqrt, ...).

Note that other operations, such as multiplication, will preserve all digits, so multiplying two 70 digit numbers
will result in one 140 digit number.
The user will have to manually trim the number of digits after calculations to reasonable amounts using the
`x.with_prec(30)` method.
Note that other operations, such as multiplication, will preserve all digits;
so multiplying two 70 digit numbers will result in one 140 digit number.
The user will have to manually trim the number of digits after calculations to
reasonable amounts using the `x.with_prec(30)` method.

A new set of methods with explicit precision and rounding modes is being worked
on, but even after those are introduced the default precision will have to be
used as the implicit value.

#### Rounding mode

The default Context uses this value for rounding.
Valid values are the variants of the [RoundingMode] enum.

Defaults to `HalfEven`.

[RoundingMode]: https://docs.rs/bigdecimal/latest/bigdecimal/rounding/enum.RoundingMode.html


#### Exponential Format Threshold

The maximum number of leading zeros after the decimal place before
the formatter uses exponential form (i.e. scientific notation).

A new set of methods with explicit precision and rounding modes is being worked on, but even after those
are introduced the default precision will have to be used as the implicit value.
There is currently no mechanism to change this during runtime.
If you know of a good solution for number formatting in Rust, please let me know!

## Improvements

Work is being done on this codebase again and there are many features
and improvements on the way.
#### Example Compile time configuration

Given the program:

```rust
fn main() {
let n = BigDecimal::from(700);
println!("1/{n} = {}", n.inverse());
}
```

Compiling with different environment variables prints different results

```
$ export BIG_DECIMAL_DEFAULT_PRECISION=8
$ cargo run
1/700 = 0.0014285714
$ export RUST_BIGDECIMAL_DEFAULT_PRECISION=5
$ cargo run
1/700 = 0.0014286
$ export RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE=Down
$ cargo run
1/700 = 0.0014285
$ export RUST_BIGDECIMAL_EXPONENTIAL_FORMAT_THRESHOLD=2
$ cargo run
1/700 = 1.4285E-3
```

> [!NOTE]
> These are **compile time** environment variables, and the BigDecimal
> library is not configurable at **runtime** via environment variable, or
> any kind of global variables, by default.
>
> This is for flexibility and performance.

## About
Expand All @@ -82,8 +164,3 @@ Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the
Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.


## Community

Join the conversation on Zulip: https://bigdecimal-rs.zulipchat.com
18 changes: 18 additions & 0 deletions build.rs
Expand Up @@ -16,6 +16,7 @@ fn main() {
let outdir: PathBuf = std::env::var_os("OUT_DIR").unwrap().into();
write_default_precision_file(&outdir);
write_default_rounding_mode(&outdir);
write_exponential_format_threshold_file(&outdir);
}


Expand Down Expand Up @@ -46,3 +47,20 @@ fn write_default_rounding_mode(outdir: &Path) {

std::fs::write(rust_file_path, rust_file_contents).unwrap();
}

/// Create write_default_rounding_mode.rs, containing definition of constant EXPONENTIAL_FORMAT_THRESHOLD loaded in src/impl_fmt.rs
fn write_exponential_format_threshold_file(outdir: &Path) {
let env_var = env::var("RUST_BIGDECIMAL_EXPONENTIAL_FORMAT_THRESHOLD").unwrap_or_else(|_| "5".to_owned());
println!("cargo:rerun-if-env-changed=RUST_BIGDECIMAL_EXPONENTIAL_FORMAT_THRESHOLD");

let rust_file_path = outdir.join("exponential_format_threshold.rs");

let value: u32 = env_var
.parse::<std::num::NonZeroU32>()
.expect("$RUST_BIGDECIMAL_EXPONENTIAL_FORMAT_THRESHOLD must be an integer > 0")
.into();

let rust_file_contents = format!("const EXPONENTIAL_FORMAT_THRESHOLD: i64 = {};", value);

std::fs::write(rust_file_path, rust_file_contents).unwrap();
}
2 changes: 1 addition & 1 deletion examples/floating-precision.rs
Expand Up @@ -4,7 +4,7 @@ use bigdecimal::BigDecimal;
use std::str::FromStr;

fn main() {
let input = std::env::args().skip(1).next().unwrap_or("0.7".to_string());
let input = std::env::args().nth(1).unwrap_or("0.7".to_string());
let decimal = BigDecimal::from_str(&input).expect("invalid decimal");
let floating = f32::from_str(&input).expect("invalid float");

Expand Down
4 changes: 2 additions & 2 deletions examples/simple-math.rs
Expand Up @@ -21,8 +21,8 @@ sum mut: 48.00000000000000
fn main() {
println!("Hello, Big Decimals!");
let input = "0.8";
let dec = BigDecimal::from_str(&input).unwrap();
let float = f32::from_str(&input).unwrap();
let dec = BigDecimal::from_str(input).unwrap();
let float = f32::from_str(input).unwrap();
println!("Input ({}) with 10 decimals: {} vs {})", input, dec, float);

let bd_square = dec.square();
Expand Down

0 comments on commit f84ba98

Please sign in to comment.