bigdecimal-0.4.7/.cargo_vcs_info.json0000644000000001360000000000100131240ustar { "git": { "sha1": "b60d353c2d77a41986ade403748b0be2821bc9ba" }, "path_in_vcs": "" }bigdecimal-0.4.7/.circleci/config.yml000064400000000000000000000115141046102023000155410ustar 00000000000000version: 2.1 orbs: # codecov: codecov/codecov@3.3.0 rust: circleci/rust@1.6.1 jobs: build-and-test: parameters: rust-version: type: string default: "1.75.0" debian-version: type: string 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: RUSTFLAGS: '-D warnings' CARGO_NET_GIT_FETCH_WITH_CLI: "true" steps: - checkout - 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: if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi - restore_cache: keys: - 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.lock" }} - run: name: Build command: cargo build << parameters.rust-features >> - run: name: Test command: cargo test << parameters.rust-features >> upload-coverage: parameters: rust-version: type: string debian-version: type: string default: "bookworm" machine: true steps: - checkout - run: name: Generate Coverage command: > docker run --security-opt seccomp=unconfined -v "${PWD}:/home" -e CI=true $(bash <(curl -s https://codecov.io/env)) akubera/rust-codecov:<< parameters.rust-version >>-<< parameters.debian-version >> sh -c 'cargo test -q --no-run && kcov-rust && upload-kcov-results-to-codecov' - store_artifacts: path: target/cov lint-check: docker: - 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/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" pre-steps: - checkout - run: command: cargo generate-lockfile - lint-check - build-and-test: name: build-and-test:MSRV rust-version: "1.43.1" debian-version: "buster" - build-and-test: 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 - build-and-test: name: build-and-test:latest:serde rust-features: "--all-targets --features='serde'" proptest-enable: true - build-and-test: 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'" - build-and-test: name: build-and-test:serde_json rust-features: "--features='serde-json'" - build-and-test: name: build-and-test:serde_json+no_std rust-features: "--features='serde-json' --no-default-features" - cargo-semver-check: requires: - build-and-test:latest:serde - upload-coverage: rust-version: "1.75.0" requires: - build-and-test:latest:serde bigdecimal-0.4.7/.clippy.toml000064400000000000000000000000201046102023000141570ustar 00000000000000msrv = "1.43.0" bigdecimal-0.4.7/.editorconfig000064400000000000000000000003461046102023000143740ustar 00000000000000# https://EditorConfig.org # top-most EditorConfig file root = true [*] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yaml] indent_size = 2 bigdecimal-0.4.7/.gitignore000064400000000000000000000002261046102023000137040ustar 00000000000000Cargo.lock target .vscode/ .*cache/ benches/test-data/ builds/ *.tgz *.tar *.bak *.backup *venv*/ .python-version *.ipynb proptest-regressions bigdecimal-0.4.7/.gitlab-ci.yml000075500000000000000000000214451046102023000143610ustar 00000000000000# Gitlab CI Config # ================ # # Defines jobs for checking/building/testing various # configurations of the BigDecimal crate. # # Always tests against stable and MinimumSupportedRustVersion (1.43) # Other random versions are available, requiring manual action to run. # # Most jobs are run upon custom rust containers I try to keep updated: # https://gitlab.com/akubera/containers/-/blob/master/akubera-rust/Dockerfile.in # https://hub.docker.com/r/akubera/rust/tags # # A coverage report is done via one of my rust-kcov or rust-grcov containers: # https://gitlab.com/akubera/containers/-/blob/master/rust-kcov/Dockerfile.in # https://gitlab.com/akubera/containers/-/blob/master/rust-grcov/Dockerfile.in # # A cargo:benchmark job is available, which saves a criterion report to gitlab, # manualy activated and of dubious reliability without control of target machine. # #----------------------------# # Yaml "Template" Objects # #----------------------------# .script: cargo-check: &script-cargo-check before_script: - rustc --version && cargo --version script: - cargo check --all-targets .script-cargo-build: &script-cargo-build before_script: - rustc --version && cargo --version script: - cargo build --all-targets .script-cargo-test: &script-cargo-test before_script: - rustc --version && cargo --version script: - cargo test --verbose .cache-cargo-registry: &cache-cargo-registry key: cargo-registry policy: pull paths: - .cargo/registry/index - .cargo/registry/cache - target/debug/deps .cache-cargo-build: &cache-cargo-build key: rustbuild-${RUST_CACHE_KEY} policy: pull-push paths: - target/debug/deps - target/debug/build .cache-cargo-semver-check: &cache-cargo-semver-check key: cargo-semver-checks policy: pull-push paths: - target/semver-checks/cache .rules.never-without-branch: rules: &rules-never-without-branch - if: '$CI_COMMIT_BRANCH == null' when: never .rules:always-master-otherwise-manual: rules: &rules-always-master-otherwise-manual - if: '$CI_COMMIT_BRANCH == "master"' when: always - if: '$CI_COMMIT_BRANCH == null' when: never - when: manual #---------------------# # Global Settings # #---------------------# cache: - <<: *cache-cargo-registry - <<: *cache-cargo-build variables: # store cargo registry in the project directory (for caching) CARGO_HOME: ${CI_PROJECT_DIR}/.cargo # this fixes an error when updating cargo registry CARGO_NET_GIT_FETCH_WITH_CLI: 'true' stages: - check - build - test - deploy #---------------------# # CI Jobs # #---------------------# cargo:check: stage: check image: akubera/rust:stable cache: - <<: *cache-cargo-registry policy: pull-push - <<: *cache-cargo-build variables: RUST_CACHE_KEY: "stable" <<: *script-cargo-check script: # enable property tests for the stable 'pipeline' - scripts/bigdecimal-property-tests run cargo check --all-targets cargo:clippy: stage: check image: "akubera/rust:stable" needs: - cargo:check allow_failure: true variables: RUST_CACHE_KEY: "stable" before_script: - rustc --version && cargo --version && cargo clippy --version script: - cargo clippy -- -Dclippy::{dbg_macro,todo} cargo:semver-checks: stage: check image: "akubera/rust:stable" needs: - cargo:check allow_failure: true cache: - <<: *cache-cargo-registry - <<: *cache-cargo-semver-check before_script: - rustc --version && cargo --version && cargo semver-checks --version script: - cargo semver-checks cargo:build-stable: stage: build image: akubera/rust:stable needs: - cargo:check variables: RUST_CACHE_KEY: "stable" <<: *script-cargo-build script: # enable property tests for the stable 'pipeline' - scripts/bigdecimal-property-tests run cargo build --all-targets cargo:test-stable: stage: test image: akubera/rust:stable needs: - "cargo:build-stable" variables: RUST_CACHE_KEY: "stable" <<: *script-cargo-test script: # enable property tests for the stable 'pipeline' - scripts/bigdecimal-property-tests run cargo test cargo:build:no-std: stage: build image: akubera/rust:stable needs: - cargo:check variables: RUST_CACHE_KEY: "stable+no_std" <<: *script-cargo-build script: - cargo build --no-default-features --lib cargo:test:no-std: stage: test image: akubera/rust:stable needs: - "cargo:build:no-std" variables: RUST_CACHE_KEY: "stable+no_std" <<: *script-cargo-test script: - cargo test --no-default-features --lib cargo:build:serde: stage: build image: akubera/rust:stable needs: - cargo:check variables: RUST_CACHE_KEY: "stable+serde" <<: *script-cargo-build script: - cargo build --features=serde --all-targets cargo:test:serde: stage: test image: akubera/rust:stable needs: - "cargo:build:serde" variables: RUST_CACHE_KEY: "stable+serde" <<: *script-cargo-test script: - cargo test --features=serde --all-targets - cargo test --features=serde --no-default-features cargo:build:serde-json: stage: build image: akubera/rust:stable needs: - cargo:check variables: RUST_CACHE_KEY: "stable+serde-json" <<: *script-cargo-build script: - cargo build --features=serde-json --all-targets cargo:test:serde-json: stage: test image: akubera/rust:stable needs: - "cargo:build:serde-json" variables: RUST_CACHE_KEY: "stable+serde-json" <<: *script-cargo-test script: - cargo test --features=serde-json --all-targets cargo:build-nightly: stage: build image: rustlang/rust:nightly allow_failure: true variables: RUST_CACHE_KEY: "nightly" <<: *script-cargo-build cargo:test-nightly: stage: test image: rustlang/rust:nightly needs: - cargo:build-nightly allow_failure: true variables: RUST_CACHE_KEY: "nightly" <<: *script-cargo-test cargo:check-1.43: stage: check image: "akubera/rust-kcov:1.43.1-buster" variables: RUST_CACHE_KEY: "1.43" <<: *script-cargo-check cargo:build-1.43: stage: build image: "akubera/rust-kcov:1.43.1-buster" needs: - "cargo:check-1.43" variables: RUST_CACHE_KEY: "1.43" <<: *script-cargo-build cargo:test-1.43: stage: test needs: - "cargo:build-1.43" image: "akubera/rust-kcov:1.43.1-buster" variables: RUST_CACHE_KEY: "1.43" <<: *script-cargo-test cargo:test-1.43:serde: stage: test needs: - "cargo:build-1.43" image: "akubera/rust-kcov:1.43.1-buster" variables: RUST_CACHE_KEY: "1.43-serde" <<: *script-cargo-test script: - cargo test --features=serde --all-targets - cargo test --features=serde --no-default-features cargo:check-1.70: stage: check image: "akubera/rust-grcov:1.70.0-bullseye" variables: RUST_CACHE_KEY: "1.70" <<: *script-cargo-check cargo:build-1.70: stage: build image: "akubera/rust-grcov:1.70.0-bullseye" needs: - "cargo:check-1.70" variables: RUST_CACHE_KEY: "1.70" <<: *script-cargo-build cargo:test-1.70: stage: test needs: - "cargo:build-1.70" image: "akubera/rust-grcov:1.70.0-bullseye" variables: RUST_CACHE_KEY: "1.70" <<: *script-cargo-test coverage-test: stage: test needs: - "cargo:test-1.70" image: "akubera/rust-grcov:1.70.0-bullseye" variables: RUST_CACHE_KEY: "1.70" CARGO_NET_GIT_FETCH_WITH_CLI: 'true' LLVM_PROFILE_FILE: "target/coverage/%p-%m.profraw" RUSTFLAGS: "-Cinstrument-coverage -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off " # RUSTDOCFLAGS: "-Cpanic=abort" CARGO_INCREMENTAL: "0" coverage: '/Code Coverage: \d+\.\d+/' before_script: - rustc --version && cargo --version script: - cargo test - ls -l target/coverage - grcov target/coverage --binary-path target/debug -s . --keep-only 'src/*' -tcobertura -o cobertura.xml - > grep -m1 -o 'line-rate="[^"]*' cobertura.xml | sed 's/[^0-9.]*//' | awk '{ print "Code Coverage: " $0 * 100 }' artifacts: reports: coverage_report: coverage_format: cobertura path: cobertura.xml cargo:benchmark: stage: test needs: - "cargo:test-1.70" image: "akubera/bigdecimal-benchmark-base:1.70.0-bullseye" when: manual allow_failure: true # No cache as benchmark-image stores registry in different location cache: [] variables: RUST_CACHE_KEY: "1.70" CARGO_HOME: /usr/local/cargo BENCHMARK_EXTRAS: "1" script: - scripts/benchmark-bigdecimal artifacts: paths: - target/criterion - "*.html" cargo-publish: stage: deploy image: "rust:latest" when: manual only: - master allow_failure: true cache: [] # disable downloading artifacts dependencies: [] variables: CARGO_HOME: /usr/local/cargo script: - cargo publish bigdecimal-0.4.7/.rustfmt.toml000064400000000000000000000002511046102023000143710ustar 00000000000000disable_all_formatting = true use_small_heuristics = "Off" reorder_modules = false reorder_imports = false # imports_layout = "HorizontalVertical" # generics = "Visual" bigdecimal-0.4.7/COPYRIGHT000064400000000000000000000004101046102023000132020ustar 00000000000000The BigDecimal project is licensed under the Apache License, Version 2.0 or the MIT license at your option. All files in the project carrying such notice may not be copied, modified, or distributed, except according to these terms. bigdecimal-0.4.7/Cargo.lock0000644000000076330000000000100111100ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bigdecimal" version = "0.4.7" dependencies = [ "autocfg", "libm", "num-bigint", "num-integer", "num-traits", "paste", "serde", "serde_json", "serde_test", "siphasher", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "num-bigint" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-traits" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "proc-macro2" version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_test" version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29baf0f77ca9ad9c6ed46e1b408b5e0f30b5184bcd66884e7f6d36bd7a65a8a4" dependencies = [ "serde", ] [[package]] name = "siphasher" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "syn" version = "2.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2415488199887523e74fd9a5f7be804dfd42d868ae0eca382e3917094d210e" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" bigdecimal-0.4.7/Cargo.toml0000644000000045320000000000100111260ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "bigdecimal" version = "0.4.7" authors = ["Andrew Kubera"] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Arbitrary precision decimal numbers" homepage = "https://github.com/akubera/bigdecimal-rs" documentation = "https://docs.rs/bigdecimal" readme = "README.md" keywords = [ "numerics", "bignum", "decimal", "arbitrary-precision", ] categories = [ "mathematics", "science", "no-std", ] license = "MIT/Apache-2.0" repository = "https://github.com/akubera/bigdecimal-rs" [lib] name = "bigdecimal" path = "src/lib.rs" bench = false [[example]] name = "floating-precision" path = "examples/floating-precision.rs" [[example]] name = "formatting-examples" path = "examples/formatting-examples.rs" [[example]] name = "repeated_squares" path = "examples/repeated_squares.rs" [[example]] name = "simple-math" path = "examples/simple-math.rs" [dependencies.libm] version = "0.2.6" [dependencies.num-bigint] version = "0.4" default-features = false [dependencies.num-integer] version = "0.1" default-features = false [dependencies.num-traits] version = "0.2" default-features = false [dependencies.serde] version = "1.0" optional = true default-features = false [dependencies.serde_json] version = "1.0" features = [ "alloc", "arbitrary_precision", ] optional = true default-features = false [dev-dependencies.paste] version = "1" [dev-dependencies.serde_test] version = "<1.0.176" [dev-dependencies.siphasher] version = "0.3.10" default-features = false [build-dependencies.autocfg] version = "1" [features] default = ["std"] serde-json = [ "serde/derive", "serde_json", ] std = [ "num-bigint/std", "num-integer/std", "num-traits/std", ] string-only = [] [lints.rust.unexpected_cfgs] level = "allow" priority = 0 check-cfg = ["cfg(no_track_caller)"] bigdecimal-0.4.7/Cargo.toml.orig000064400000000000000000000035111046102023000146030ustar 00000000000000[package] name = "bigdecimal" version = "0.4.7" authors = ["Andrew Kubera"] description = "Arbitrary precision decimal numbers" documentation = "https://docs.rs/bigdecimal" homepage = "https://github.com/akubera/bigdecimal-rs" repository = "https://github.com/akubera/bigdecimal-rs" keywords = [ "numerics", "bignum", "decimal", "arbitrary-precision", ] categories = [ "mathematics", "science", "no-std" ] license = "MIT/Apache-2.0" autobenches = false edition = "2015" [lib] bench = false [dependencies] libm = "0.2.6" 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"]} [dev-dependencies] paste = "1" 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) # BENCH: criterion = { version = "0.4", features = [ "html_reports" ] } # BENCH: oorandom = { version = "11.1.3" } # BENCH: lazy_static = { version = "1" } # Only required for property testing - incompatible with older versions of rust # PROPERTY-TESTS: proptest = "1" [build-dependencies] autocfg = "1" [features] default = ["std"] serde-json = ["serde/derive", "serde_json"] string-only = [] std = ["num-bigint/std", "num-integer/std", "num-traits/std"] # BENCH: [[bench]] # BENCH: name = "arithmetic" # BENCH: harness = false [lints.rust] unexpected_cfgs = { level = "allow", check-cfg = ['cfg(no_track_caller)'] } bigdecimal-0.4.7/Justfile000064400000000000000000000021161046102023000134240ustar 00000000000000 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 bigdecimal-0.4.7/LICENSE-APACHE000064400000000000000000000251441046102023000136460ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2023 The BigDecimal-rs Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. bigdecimal-0.4.7/LICENSE-MIT000064400000000000000000000020631046102023000133510ustar 00000000000000Copyright (c) 2023 The BigDecimal-rs Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bigdecimal-0.4.7/README.md000064400000000000000000000255771046102023000132130ustar 00000000000000# bigdecimal-rs [![crate](https://img.shields.io/crates/v/bigdecimal.svg)](https://crates.io/crates/bigdecimal) [![Documentation](https://docs.rs/bigdecimal/badge.svg)](https://docs.rs/bigdecimal) [![minimum rustc 1.43](https://img.shields.io/badge/rustc-1.43+-red.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![codecov](https://codecov.io/gh/akubera/bigdecimal-rs/branch/feature/circleci/graph/badge.svg?token=YTwyxrxJ3S)](https://codecov.io/gh/akubera/bigdecimal-rs) [![build status - master](https://gitlab.com/akubera/bigdecimal-rs/badges/master/pipeline.svg?ignore_skipped=true&key_text=status:master&key_width=96)](https://gitlab.com/akubera/bigdecimal-rs/-/pipelines) [![build status - trunk](https://gitlab.com/akubera/bigdecimal-rs/badges/trunk/pipeline.svg?ignore_skipped=true&key_text=status:trunk&key_width=96)](https://gitlab.com/akubera/bigdecimal-rs/-/pipelines) 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 Add bigdecimal as a dependency to your `Cargo.toml` file: ```toml [dependencies] bigdecimal = "0.4" ``` Import and use the `BigDecimal` struct to solve your problems: ```rust use bigdecimal::BigDecimal; fn main() { let two = BigDecimal::from(2); println!("sqrt(2) = {}", two.sqrt().unwrap()); } ``` this code will print ``` sqrt(2) = 1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573 ``` ### Serialization If you are passing BigDecimals between systems, be sure to use a serialization format which explicitly supports decimal numbers and does not require transformations to floating-point binary numbers, or there will be information loss. Text formats like JSON should work ok as long as the receiver will also parse numbers as decimals so complete precision is kept accurate. Typically JSON-parsing implementations do not do this by default, and need special configuration. Binary formats like msgpack may expect/require representing numbers as 64-bit IEEE-754 floating-point, and will likely lose precision by default unless you explicitly format the decimal as a string, bytes, or some custom structure. By default, this will serialize the decimal _as a string_. To use `serde_json` with this crate it is recommended to enable the `serde-json` feature (note `serde -dash- json` , not `serde_json`) and that will add support for serializing & deserializing values to BigDecimal. By default it will parse numbers and strings using normal conventions. If you want to serialize to a number, rather than a string, you can use the `serde(with)` annotation as shown in the following example: ```toml [dependencies] bigdecimal = { version = "0.4", features = [ "serde-json" ] } # '-' not '_' ``` ```rust use bigdecimal::BigDecimal; use serde::*; use serde_json; #[derive(Debug,Serialize,Deserialize)] struct MyStruct { name: String, // this will be written to json as string value: BigDecimal, // this will be written to json as number #[serde(with = "bigdecimal::serde::json_num")] number: BigDecimal, } fn main() { let json_src = r#" { "name": "foo", "value": 1234567e-3, "number": 3.14159 } "#; let my_struct: MyStruct = serde_json::from_str(&json_src).unwrap(); dbg!(my_struct); // MyStruct { name: "foo", value: BigDecimal("1234.567"), BigDecimal("3.1459") } println!("{}", serde_json::to_string(&my_struct)); // {"name":"foo","value":"1234.567","number":3.1459} } ``` If you have suggestions for improving serialization, please bring them to the Zulip chat. ### Formatting Until a more sophisticated formatting solution is implemented (currently work in progress), we are restricted to Rust's `fmt::Display` formatting options. This is how this crate formats BigDecimals: - `{}` - Default Display - Format as "human readable" number - "Small" fractional numbers (close to zero) are printed in scientific notation - Number is considered "small" by number of leading zeros exceeding a threshold - Configurable by the compile-time environment variable: `RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD` - Default 5 - Example: `1.23e-3` will print as `0.00123` but `1.23e-10` will be `1.23E-10` - Trailing zeros will be added to "small" integers, avoiding scientific notation - May appear to have more precision than they do - Example: decimal `1e1` would be rendered as `10` - The threshold for "small" is configured by compile-time environment variable: `RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD` - Default 15 - `1e15` => `1000000000000000` - Large integers (e.g. `1e50000000`) will print in scientific notation, not a 1 followed by fifty million zeros - All other numbers are printed in standard decimal notation - `{:.}` - Display with precision - Format number with exactly `PREC` digits after the decimal place - Numbers with fractional components will be rounded at precision point, or have zeros padded to precision point - Integers will have zeros padded to the precision point - To prevent unreasonably sized output, a threshold limits the number of padded zeros - Greater than the default case, since specific precision was requested - Configurable by the compile-time environment variable: `RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING` - Default 1000 - If digits exceed this threshold, they are printed without decimal-point, suffixed with scale of the big decimal - `{:e}` / `{:E}` - Exponential format - Formats in scientific notation with either `e` or `E` as exponent delimiter - Precision is kept exactly - `{:.e}` - formats in scientific notation, keeping number - Number is rounded / zero padded until - `{:?}` - Debug - Shows internal representation of BigDecimal - `123.456` => `BigDecimal(sign=Plus, scale=3, digits=[123456])` - `-1e10000` => `BigDecimal(sign=Minus, scale=-10000, digits=[1])` - `{:#?}` - Alternate Debug (used by `dbg!()`) - Shows simple int+exponent string representation of BigDecimal - `123.456` => `BigDecimal("123456e-3")` - `-1e10000` => `BigDecimal("-1e10000")` There is a [formatting-example](examples/formatting-example.rs) script in the `examples/` directory that demonstrates the formatting options and comparison with Rust's standard floating point Display. It is recommended you include unit tests in your code to guarantee that future versions of BigDecimal continue to format numbers the way you expect. The rules above are not likely to change, but they are probably the only part of this library that are relatively subjective, and could change behavior without indication from the compiler. Also, check for changes to the configuration environment variables, those may change name until 1.0. ### 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_FMT_EXPONENTIAL_LOWER_THRESHOLD` | 5 | | `RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD` | 15 | | `RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING` | 1000 | 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. 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). 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! #### 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 This repository contains code originally meant for a bigdecimal module in the popular [num](https://crates.io/crates/num) crate, but was not merged due to uncertainty of what the best design for such a crate should be. ## License This code is dual-licensed under the permissive [MIT](https://opensource.org/licenses/MIT) & [Apache 2.0](https://opensource.org/licenses/Apache-2.0) licenses. ### Contribution 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. bigdecimal-0.4.7/benches/arithmetic.rs000064400000000000000000000224261046102023000160300ustar 00000000000000//! Benchmarks for arithmetic operation extern crate criterion; extern crate bigdecimal; extern crate oorandom; extern crate lazy_static; use lazy_static::lazy_static; use std::fs::File; use std::time::Duration; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use bigdecimal::BigDecimal; #[macro_use] mod common; use common::*; criterion_main!( arithmetic, arithmetic_bulk, ); criterion_group!( name = arithmetic_bulk; config = Criterion::default() .measurement_time(Duration::from_secs(7)) .sample_size(700); targets = criterion_benchmark, datafile_1f633481a742923ab65855c90157bbf7::addition_bulk, ); criterion_group!( name = arithmetic; config = Criterion::default() .sample_size(300); targets = datafile_9a08ddaa6ce6693cdd7b8a524e088bd0::arithmetic, datafile_1f633481a742923ab65855c90157bbf7::addition, ); fn make_random_pairs(decs: &[BigDecimal], seed: u64) -> Vec<(&BigDecimal, &BigDecimal)> { let mut cartesian_pairs = decs .iter() .enumerate() .flat_map(|(i, x)| { decs.iter().skip(i+1).map(move |y| (x, y)) }).collect::>(); // random number generator from random seed let mut rng = oorandom::Rand32::new(seed); for i in (1..cartesian_pairs.len()).rev() { let j = rng.rand_u32() as usize % i; cartesian_pairs.swap(i, j); } cartesian_pairs } fn bench_addition_pairwise(name: &str, c: &mut Criterion, decs: &[BigDecimal]) { let x_iter = decs.iter().step_by(2); let y_iter = decs.iter().skip(1).step_by(2); let pair_iter = std::iter::zip(x_iter, y_iter); c.bench_function( name, |b| b.iter_batched( || { pair_iter.clone() }, |pairs| { for (x, y) in pairs { black_box(x + y); } }, criterion::BatchSize::SmallInput)); } fn bench_addition_pairs( name: &str, c: &mut Criterion, dec_pairs: &[(&BigDecimal, &BigDecimal)], ) { let pair_iter = dec_pairs.iter().cycle(); let mut iter_count = 0; c.bench_function( name, |b| b.iter_batched( || { iter_count += 1; pair_iter.clone().skip(iter_count).next().unwrap() }, |(x, y)| { black_box(*x + *y); }, criterion::BatchSize::SmallInput)); } fn bench_addition_pairs_bulk( name: &str, c: &mut Criterion, chunk_size: usize, dec_pairs: &[(&BigDecimal, &BigDecimal)], ) { let pair_iter = dec_pairs.iter(); let mut iter_count = 0; c.bench_function( name, |b| b.iter_batched( || { let skip_by = chunk_size * iter_count; iter_count += 1; pair_iter.clone().skip(skip_by).take(chunk_size) }, |pairs| { for (x, y) in pairs { black_box(*x + *y); } }, criterion::BatchSize::SmallInput)); } fn bench_multiplication_pairs( name: &str, c: &mut Criterion, dec_pairs: &[(&BigDecimal, &BigDecimal)], ) { let pair_iter = dec_pairs.iter().cycle(); let mut iter_count = 0; c.bench_function( name, |b| b.iter_batched( || { iter_count += 1; pair_iter.clone().skip(iter_count).next().unwrap() }, |(x, y)| { black_box(*x * *y); }, criterion::BatchSize::SmallInput)); } fn bench_inverse( name: &str, c: &mut Criterion, decs: &[BigDecimal], ) { let mut idx = 0; c.bench_function( name, |b| b.iter_batched( || { idx += 1; if idx == decs.len() { idx = 0; } &decs[idx] }, |x| { black_box(x.inverse()); }, criterion::BatchSize::SmallInput)); } mod datafile_1f633481a742923ab65855c90157bbf7 { use super::*; fn get_bigdecimals() -> Vec { let file = open_benchmark_data_file!("random-bigdecimals-1f633481a742923ab65855c90157bbf7.txt"); super::read_bigdecimal_file(file) } pub fn addition(c: &mut Criterion) { let decs = get_bigdecimals(); let cartesian_pairs = make_random_pairs(&decs, 7238269155957952517_u64); bench_addition_pairwise( "addition-pairwise-1f633481a742923ab65855c90157bbf7", c, &decs ); bench_addition_pairs( "addition-random-1f633481a742923ab65855c90157bbf7", c, &cartesian_pairs ); } pub fn addition_bulk(c: &mut Criterion) { let decs = get_bigdecimals(); let cartesian_pairs = make_random_pairs(&decs, 7238269155957952517_u64); bench_addition_pairs_bulk( "addition-random-1f633481a742923ab65855c90157bbf7-100", c, 100, &cartesian_pairs ); } } mod datafile_9a08ddaa6ce6693cdd7b8a524e088bd0 { use super::*; const SRC: &'static str = include_benchmark_data_file!("random-bigdecimals-9a08ddaa6ce6693cdd7b8a524e088bd0.txt"); lazy_static! { static ref BIG_DECIMALS: Vec = super::collect_bigdecimals(SRC); } fn get_bigdecimals<'a>() -> &'a [BigDecimal] { BIG_DECIMALS.as_slice() } pub fn arithmetic(c: &mut Criterion) { let decs = get_bigdecimals(); let cartesian_pairs = make_random_pairs(&decs, 7238269155957952517_u64); bench_addition_pairwise( "addition-pairwise-9a08ddaa6ce6693cdd7b8a524e088bd0", c, &decs ); bench_addition_pairs( "addition-random-9a08ddaa6ce6693cdd7b8a524e088bd0", c, &cartesian_pairs ); bench_multiplication_pairs( "multiplication-random-9a08ddaa6ce6693cdd7b8a524e088bd0", c, &cartesian_pairs ); bench_inverse( "inverse-9a08ddaa6ce6693cdd7b8a524e088bd0", c, &decs ); } } pub fn criterion_benchmark(c: &mut Criterion) { let src_a = include_benchmark_data_file!("random-bigdecimals-a329e61834832d89593b29f12510bdc8.txt"); let src_b = include_benchmark_data_file!("random-bigdecimals-4be58192272b15fc67573b39910837d0.txt"); let decs_a = collect_bigdecimals(src_a); let decs_b = collect_bigdecimals(src_b); assert_ne!(decs_a.len(), 0); assert_ne!(decs_b.len(), 0); let mut decimal_values = Vec::with_capacity(decs_a.len() + decs_b.len()); for dec in decs_a.iter().chain(decs_b.iter()) { decimal_values.push(dec) } let mut decimal_pairs = Vec::with_capacity(decs_a.len() * decs_b.len()); for a in decs_a.iter() { for b in decs_b.iter() { decimal_pairs.push((a, b)); } } let mut random_decimal = RandomIterator::new(&decimal_values); // let mut random_decimal = decimal_values.iter().cycle(); let mut random_pairs = RandomIterator::new(&decimal_pairs); // let mut random_pairs = decimal_pairs.iter().cycle().map(|d| *d); c.bench_function( "addition", |b| b.iter_batched( || { random_pairs.next() }, |(a, b)| { black_box(a + b); }, criterion::BatchSize::SmallInput)); c.bench_function( "subtraction", |b| b.iter_batched( || { random_pairs.next() }, |(a, b)| { black_box(a - b); }, criterion::BatchSize::SmallInput)); c.bench_function( "multiplication", |b| b.iter_batched( || { random_pairs.next() }, |(a, b)| { black_box(a * b); }, criterion::BatchSize::SmallInput)); c.bench_function( "division", |b| b.iter_batched( || { random_pairs.next() }, |(a, b)| { black_box(a / b); }, criterion::BatchSize::SmallInput)); c.bench_function( "square", |b| b.iter_batched( || { random_decimal.next() }, |a| { black_box(a.square()); }, criterion::BatchSize::SmallInput)); c.bench_function( "cube", |b| b.iter_batched( || { random_decimal.next() }, |a| { black_box(a.cube()); }, criterion::BatchSize::SmallInput)); let mut sqrt_group = c.benchmark_group("sqrt"); for prec in [10, 100, 1000, 10000] { sqrt_group.bench_function( format!("sqrt_prec_{prec}"), |b| b.iter_batched( || { (bigdecimal::Context::default().with_precision(std::num::NonZeroU64::new(prec).unwrap()), random_decimal.next()) }, |(ctx, a)| { black_box(a.sqrt_with_context(&ctx)); }, criterion::BatchSize::SmallInput)); } } bigdecimal-0.4.7/benches/common.rs000064400000000000000000000037001046102023000151610ustar 00000000000000//! common routines to be included by benches use std::fs::File; use std::io::BufReader; use bigdecimal::BigDecimal; use std::str::FromStr; use std::io::BufRead; macro_rules! resolve_benchmark_data_file { ( $filename:literal ) => { concat!(env!("BIGDECIMAL_BENCHMARK_DATA_PATH"), "/", $filename) } } macro_rules! open_benchmark_data_file { ( $filename:literal ) => {{ let path = resolve_benchmark_data_file!($filename); File::open(path).expect(&format!(concat!("Could not load random datafile ", $filename, " from path {:?}"), path)) }}; } macro_rules! include_benchmark_data_file { ( $filename:literal ) => {{ include_str!( resolve_benchmark_data_file!($filename) ) }}; } /// Read vector of big decimals from lines in file pub fn read_bigdecimal_file(file: File) -> Vec { read_bigdecimals(BufReader::new(file)) } /// Read bigdecaiml from buffered reader pub fn read_bigdecimals(reader: R) -> Vec { reader .lines() .map(|maybe_string| maybe_string.unwrap()) .map(|line| BigDecimal::from_str(&line).unwrap()) .collect() } /// Collect big-decimals from lines in string pub fn collect_bigdecimals(src: &str) -> Vec { src.lines() .map(|line| BigDecimal::from_str(&line).unwrap()) .collect() } /// Randomly iterates through items in vector pub struct RandomIterator<'a, T> { v: &'a Vec, rng: oorandom::Rand32, } impl<'a, T: Copy> RandomIterator<'a, T> { pub fn new(v: &'a Vec) -> Self { let seed = v.as_ptr() as u64; Self::new_with_seed(v, seed) } pub fn new_with_seed(v: &'a Vec, seed: u64) -> Self { Self { v: v, rng: oorandom::Rand32::new(seed), } } pub fn next(&mut self) -> T { let randval = self.rng.rand_u32() as usize % self.v.len(); let idx = randval % self.v.len(); self.v[idx] } } bigdecimal-0.4.7/build.rs000064400000000000000000000110101046102023000133520ustar 00000000000000use std::env; use std::path::{Path, PathBuf}; // configuration defaults const DEFAULT_PRECISION: &str = "100"; const DEFAULT_ROUNDING_MODE: &str = "HalfEven"; const FMT_EXPONENTIAL_LOWER_THRESHOLD: &str = "5"; const FMT_EXPONENTIAL_UPPER_THRESHOLD: &str = "15"; const FMT_MAX_INTEGER_PADDING: &str = "1000"; const SERDE_MAX_SCALE: &str = "150000"; 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); // Option::zip ac.emit_rustc_version(1, 46); // Remove this comment if enabled proptests // ::PROPERTY-TESTS:: autocfg::emit("property_tests"); 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); write_max_serde_parsing_scale_limit(&outdir); } /// Loads the environment variable string or default macro_rules! load_env { ($env:ident, $name:literal, $default:ident) => {{ println!("cargo:rerun-if-env-changed={}", $name); $env::var($name).unwrap_or_else(|_| $default.to_owned()) }}; } /// Create default_precision.rs, containing definition of constant DEFAULT_PRECISION loaded in src/lib.rs fn write_default_precision_file(outdir: &Path) { let env_var = load_env!(env, "RUST_BIGDECIMAL_DEFAULT_PRECISION", DEFAULT_PRECISION); let rust_file_path = outdir.join("default_precision.rs"); let default_prec: u32 = env_var .parse::() .expect("$RUST_BIGDECIMAL_DEFAULT_PRECISION must be an integer > 0") .into(); let rust_file_contents = format!("const DEFAULT_PRECISION: u64 = {};", default_prec); std::fs::write(rust_file_path, rust_file_contents).unwrap(); } /// Create default_rounding_mode.rs, using value of RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE environment variable fn write_default_rounding_mode(outdir: &Path) { let rounding_mode_name = load_env!(env, "RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE", DEFAULT_ROUNDING_MODE); let rust_file_path = outdir.join("default_rounding_mode.rs"); let rust_file_contents = format!("const DEFAULT_ROUNDING_MODE: RoundingMode = RoundingMode::{};", rounding_mode_name); 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 low_value = load_env!(env, "RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD", FMT_EXPONENTIAL_LOWER_THRESHOLD); let high_value = load_env!(env, "RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD", FMT_EXPONENTIAL_UPPER_THRESHOLD); let max_padding = load_env!(env, "RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING", FMT_MAX_INTEGER_PADDING); let low_value: u32 = low_value .parse::() .expect("$RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD must be an integer > 0") .into(); let high_value: u32 = high_value .parse::() .expect("$RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD must be valid u32"); let max_padding: u32 = max_padding .parse::() .expect("$RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING must be valid u32"); let rust_file_path = outdir.join("exponential_format_threshold.rs"); let rust_file_contents = [ format!("const EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD: usize = {};", low_value), format!("const EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD: usize = {};", high_value), format!("const FMT_MAX_INTEGER_PADDING: usize = {};", max_padding), ]; std::fs::write(rust_file_path, rust_file_contents.join("\n")).unwrap(); } /// Create write_default_rounding_mode.rs, containing definition of constant EXPONENTIAL_FORMAT_THRESHOLD loaded in src/impl_fmt.rs fn write_max_serde_parsing_scale_limit(outdir: &Path) { let scale_limit = load_env!(env, "RUST_BIGDECIMAL_SERDE_SCALE_LIMIT", SERDE_MAX_SCALE); let scale_limit: u32 = scale_limit .parse::() .or_else(|e| if scale_limit.to_lowercase() == "none" { Ok(0) } else { Err(e) }) .expect("$RUST_BIGDECIMAL_SERDE_SCALE_LIMIT must be an integer"); let rust_file_path = outdir.join("serde_scale_limit.rs"); let rust_file_contents = [ format!("const SERDE_SCALE_LIMIT: i64 = {};", scale_limit), ]; std::fs::write(rust_file_path, rust_file_contents.join("\n")).unwrap(); } bigdecimal-0.4.7/examples/floating-precision.rs000064400000000000000000000007051046102023000176760ustar 00000000000000extern crate bigdecimal; use bigdecimal::BigDecimal; use std::str::FromStr; fn main() { 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"); println!("Input string: {}", &input); println!("Big-decimal value: {:.10}", decimal); println!("Floating-point value: {:.10}", floating); } bigdecimal-0.4.7/examples/formatting-examples.rs000064400000000000000000000035651046102023000200770ustar 00000000000000 extern crate bigdecimal; extern crate num_traits; use bigdecimal::BigDecimal; macro_rules! print_varying_values { ($fmt:literal, $exp_range:expr) => { print_varying_values!($fmt, $exp_range, 1); }; ($fmt:literal, $exp_range:expr, $value:literal) => { println!("{}:", $fmt); println!("{}×", $value); for exp in $exp_range { let n = BigDecimal::new($value.into(), -exp); let dec_str = format!($fmt, n); let f = ($value as f64) * 10f64.powi(exp as i32); let float_str = format!($fmt, f); let s = format!("10^{}", exp); println!("{:>7} : {:25} {}", s, dec_str, float_str); } println!(""); } } macro_rules! print_varying_formats { ($src:literal) => { let src = $src; let n: BigDecimal = src.parse().unwrap(); let f: f64 = src.parse().unwrap(); println!("{}", src); println!(" {{}} : {:<15} {}", n, f); for prec in 0..5 { println!(" {{:.{prec}}} : {:<15.prec$} {:.prec$}", n, f, prec=prec); } println!(""); }; } fn main() { println!(" | BigDecimal | f64"); println!(" +------------ +-----"); print_varying_formats!("1234"); print_varying_formats!("1.234"); print_varying_formats!(".1234"); print_varying_formats!(".00001234"); print_varying_formats!(".00000001234"); print_varying_formats!("12340"); print_varying_formats!("1234e1"); print_varying_formats!("1234e10"); print_varying_values!("{}", -20..=20); print_varying_values!("{:e}", -20..=20); print_varying_values!("{:.2e}", -20..=20); print_varying_values!("{}", -10..=10, 12345); print_varying_values!("{:e}", -10..=10, 12345); print_varying_values!("{:.2e}", -10..=10, 12345); print_varying_values!("{:.3}", -10..=10, 12345); } bigdecimal-0.4.7/examples/repeated_squares.rs000064400000000000000000000011311046102023000174300ustar 00000000000000extern crate bigdecimal; use bigdecimal::BigDecimal; use std::time::Instant; use std::str::FromStr; fn main() { // let mut x = BigDecimal::from(1.1); let mut x = BigDecimal::from_str("1.1").unwrap(); // for iter in 0..1_000_000 { for iter in 0..26 { let start = Instant::now(); x = x.clone() * x; // x = x.take_and_square(); let end = Instant::now(); let usage = end - start; println!( "iter {} takes {} secs", iter, usage.as_secs() as f32 + usage.subsec_nanos() as f32 / 1.0e9 ); } } bigdecimal-0.4.7/examples/simple-math.rs000064400000000000000000000045651046102023000163320ustar 00000000000000extern crate bigdecimal; use bigdecimal::*; use std::str::FromStr; /* Default example output: Hello, Big Decimals! Input (0.8) with 10 decimals: 0.8 vs 0.8) square 0.64 From Prim: 3.300000000000000 match test 33.90000000000000 24.00000000000000 + 34.00000000000000 = 58.00000000000000 sum mut: 48.00000000000000 0.000000000000000 IS equal to zero 24.00000000000000 / 1.000000000000000 = 24.00000000000000 24.0 / 1.000000000000000 = 24.0 24.0 / 1.5 = 16 24.000 / 1.50 = 16.0 */ fn main() { println!("Hello, Big Decimals!"); let input = "0.8"; 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(); println!("square {}", bd_square); let bd_from_prim = BigDecimal::from_f64(3.3); println!("From Prim: {}", bd_from_prim.unwrap()); let bd_match: BigDecimal = match BigDecimal::from_f64(33.9) { Some(bd) => bd, None => BigDecimal::zero(), }; println!("match test {}", bd_match); let bd_add1 = &BigDecimal::from_f64(24.0).unwrap(); let bd_add2 = &BigDecimal::from_f64(34.0).unwrap(); print_sum(bd_add1, bd_add2); let mut bd_add3 = BigDecimal::from_f64(24.0).unwrap(); let bd_add4 = BigDecimal::from_f64(24.0).unwrap(); bd_add3 += bd_add4; println!("sum mut: {}", bd_add3); let bd_nez = BigDecimal::from_f64(0.0).unwrap(); if bd_nez != BigDecimal::zero() { println!("{} is not equal to zero", &bd_nez); } else { println!("{} IS equal to zero", &bd_nez); } let bd_div1 = &BigDecimal::from_f64(24.0).unwrap(); let bd_div2 = &BigDecimal::from_f64(1.0).unwrap(); print_division(bd_div1, bd_div2); let bd_div1 = &BigDecimal::from_str("24.0").unwrap(); print_division(bd_div1, bd_div2); let bd_num = &BigDecimal::from_str("24.0").unwrap(); let bd_den = &BigDecimal::from_str("1.5").unwrap(); print_division(bd_num, bd_den); let bd_num = &BigDecimal::from_str("24.000").unwrap(); let bd_den = &BigDecimal::from_str("1.50").unwrap(); print_division(bd_num, bd_den); } fn print_sum(a: &BigDecimal, b: &BigDecimal) { let sum = a + b; println!("{} + {} = {}", a, b, sum); } fn print_division(num: &BigDecimal, den: &BigDecimal) { let quotient = num / den; println!("{} / {} = {}", num, den, quotient); } bigdecimal-0.4.7/scripts/benchmark-bigdecimal000075500000000000000000000025751046102023000173520ustar 00000000000000#!/bin/bash # # Run Criterion Benchmarks # # enable bench-only dependencies in Cargo sed -i.bak -e 's/# BENCH: //' Cargo.toml # Run criterion cargo bench "$@" # Restore Cargo.toml with backup mv Cargo.toml.bak Cargo.toml # store extra things for the benchmarking report if [ -n "$BENCHMARK_EXTRAS" ]; then cat < index.html BigDecimal Benchmark Results ${CI_COMMIT_SHA}

BigDecimal Benchmark Results

Criterion Report: Report

EOF # Add svg plots to index html find target/criterion -name 'pdf.svg' -type f -print0 | sort -z | while read -r -d $'\0' svg_file do name=$(echo "$svg_file" | cut -d '/' -f 3) sample_datafile=target/criterion/$name/new/sample.json if [ -f "$sample_datafile" ]; then echo "

$name" >> index.html else echo "

$name

" >> index.html fi echo "" >> index.html done fi bigdecimal-0.4.7/scripts/bigdecimal-property-tests000075500000000000000000000026711046102023000204410ustar 00000000000000#!/bin/sh # # Run commands with property-tests enabled for bigdecimal crate # # Tests are defined in src/lib.tests.property-test.rs # SED=$(command -v gsed || command -v sed) if [ -z "$SED" ]; then echo "This program requires sed" exit 1 fi usage() { echo "Usage: $0 [enable|disable|test|run ]" echo "" echo " enable: Enable property tests by removing comments in Cargo.toml & build.rs" echo " disable: Restore property-tests comments in Cargo.toml & build.rs" echo " test: Runs 'cargo test' between enabling & disabling property tests" echo "run : Run user supplied command between enabling & disabling property tests" } enable_property_tests() { # enable property-test dependencies in Cargo ${SED} -i.bak -e 's|# PROPERTY-TESTS: ||' Cargo.toml # add the property-test configuration in build.rs ${SED} -i.bak -e 's|// ::PROPERTY-TESTS:: ||' build.rs } restore_disabled_property_tests() { # Restore Cargo.toml with backup mv Cargo.toml.bak Cargo.toml mv build.rs.bak build.rs touch build.rs } DEFAULT_CMD=help CMD=${1:-$DEFAULT_CMD} shift case "${CMD}" in run) enable_property_tests # Run commands "$@" restore_disabled_property_tests ;; test) enable_property_tests cargo test "$@" restore_disabled_property_tests ;; enable) enable_property_tests ;; disable) restore_disabled_property_tests ;; *) usage ;; esac bigdecimal-0.4.7/scripts/fetch-benchmark-data.sh000075500000000000000000000020231046102023000176670ustar 00000000000000#!/bin/bash # # Fetch benchmark data # # Should this just be a makefile? # TEST_DATA_DIR=benches/test-data CURL=$(command -v curl) WGET=$(command -v wget) GITLAB_URL_PATTERN='https://gitlab.com/akubera/bigdecimal-rs/-/raw/data-files/random-test-inputs/' function fetch_benchmark_bigdecimal_file() { local FILENAME="random-bigdecimals-$1.txt" local FILEPATH=$TEST_DATA_DIR/$FILENAME if [ -e "$FILEPATH" ]; then echo "exists: $FILEPATH" else local URL=${GITLAB_URL_PATTERN///$FILENAME} echo "fetching: $FILEPATH from $URL" if [ "$CURL" ]; then $CURL -s --fail -L "$URL" -o "$FILEPATH" elif [ "$WGET" ]; then "$WGET" --quiet "$URL" -O "$FILEPATH" else echo "No supported fetching program" fi fi } mkdir -p $TEST_DATA_DIR fetch_benchmark_bigdecimal_file "1f633481a742923ab65855c90157bbf7" fetch_benchmark_bigdecimal_file "9a08ddaa6ce6693cdd7b8a524e088bd0" fetch_benchmark_bigdecimal_file "4be58192272b15fc67573b39910837d0" fetch_benchmark_bigdecimal_file "a329e61834832d89593b29f12510bdc8" bigdecimal-0.4.7/scripts/make-fmt-format-tests.py000075500000000000000000000062561046102023000201200ustar 00000000000000#!/usr/bin/env python # # Generate Rust code given Decimals and formatting strings # import sys import textwrap from decimal import * # Decimal strings to the testing formats INPUTS = { "1": [ "{}", "{:.1}", "{:.4}", "{:4.1}", "{:>4.1}", "{:<4.1}", "{:+05.1}", ], "123456": [ "{}", "{:+05.1}", "{:.1}", "{:.4}", "{:4.1}", "{:>4.3}", "{:>4.4}", "{:<4.1}", ], "9999999": [ "{:.4}", "{:.8}", ], "19073.97235939614856": [ "{}", "{:+05.7}", "{:.3}", "{:0.4}", "{:4.1}", "{:>8.3}", "{:>8.4}", "{:<8.1}", ], "491326e-12": [ "{}", "{:+015.7}", "{:.3}", "{:0.4}", "{:4.1}", "{:>8.3}", "{:>8.4}", "{:<8.1}", ], } def main(): from argparse import ArgumentParser, FileType parser = ArgumentParser(description="Create test modules for src/impl_fmt.rs") parser.add_argument("-o", "--output", nargs="?", default="-", type=FileType("w")) args = parser.parse_args() for dec_str, formats in INPUTS.items(): src = make_test_module_src(dec_str, formats) args.output.write(src) return 0 def make_test_module_src(dec_str: Decimal, formats: list[str]) -> str: """ Return Rust module source which tests given decimal aginst given formats """ dec = Decimal(dec_str) mod_name = "dec_%s" % gen_name_from_dec(dec_str) formats_and_outputs = [ (fmt, gen_name_from_fmt(fmt), fmt.format(dec)) for fmt in formats ] max_len_name_and_fmt = max( len(fmt) + len(name) for fmt, name, _ in formats_and_outputs ) lines = [] for fmt, name, value in formats_and_outputs: spacer = " " * (max_len_name_and_fmt - (len(fmt) + len(name)) + 2) lines.append(f'impl_case!(fmt_{name}:{spacer}"{fmt}" => "{value}");') body = textwrap.indent("\n".join(lines), " ") text = textwrap.dedent( f""" mod {mod_name} {{ use super::*; fn test_input() -> BigDecimal {{ "{dec_str}".parse().unwrap() }} %s }} """ ) return text % body def gen_name_from_dec(dec_str: str) -> str: """Given decimal input string return valid function/module name""" return "".join(map_fmt_char_to_name_char(c) for c in dec_str) def gen_name_from_fmt(fmt: str) -> str: """Given decimal input string return valid function/module name""" if fmt == "{}": return "default" else: assert fmt.startswith("{:") and fmt.endswith("}") return "".join(map_fmt_char_to_name_char(c) for c in fmt[2:-1]) def map_fmt_char_to_name_char(c: str) -> str: """Turn special characters into identifiers""" match c: case ".": return "d" case "+": return "p" case "-": return "n" case "<": return "l" case ">": return "r" case ":": return "c" case _: return str(c) if __name__ == "__main__": sys.exit(main()) bigdecimal-0.4.7/src/arithmetic/addition.rs000064400000000000000000000071161046102023000170020ustar 00000000000000//! addition routines //! use crate::*; pub(crate) fn add_bigdecimals( mut a: BigDecimal, mut b: BigDecimal, ) -> BigDecimal { if b.is_zero() { a.extend_scale_to(b.scale); return a; } if a.is_zero() { b.extend_scale_to(a.scale); return b; } let (a, b) = match a.scale.cmp(&b.scale) { Ordering::Equal => (a, b), Ordering::Less => (a.take_and_scale(b.scale), b), Ordering::Greater => (b.take_and_scale(a.scale), a), }; add_aligned_bigdecimals(a, b) } fn add_aligned_bigdecimals( mut a: BigDecimal, mut b: BigDecimal, ) -> BigDecimal { debug_assert_eq!(a.scale, b.scale); if a.int_val.bits() >= b.int_val.bits() { a.int_val += b.int_val; a } else { b.int_val += a.int_val; b } } pub(crate) fn add_bigdecimal_refs<'a, 'b, Lhs, Rhs>( lhs: Lhs, rhs: Rhs, ctx: Option<&Context>, ) -> BigDecimal where Rhs: Into>, Lhs: Into>, { use stdlib::cmp::Ordering::*; let lhs = lhs.into(); let rhs = rhs.into(); if rhs.is_zero() { let scale_diff = rhs.scale.saturating_sub(lhs.scale).max(0).min(15); return lhs.to_owned_with_scale(lhs.scale + scale_diff); } if lhs.is_zero() { let scale_diff = lhs.scale.saturating_sub(rhs.scale).max(0).min(15); return rhs.to_owned_with_scale(rhs.scale + scale_diff); } match lhs.scale.cmp(&rhs.scale) { Equal => { add_aligned_bigdecimal_ref_ref(lhs, rhs) } Greater => { add_unaligned_bigdecimal_ref_ref(lhs, rhs, ctx) } Less => { add_unaligned_bigdecimal_ref_ref(rhs, lhs, ctx) } } } pub(crate) fn addassign_bigdecimals( lhs: &mut BigDecimal, rhs: BigDecimal, ) { if rhs.is_zero() { return; } if lhs.is_zero() { *lhs = rhs; return; } lhs.add_assign(rhs.to_ref()); } pub(crate) fn addassign_bigdecimal_ref<'a, T: Into>>( lhs: &mut BigDecimal, rhs: T, ) { // TODO: Replace to_owned() with efficient addition algorithm let rhs = rhs.into().to_owned(); match lhs.scale.cmp(&rhs.scale) { Ordering::Less => { let scaled = lhs.with_scale(rhs.scale); lhs.int_val = scaled.int_val + &rhs.int_val; lhs.scale = rhs.scale; } Ordering::Greater => { let scaled = rhs.with_scale(lhs.scale); lhs.int_val += scaled.int_val; } Ordering::Equal => { lhs.int_val += &rhs.int_val; } } } /// Add BigDecimal references which have the same scale (integer addition) fn add_aligned_bigdecimal_ref_ref( lhs: BigDecimalRef, rhs: BigDecimalRef ) -> BigDecimal { debug_assert!(!lhs.is_zero() && !rhs.is_zero()); debug_assert_eq!(lhs.scale, rhs.scale); if lhs.digits.bits() >= rhs.digits.bits() { lhs.to_owned() + rhs } else { rhs.to_owned() + lhs } } fn add_unaligned_bigdecimal_ref_ref( lhs: BigDecimalRef, rhs: BigDecimalRef, _ctx: Option<&Context>, ) -> BigDecimal { debug_assert!(!lhs.is_zero() && !rhs.is_zero()); debug_assert!(lhs.scale >= rhs.scale); let scale_diff = (lhs.scale - rhs.scale) as u64; let shifted_rhs_digits = rhs.digits * ten_to_the_uint(scale_diff); let shifted_rhs_int = BigInt::from_biguint(rhs.sign, shifted_rhs_digits); let shifted_rhs = BigDecimal::new(shifted_rhs_int, lhs.scale); shifted_rhs + lhs } #[cfg(test)] mod test { use super::*; include!("addition.tests.rs"); } bigdecimal-0.4.7/src/arithmetic/addition.tests.rs000064400000000000000000000036731046102023000201470ustar 00000000000000mod add_bigdecimals { use super::*; use paste::paste; macro_rules! impl_case { ( $name:ident: $a:literal + $b:literal = $c:literal ) => { #[test] fn $name() { let lhs: BigDecimal = $a.parse().unwrap(); let rhs: BigDecimal = $b.parse().unwrap(); let l_plus_r = add_bigdecimals(lhs.clone(), rhs.clone()); let r_plus_l = add_bigdecimals(rhs, lhs); let expected: BigDecimal = $c.parse().unwrap(); assert_eq!(expected.int_val, l_plus_r.int_val); assert_eq!(expected.scale, l_plus_r.scale); assert_eq!(expected.int_val, r_plus_l.int_val); assert_eq!(expected.scale, r_plus_l.scale); } paste! { #[test] fn [< $name _refs >]() { let lhs: BigDecimal = $a.parse().unwrap(); let rhs: BigDecimal = $b.parse().unwrap(); let l_plus_r = add_bigdecimal_refs(&lhs, &rhs, None); let r_plus_l = add_bigdecimal_refs(&rhs, &lhs, None); let expected: BigDecimal = $c.parse().unwrap(); assert_eq!(expected.int_val, l_plus_r.int_val); assert_eq!(expected.scale, l_plus_r.scale); assert_eq!(expected.int_val, r_plus_l.int_val); assert_eq!(expected.scale, r_plus_l.scale); } } }; } impl_case!(case_1d2345_123d45: "1.2345" + "123.45" = "124.6845"); impl_case!(case_123d43e5_1d2345: "123.43e5" + "1.2345" = "12343001.2345"); impl_case!(case_0_0: "0" + "0" = "0"); impl_case!(case_0_0d00: "0" + "0.00" = "0.00"); impl_case!(case_10_0d00: "10" + "0.00" = "10.00"); impl_case!(case_22132e2_0d0000: "22132e2" + "0.0000" = "2213200.0000"); impl_case!(case_n316d79_0en6: "-316.79" + "0e-6" = "-316.790000"); } bigdecimal-0.4.7/src/arithmetic/cbrt.rs000064400000000000000000000203631046102023000161400ustar 00000000000000//! Implementation of cube-root algorithm use crate::*; use num_bigint::BigUint; use rounding::NonDigitRoundingData; use stdlib::num::NonZeroU64; pub(crate) fn impl_cbrt_int_scale(n: &BigInt, scale: i64, ctx: &Context) -> BigDecimal { let rounding_data = NonDigitRoundingData { sign: n.sign(), mode: ctx.rounding_mode(), }; impl_cbrt_uint_scale((n.magnitude(), scale).into(), ctx.precision(), rounding_data) } /// implementation of cuberoot - always positive pub(crate) fn impl_cbrt_uint_scale( n: WithScale<&BigUint>, precision: NonZeroU64, // contains sign and rounding mode rounding_data: NonDigitRoundingData, ) -> BigDecimal { if n.is_zero() { let biguint = BigInt::from_biguint(Sign::Plus, n.value.clone()); return BigDecimal::new(biguint, n.scale / 3); } // count number of digits in the decimal let integer_digit_count = count_decimal_digits_uint(n.value); // extra digits to use for rounding let extra_rounding_digit_count = 4; // required number of digits for precision and rounding let required_precision = precision.get() + extra_rounding_digit_count; let required_precision = 3 * required_precision; // number of extra zeros to add to end of integer_digits let mut exp_shift = required_precision.saturating_sub(integer_digit_count); // effective scale after multiplying by 10^exp_shift // (we've added that many trailing zeros after) let shifted_scale = n.scale + exp_shift as i64; let (mut new_scale, remainder) = shifted_scale.div_rem(&3); 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 let mut integer_digits = stdlib::borrow::Cow::Borrowed(n.value); // add required trailing zeros to integer_digits if exp_shift > 0 { arithmetic::multiply_by_ten_to_the_uint( integer_digits.to_mut(), exp_shift ); } let result_digits = integer_digits.nth_root(3); let result_digits_count = count_decimal_digits_uint(&result_digits); debug_assert!(result_digits_count > precision.get()); let digits_to_trim = result_digits_count - precision.get(); debug_assert_ne!(digits_to_trim, 0); debug_assert!((result_digits_count as i64 - count_decimal_digits_uint(&integer_digits) as i64 / 3).abs() < 2); new_scale -= digits_to_trim as i64; let divisor = ten_to_the_uint(digits_to_trim); let (mut result_digits, remainder) = result_digits.div_rem(&divisor); let remainder_digits = remainder.to_radix_le(10); let insig_digit0; let trailing_digits; if remainder_digits.len() < digits_to_trim as usize { // leading zeros insig_digit0 = 0; trailing_digits = remainder_digits.as_slice(); } else { let (&d, rest) = remainder_digits.split_last().unwrap(); insig_digit0 = d; trailing_digits = rest; } let insig_data = rounding::InsigData::from_digit_and_lazy_trailing_zeros( rounding_data, insig_digit0, || { trailing_digits.iter().all(Zero::is_zero) } ); // lowest digit to round let sig_digit = (&result_digits % 10u8).to_u8().unwrap(); let rounded_digit = insig_data.round_digit(sig_digit); let rounding_term = rounded_digit - sig_digit; result_digits += rounding_term; let result = BigInt::from_biguint(rounding_data.sign, result_digits); BigDecimal::new(result, new_scale) } #[cfg(test)] mod test { use super::*; use stdlib::num::NonZeroU64; macro_rules! impl_test { ($name:ident; $input:literal => $expected:literal) => { #[test] fn $name() { let n: BigDecimal = $input.parse().unwrap(); let value = n.cbrt(); let expected = $expected.parse().unwrap(); assert_eq!(value, expected); } }; ($name:ident; prec=$prec:literal; round=$round:ident; $input:literal => $expected:literal) => { #[test] fn $name() { let ctx = Context::new(NonZeroU64::new($prec).unwrap(), RoundingMode::$round); let n: BigDecimal = $input.parse().unwrap(); let value = n.cbrt_with_context(&ctx); let expected = $expected.parse().unwrap(); assert_eq!(value, expected); } }; } mod default { use super::*; impl_test!(case_0; "0.00" => "0"); impl_test!(case_1; "1.00" => "1"); impl_test!(case_1d001; "1.001" => "1.000333222283909495175449559955220102010284758197360454054345461242739715702641939155238095670636841"); impl_test!(case_10; "10" => "2.154434690031883721759293566519350495259344942192108582489235506346411106648340800185441503543243276"); impl_test!(case_13409d179789484375; "13409.179789484375" => "23.7575"); impl_test!(case_n59283293e25; "-59283293e25" => "-84006090355.84281237113712383191213626687332139035750444925827809487776780721673264524620270275301685"); impl_test!(case_94213372931en127; "94213372931e-127" => "2.112049945275324414051072540210070583697242797173805198575907094646677475250362108901530353886613160E-39"); } impl_test!(case_prec15_down_10; prec=15; round=Down; "10" => "2.15443469003188"); impl_test!(case_prec6_up_0d979970546636727; prec=6; round=Up; "0.979970546636727" => "0.993279"); impl_test!(case_1037d495615705321421375_full; "1037.495615705321421375" => "10.123455"); impl_test!(case_1037d495615705321421375_prec7_halfdown; prec=7; round=HalfDown; "1037.495615705321421375" => "10.12345"); impl_test!(case_1037d495615705321421375_prec7_halfeven; prec=7; round=HalfEven; "1037.495615705321421375" => "10.12346"); impl_test!(case_1037d495615705321421375_prec7_halfup; prec=7; round=HalfUp; "1037.495615705321421375" => "10.12346"); impl_test!(case_0d014313506928855520728400001_full; "0.014313506928855520728400001" => "0.242800001"); impl_test!(case_0d014313506928855520728400001_prec6_down; prec=6; round=Down; "0.014313506928855520728400001" => "0.242800"); impl_test!(case_0d014313506928855520728400001_prec6_up; prec=6; round=Up; "0.014313506928855520728400001" => "0.242801"); impl_test!(case_4151902e20_prec16_halfup; prec=16; round=HalfUp; "4151902e20" => "746017527.6855992"); impl_test!(case_4151902e20_prec16_up; prec=16; round=Up; "4151902e20" => "746017527.6855993"); impl_test!(case_4151902e20_prec17_up; prec=17; round=Up; "4151902e20" => "746017527.68559921"); impl_test!(case_4151902e20_prec18_up; prec=18; round=Up; "4151902e20" => "746017527.685599209"); // impl_test!(case_4151902e20_prec18_up; prec=18; round=Up; "4151902e20" => "746017527.685599209"); impl_test!(case_1850846e201_prec14_up; prec=16; round=Up; "1850846e201" => "1.227788123885769e69"); impl_test!(case_6d3797558642427987505823530913e85_prec16_up; prec=160; round=Up; "6.3797558642427987505823530913E+85" => "3995778017e19"); impl_test!(case_88573536600476899341824_prec20_up; prec=20; round=Up; "88573536600476899341824" => "44576024"); impl_test!(case_88573536600476899341824_prec7_up; prec=7; round=Up; "88573536600476899341824" => "4457603e1"); impl_test!(case_833636d150970875_prec5_up; prec=5; round=Up; "833636.150970875" => "94.115000"); impl_test!(case_833636d150970875_prec5_halfup; prec=5; round=HalfUp; "833636.150970875" => "94.115"); impl_test!(case_833636d150970875_prec4_halfup; prec=4; round=HalfUp; "833636.150970875" => "94.12"); impl_test!(case_833636d150970875_prec20_up; prec=20; round=Up; "833636.150970875" => "94.115000"); #[cfg(property_tests)] mod prop { use super::*; use proptest::*; use num_traits::FromPrimitive; proptest! { #[test] fn cbrt_of_cube_is_self(f: f64, prec in 15..50u64) { // ignore non-normal numbers prop_assume!(f.is_normal()); let n = BigDecimal::from_f64(f).unwrap().with_prec(prec); let n_cubed = n.cube(); let x = n_cubed.cbrt(); prop_assert_eq!(x, n); } } } } bigdecimal-0.4.7/src/arithmetic/inverse.rs000064400000000000000000000171301046102023000166570ustar 00000000000000//! inverse implementation use crate::*; /// Implementation of inverse: (1/n) pub(crate) fn impl_inverse_uint_scale(n: &BigUint, scale: i64, ctx: &Context) -> BigDecimal { let guess = make_inv_guess(n.bits(), scale); let max_precision = ctx.precision().get(); let s = BigDecimal::new(BigInt::from_biguint(Sign::Plus, n.clone()), scale); let two = BigDecimal::from(2); let next_iteration = move |r: BigDecimal| { let tmp = &two - &s * &r; r * tmp }; // calculate first iteration let mut running_result = next_iteration(guess); debug_assert!(!running_result.is_zero(), "Zero detected in inverse calculation of {}e{}", n, -scale); let mut prev_result = BigDecimal::one(); let mut result = BigDecimal::zero(); // TODO: Prove that we don't need to arbitrarily limit iterations // and that convergence can be calculated while prev_result != result { // store current result to test for convergence prev_result = result; // calculate next iteration running_result = next_iteration(running_result).with_prec(max_precision + 2); // 'result' has clipped precision, 'running_result' has full precision result = if running_result.digits() > max_precision { running_result.with_precision_round(ctx.precision(), ctx.rounding_mode()) } else { running_result.clone() }; } return result; } /// guess inverse based on the number of bits in the integer and decimal's scale fn make_inv_guess(bit_count: u64, scale: i64) -> BigDecimal { // scale by ln(2) let magic_factor = stdlib::f64::consts::LN_2; let bit_count = bit_count as f64; let initial_guess = magic_factor * exp2(-bit_count); if initial_guess.is_finite() && initial_guess != 0.0 { if let Ok(mut result) = BigDecimal::try_from(initial_guess) { result.scale -= scale; return result; } } // backup guess for out-of-range integers let approx_scale = bit_count * stdlib::f64::consts::LOG10_2; let approx_scale_int = approx_scale.trunc(); let approx_scale_frac = approx_scale - approx_scale_int; let recip = libm::exp10(-approx_scale_frac); let mut res = BigDecimal::from_f32((magic_factor * recip) as f32).unwrap(); res.scale += approx_scale_int as i64; res.scale -= scale; return res; } #[cfg(test)] mod test_make_inv_guess { use super::*; use paste::paste; macro_rules! impl_case { ( $bin_count:literal, -$scale:literal => $expected:literal ) => { paste! { impl_case!( [< case_ $bin_count _n $scale >]: $bin_count, -$scale => $expected); } }; ( $bin_count:literal, $scale:literal => $expected:literal ) => { paste! { impl_case!( [< case_ $bin_count _ $scale >]: $bin_count, $scale => $expected); } }; ( $name:ident: $bin_count:expr, $scale:expr => $expected:literal ) => { impl_case!($name: $bin_count, $scale, prec=5 => $expected); }; ( $name:ident: $bin_count:expr, $scale:expr, prec=$prec:literal => $expected:literal ) => { #[test] fn $name() { let guess = make_inv_guess($bin_count, $scale); let expected: BigDecimal = $expected.parse().unwrap(); assert_eq!(guess.with_prec($prec), expected.with_prec($prec)); } }; } impl_case!(0, 0 => "0.69315"); impl_case!(1, 0 => "0.34657"); impl_case!(2, 0 => "0.17329"); impl_case!(2, 1 => "1.7329"); // 1 / (2^3 * 10^5) ~ impl_case!(3, -5 => "8.6643e-07"); // 2^-20 impl_case!(20, 0 => "6.6104e-07"); impl_case!(20, -900 => "6.6104E-907"); impl_case!(20, 800 => "6.6104E+793"); impl_case!(40, 10000 => "6.3041E+9987"); impl_case!(70, -5 => "5.8712e-27"); impl_case!(70, 5 => "5.8712e-17"); impl_case!(70, 50 => "5.8712e+28"); impl_case!(888, -300 => "3.3588E-568"); impl_case!(888, -19 => "3.3588E-287"); impl_case!(888, 0 => "3.3588E-268"); impl_case!(888, 270 => "335.88"); impl_case!(1022, 10 => "1.5423e-298"); impl_case!(1022, 308 => "1.5423"); impl_case!(1038, 316 => "2353.4"); impl_case!(case_31028_n659: 31028, -659 => "3.0347E-10000"); impl_case!(case_31028_0: 31028, 0 => "3.0347E-9341"); impl_case!(case_31028_1: 31028, 1 => "3.0347E-9340"); impl_case!(case_31028_9340: 31028, 9340 => ".30347"); impl_case!(case_31028_10000: 31028, 10000 => "3.0347E+659"); // impl_case!(case_max: u64::MAX, 270 => "335.88"); } #[cfg(test)] mod test { use super::*; use stdlib::num::NonZeroU64; #[test] fn test_inverse_35543972957198043e291() { let v = vec![ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 2324389888, 849200558 ]; let x = BigInt::new(Sign::Minus, v); let d = BigDecimal::from(x); let expected = "-2.813416500187520746852694701086705659180043761702417561798711758892800449936819185796527214192677476E-308".parse().unwrap(); assert_eq!(d.inverse(), expected); assert_eq!(d.neg().inverse(), expected.neg()); } macro_rules! impl_case { ($name:ident: $prec:literal, $round:ident => $expected:literal) => { #[test] fn $name() { let n = test_input(); let prec = NonZeroU64::new($prec).unwrap(); let rounding = RoundingMode::$round; let ctx = Context::new(prec, rounding); let result = n.inverse_with_context(&ctx); let expected = $expected.parse().unwrap(); assert_eq!(&result, &expected); let product = result * &n; let epsilon = BigDecimal::new(BigInt::one(), $prec - 1); let diff = (BigDecimal::one() - &product).abs(); assert!(diff < epsilon); } }; } mod invert_seven { use super::*; fn test_input() -> BigDecimal { BigDecimal::from(7u8) } impl_case!(case_prec10_round_down: 10, Down => "0.1428571428"); impl_case!(case_prec10_round_up: 10, Up => "0.1428571429"); impl_case!(case_prec11_round_ceiling: 11, Ceiling => "0.14285714286"); } #[test] fn inv_random_number() { let n = BigDecimal::try_from(0.08121970592310568).unwrap(); let ctx = Context::new(NonZeroU64::new(40).unwrap(), RoundingMode::Down); let i = n.inverse_with_context(&ctx); assert_eq!(&i, &"12.31228294456944530942557443718279245563".parse().unwrap()); let product = i * &n; assert!(BigDecimal::one() - &product < "1e-39".parse().unwrap()); } #[cfg(property_tests)] mod prop { use super::*; use proptest::*; use num_traits::FromPrimitive; proptest! { #[test] fn inverse_multiplies_to_one(f: f64, prec in 1..100u64) { // ignore non-normal numbers prop_assume!(f.is_normal()); prop_assume!(f != 0.0); let n = BigDecimal::from_f64(f).unwrap(); let ctx = Context::new(NonZeroU64::new(prec).unwrap(), RoundingMode::Up); let i = n.inverse_with_context(&ctx); let product = &i * &n; // accurate to precision minus one (due to rounding) let epsilon = BigDecimal::new(1.into(), prec as i64 - 1); let diff_from_one = BigDecimal::one() - &product; prop_assert!(diff_from_one.abs() < epsilon, "{} >= {}", diff_from_one.abs(), epsilon); } } } } bigdecimal-0.4.7/src/arithmetic/mod.rs000064400000000000000000000115041046102023000157620ustar 00000000000000//! arithmetic routines use crate::*; use num_traits::CheckedSub; pub(crate) mod addition; pub(crate) mod sqrt; pub(crate) mod cbrt; pub(crate) mod inverse; /// Return 10^pow /// /// Try to calculate this with fewest number of allocations /// pub(crate) fn ten_to_the(pow: u64) -> BigInt { ten_to_the_uint(pow).into() } /// Return 10^{pow} as u64 pub(crate) fn ten_to_the_u64(pow: u8) -> u64 { debug_assert!(pow < 20); 10u64.pow(pow as u32) } /// Return 10^pow pub(crate) fn ten_to_the_uint(pow: u64) -> BigUint { if pow < 20 { return BigUint::from(10u64.pow(pow as u32)); } // linear case of 10^pow = 10^(19 * count + rem) if pow < 590 { let ten_to_nineteen = 10u64.pow(19); // count factors of 19 let (count, rem) = pow.div_rem(&19); let mut res = BigUint::from(ten_to_nineteen); for _ in 1..count { res *= ten_to_nineteen; } if rem != 0 { res *= 10u64.pow(rem as u32); } return res; } // use recursive algorithm where linear case might be too slow let (quotient, rem) = pow.div_rem(&16); let x = ten_to_the_uint(quotient); let x2 = &x * &x; let x4 = &x2 * &x2; let x8 = &x4 * &x4; let res = &x8 * &x8; if rem == 0 { res } else { res * 10u64.pow(rem as u32) } } pub(crate) fn multiply_by_ten_to_the_uint(n: &mut T, pow: P) where T: MulAssign + MulAssign, P: ToPrimitive { let pow = pow.to_u64().expect("exponent overflow error"); if pow < 20 { *n *= 10u64.pow(pow as u32); } else { *n *= ten_to_the_uint(pow); } } /// Return number of decimal digits in integer pub(crate) fn count_decimal_digits(int: &BigInt) -> u64 { count_decimal_digits_uint(int.magnitude()) } /// Return number of decimal digits in unsigned integer pub(crate) fn count_decimal_digits_uint(uint: &BigUint) -> u64 { if uint.is_zero() { return 1; } let mut digits = (uint.bits() as f64 / LOG2_10) as u64; // guess number of digits based on number of bits in UInt let mut num = ten_to_the_uint(digits); while *uint >= num { num *= 10u8; digits += 1; } digits } /// Return difference of two numbers, returning diff as u64 pub(crate) fn diff(a: T, b: T) -> (Ordering, u64) where T: ToPrimitive + CheckedSub + stdlib::cmp::Ord { use stdlib::cmp::Ordering::*; let (ord, diff) = checked_diff(a, b); (ord, diff.expect("subtraction overflow")) } /// Return difference of two numbers. If num doesn't fit in u64, return None pub(crate) fn checked_diff(a: T, b: T) -> (Ordering, Option) where T: ToPrimitive + CheckedSub + stdlib::cmp::Ord { use stdlib::cmp::Ordering::*; let _try_subtracting = |x:T, y:T| x.checked_sub(&y).and_then(|diff| diff.to_u64()); match a.cmp(&b) { Less => (Less, _try_subtracting(b, a)), Greater => (Greater, _try_subtracting(a, b)), Equal => (Equal, Some(0)), } } /// Return difference of two numbers, returning diff as usize #[allow(dead_code)] pub(crate) fn diff_usize(a: usize, b: usize) -> (Ordering, usize) { use stdlib::cmp::Ordering::*; match a.cmp(&b) { Less => (Less, b - a), Greater => (Greater, a - b), Equal => (Equal, 0), } } /// 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 /// pub(crate) fn add_carry(n: u8, carry: &mut u8) -> u8 { let s = n + *carry; if s < 10 { *carry = 0; s } else { debug_assert!(s < 20); *carry = 1; s - 10 } } /// If n is greater than 10, split and store overflow in carry /// /// No action if n is less than 10. /// /// Carry is not allowed to be 1 if n is two digits /// pub(crate) fn store_carry(n: u8, carry: &mut u8) -> u8 { if n < 10 { n } else { debug_assert!(n < 20); debug_assert_eq!(carry, &0); *carry = 1; n - 10 } } /// Extend destination vector with values in D, adding carry while carry is not zero /// /// If carry overflows, it is NOT pushed into the destination vector. /// pub(crate) fn extend_adding_with_carry>( dest: &mut Vec, mut digits: D, carry: &mut u8, ) { while *carry != 0 { match digits.next() { Some(d) => { dest.push(add_carry(d, carry)) } None => { return; } } } dest.extend(digits); } bigdecimal-0.4.7/src/arithmetic/sqrt.rs000064400000000000000000000256261046102023000162060ustar 00000000000000//! square root implementation 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 scale_diff = BigInt::from(num_digits) - scale; // Calculate the number of wanted digits and the exponent we need to raise the original value to // We want twice as many digits as the precision because sqrt halves the number of digits // We add an extra one for rounding purposes let prec = ctx.precision().get(); let extra_rounding_digit_count = 5; let wanted_digits = 2 * (prec + extra_rounding_digit_count); let exponent = wanted_digits.saturating_sub(num_digits) + u64::from(scale_diff.is_odd()); let sqrt_digits = (n * ten_to_the_uint(exponent)).sqrt(); // Calculate the scale of the result let result_scale_digits = 2 * (2 * prec - scale_diff) - 1; let result_scale_decimal: BigDecimal = BigDecimal::new(result_scale_digits, 0) / 4.0; let mut result_scale = result_scale_decimal.with_scale_round(0, RoundingMode::HalfEven).int_val; // Round the value so it has the correct precision requested result_scale += count_decimal_digits_uint(&sqrt_digits).saturating_sub(prec); let unrounded_result = BigDecimal::new(sqrt_digits.into(), result_scale.to_i64().unwrap()); unrounded_result.with_precision_round(ctx.precision(), ctx.rounding_mode()) } #[cfg(test)] mod test { use super::*; macro_rules! impl_case { ($name:ident; $input:literal => $expected:literal) => { #[test] fn $name() { let n: BigDecimal = $input.parse().unwrap(); let value = n.sqrt().unwrap(); let expected = $expected.parse().unwrap(); assert_eq!(value, expected); // assert_eq!(value.scale, expected.scale); } }; ($name:ident; prec=$prec:literal; round=$round:ident; $input:literal => $expected:literal) => { #[test] fn $name() { let ctx = Context::default() .with_prec($prec).unwrap() .with_rounding_mode(RoundingMode::$round); let n: BigDecimal = $input.parse().unwrap(); let value = n.sqrt_with_context(&ctx).unwrap(); let expected = $expected.parse().unwrap(); assert_eq!(value, expected); // assert_eq!(value.scale, expected.scale); } }; } impl_case!(case_0d000; "0.000" => "0"); impl_case!(case_1en232; "1e-232" => "1e-116"); impl_case!(case_1d00; "1.00" => "1.00"); impl_case!(case_1d001; "1.001" => "1.000499875062460964823258287700109753027590031219780479551442971840836093890879944856933288426795152"); impl_case!(case_100d0; "100" => "10.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); impl_case!(case_49; "49" => "7.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); impl_case!(case_d25; ".25" => ".5"); impl_case!(case_0d0152399025; "0.0152399025" => ".12345"); impl_case!(case_0d00400; "0.00400" => "0.06324555320336758663997787088865437067439110278650433653715009705585188877278476442688496216758600590"); impl_case!(case_0d1; "0.1" => "0.3162277660168379331998893544432718533719555139325216826857504852792594438639238221344248108379300295"); impl_case!(case_152399025; "152399025" => "12345"); impl_case!(case_2; "2" => "1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573"); impl_case!(case_125348; "125348" => "354.0451948551201563108487193176101314241016013304294520812832530590100407318465590778759640828114535"); impl_case!(case_121d000242000121; "121.000242000121000000" => "11.000011000"); impl_case!(case_0d01234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901; "0.01234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901" => "0.1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); impl_case!(case_2e70; "2e70" => "141421356237309504880168872420969807.8569671875376948073176679737990732478462107038850387534327641573"); impl_case!(case_8d9793115997963468544185161590576171875en11; "8.9793115997963468544185161590576171875e-11" => "0.000009475922962855041517561783740144225422359796851494316346796373337470068631250135521161989831460407155"); impl_case!(case_18446744073709551616d1099511; "18446744073709551616.1099511" => "4294967296.000000000012799992691725492477397918722952224079252026972356303360555051219312462698703293"); impl_case!(case_3d1415926; "3.141592653589793115997963468544185161590576171875" => "1.772453850905515992751519103139248439290428205003682302442979619028063165921408635567477284443197875"); impl_case!(case_0d71777001; "0.7177700109762963922745342343167413624881759290454997218753321040760896053150388903350654937434826216803814031987652326749140535150336357405672040727695124057298138872112244784753994931999476811850580200000000000000000000000000000000" => "0.8472130847527653667042980517799020703921106560594525833177762276594388966885185567535692987624493813"); impl_case!(case_0d110889ddd444; "0.1108890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000444" => "0.3330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000667"); impl_case!(case_3e170; "3e170" => "17320508075688772935274463415058723669428052538103806280558069794519330169088000370811.46186757248576"); impl_case!(case_9e199; "9e199" => "9486832980505137995996680633298155601158665417975650480572514558377783315917714664032744325137900886"); impl_case!(case_7e200; "7e200" => "2645751311064590590501615753639260425710259183082450180368334459201068823230283627760392886474543611e1"); impl_case!(case_777e204; "777e204" => "2.787471972953270789531596912111625325974789615194854615319795902911796043681078997362635440358922503E+103"); impl_case!(case_777e600; "7e600" => "2.645751311064590590501615753639260425710259183082450180368334459201068823230283627760392886474543611E+300"); impl_case!(case_2e900; "2e900" => "1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573E+450"); impl_case!(case_7e999; "7e999" => "8.366600265340755479781720257851874893928153692986721998111915430804187725943170098308147119649515362E+499"); impl_case!(case_74908163946345982392040522594123773796e999; "74908163946345982392040522594123773796e999" => "2.736935584670307552030924971360722787091742391079630976117950955395149091570790266754718322365663909E+518"); impl_case!(case_20e1024; "20e1024" => "4.472135954999579392818347337462552470881236719223051448541794490821041851275609798828828816757564550E512"); impl_case!(case_3en1025; "3e-1025" => "5.477225575051661134569697828008021339527446949979832542268944497324932771227227338008584361638706258E-513"); impl_case!(case_3242053850483855en13_prec11_round_down; prec=11; round=Down; "324.2053850483855" => "18.005704236"); impl_case!(case_3242053850483855en13_prec11_round_up; prec=11; round=Up; "324.2053850483855" => "18.005704237"); impl_case!(case_3242053850483855en13_prec31_round_up; prec=31; round=Up; "324.2053850483855" => "18.00570423639090823994825477228"); impl_case!(case_5d085019992340351en10_prec25_round_down; prec=25; round=Down; "5.085019992340351e-10" => "0.00002254998889653906459324292"); impl_case!(case_3025d13579652399025_prec3_round_up; prec=3; round=Up; "3025.13579652399025" => "55.1"); impl_case!(case_3025d13579652399025_prec9_round_down; prec=9; round=Down; "3025.13579652399025" => "55.0012345"); impl_case!(case_3025d13579652399025_prec9_round_up; prec=9; round=Up; "3025.13579652399025" => "55.0012345"); impl_case!(case_3025d13579652399025_prec8_round_halfdown; prec=8; round=HalfDown; "3025.13579652399025" => "55.001234"); impl_case!(case_3025d13579652399025_prec8_round_halfeven; prec=8; round=HalfEven; "3025.13579652399025" => "55.001234"); impl_case!(case_3025d13579652399025_prec8_round_halfup; prec=8; round=HalfUp; "3025.13579652399025" => "55.001235"); #[test] fn test_sqrt_rounding() { let vals = vec![ // sqrt(1.21) = 1.1, [Ceiling, Up] should round up ("1.21", "2", "1", "1", "1", "1", "1", "2"), // sqrt(2.25) = 1.5, [Ceiling, HalfEven, HalfUp, Up] should round up ("2.25", "2", "1", "1", "1", "2", "2", "2"), // sqrt(6.25) = 2.5, [Ceiling, HalfUp, Up] should round up ("6.25", "3", "2", "2", "2", "2", "3", "3"), // sqrt(8.41) = 2.9, [Ceiling, HalfDown, HalfEven, HalfUp, Up] should round up ("8.41", "3", "2", "2", "3", "3", "3", "3"), ]; for &(val, ceiling, down, floor, half_down, half_even, half_up, up) in vals.iter() { let val = BigDecimal::from_str(val).unwrap(); let ceiling = BigDecimal::from_str(ceiling).unwrap(); let down = BigDecimal::from_str(down).unwrap(); let floor = BigDecimal::from_str(floor).unwrap(); let half_down = BigDecimal::from_str(half_down).unwrap(); let half_even = BigDecimal::from_str(half_even).unwrap(); let half_up = BigDecimal::from_str(half_up).unwrap(); let up = BigDecimal::from_str(up).unwrap(); let ctx = Context::default().with_prec(1).unwrap(); assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Ceiling)).unwrap(), ceiling); assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Down)).unwrap(), down); assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Floor)).unwrap(), floor); assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::HalfDown)).unwrap(), half_down); assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::HalfEven)).unwrap(), half_even); assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::HalfUp)).unwrap(), half_up); assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Up)).unwrap(), up); } } #[cfg(property_tests)] mod prop { use super::*; use proptest::*; use num_traits::FromPrimitive; proptest! { #[test] fn sqrt_of_square_is_self(f: f64, prec in 15..50u64) { // ignore non-normal numbers prop_assume!(f.is_normal()); let n = BigDecimal::from_f64(f.abs()).unwrap().with_prec(prec); let n_squared = n.square(); let x = n_squared.sqrt().unwrap(); prop_assert_eq!(x, n); } } } } bigdecimal-0.4.7/src/context.rs000064400000000000000000000155501046102023000145430ustar 00000000000000//! Define arithmetical context //! use crate::*; use stdlib::num::NonZeroU64; use arithmetic::store_carry; // const DEFAULT_PRECISION: u64 = ${RUST_BIGDECIMAL_DEFAULT_PRECISION} or 100; include!(concat!(env!("OUT_DIR"), "/default_precision.rs")); /// Mathematical Context /// /// Stores rules for numerical operations, such as how to round and /// number of digits to keep. /// /// Defaults are defined at compile time, determined by environment /// variables: /// /// | Variable | Descripiton | default | /// |-----------------------------------------|-----------------|----------| /// | `RUST_BIGDECIMAL_DEFAULT_PRECISION` | digit precision | 100 | /// | `RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE` | rounding-mode | HalfEven | /// /// It is recommended that the user set explicit values of a Context and *not* /// rely on compile time constants, but the option is there if necessary. /// #[derive(Debug, Clone)] pub struct Context { /// total number of digits precision: NonZeroU64, /// how to round rounding: RoundingMode, } impl Context { /// Create context with precision and rounding mode pub fn new(precision: NonZeroU64, rounding: RoundingMode) -> Self { Context { precision: precision, rounding: rounding, } } /// Copy context with new precision value pub fn with_precision(&self, precision: NonZeroU64) -> Self { Self { precision: precision, ..*self } } /// Copy context with new precision value pub fn with_prec(&self, precision: T) -> Option { precision .to_u64() .and_then(NonZeroU64::new) .map(|prec| { Self { precision: prec, ..*self } }) } /// Copy context with new rounding mode pub fn with_rounding_mode(&self, mode: RoundingMode) -> Self { Self { rounding: mode, ..*self } } /// Return maximum precision pub fn precision(&self) -> NonZeroU64 { self.precision } /// Return rounding mode pub fn rounding_mode(&self) -> RoundingMode { self.rounding } /// Round decimal to precision in this context, using rounding-mode pub fn round_decimal(&self, n: BigDecimal) -> BigDecimal { n.with_precision_round(self.precision(), self.rounding_mode()) } /// Round decimal to precision in this context, using rounding-mode pub fn round_decimal_ref<'a, D: Into>>(&self, n: D) -> BigDecimal { let d = n.into().to_owned(); d.with_precision_round(self.precision(), self.rounding_mode()) } /// Round digits x and y with the rounding mode #[allow(dead_code)] pub(crate) fn round_pair(&self, sign: Sign, x: u8, y: u8, trailing_zeros: bool) -> u8 { self.rounding.round_pair(sign, (x, y), trailing_zeros) } /// Round digits x and y with the rounding mode #[allow(dead_code)] pub(crate) fn round_pair_with_carry( &self, sign: Sign, x: u8, y: u8, trailing_zeros: bool, carry: &mut u8, ) -> u8 { self.rounding.round_pair_with_carry(sign, (x, y), trailing_zeros, carry) } } impl stdlib::default::Default for Context { fn default() -> Self { Self { precision: NonZeroU64::new(DEFAULT_PRECISION).unwrap(), rounding: RoundingMode::default(), } } } impl Context { /// Add two big digit references pub fn add_refs<'a, 'b, A, B>(&self, a: A, b: B) -> BigDecimal where A: Into>, B: Into>, { let mut sum = BigDecimal::zero(); self.add_refs_into(a, b, &mut sum); sum } /// Add two decimal refs, storing value in dest pub fn add_refs_into<'a, 'b, A, B>(&self, a: A, b: B, dest: &mut BigDecimal) where A: Into>, B: Into>, { let sum = a.into() + b.into(); *dest = sum.with_precision_round(self.precision, self.rounding) } } #[cfg(test)] mod test_context { use super::*; #[test] fn contstructor_and_setters() { let ctx = Context::default(); let c = ctx.with_prec(44).unwrap(); assert_eq!(c.precision.get(), 44); assert_eq!(c.rounding, RoundingMode::HalfEven); let c = c.with_rounding_mode(RoundingMode::Down); assert_eq!(c.precision.get(), 44); assert_eq!(c.rounding, RoundingMode::Down); } #[test] fn sum_two_references() { use stdlib::ops::Neg; let ctx = Context::default(); let a: BigDecimal = "209682.134972197168613072130300".parse().unwrap(); let b: BigDecimal = "3.0782968222271332463325639E-12".parse().unwrap(); let sum = ctx.add_refs(&a, &b); assert_eq!(sum, "209682.1349721971716913689525271332463325639".parse().unwrap()); // make negative copy of b without cloning values let neg_b = b.to_ref().neg(); let sum = ctx.add_refs(&a, neg_b); assert_eq!(sum, "209682.1349721971655347753080728667536674361".parse().unwrap()); let sum = ctx.with_prec(27).unwrap().with_rounding_mode(RoundingMode::Up).add_refs(&a, neg_b); assert_eq!(sum, "209682.134972197165534775309".parse().unwrap()); } mod round_decimal_ref { use super::*; #[test] fn case_bigint_1234567_prec3() { let ctx = Context::default().with_prec(3).unwrap(); let i = BigInt::from(1234567); let d = ctx.round_decimal_ref(&i); assert_eq!(d.int_val, 123.into()); assert_eq!(d.scale, -4); } #[test] fn case_bigint_1234500_prec4_halfup() { let ctx = Context::default() .with_prec(4).unwrap() .with_rounding_mode(RoundingMode::HalfUp); let i = BigInt::from(1234500); let d = ctx.round_decimal_ref(&i); assert_eq!(d.int_val, 1235.into()); assert_eq!(d.scale, -3); } #[test] fn case_bigint_1234500_prec4_halfeven() { let ctx = Context::default() .with_prec(4).unwrap() .with_rounding_mode(RoundingMode::HalfEven); let i = BigInt::from(1234500); let d = ctx.round_decimal_ref(&i); assert_eq!(d.int_val, 1234.into()); assert_eq!(d.scale, -3); } #[test] fn case_bigint_1234567_prec10() { let ctx = Context::default().with_prec(10).unwrap(); let i = BigInt::from(1234567); let d = ctx.round_decimal_ref(&i); assert_eq!(d.int_val, 1234567000.into()); assert_eq!(d.scale, 3); } } } bigdecimal-0.4.7/src/impl_cmp.rs000064400000000000000000000363611046102023000146620ustar 00000000000000//! Implementation of comparison operations //! //! Comparisons between decimals and decimal refs //! are not directly supported as we lose some type //! inference features at the savings of a single //! '&' character. //! //! &BigDecimal and BigDecimalRef are comparable. //! use crate::*; use stdlib::cmp::Ordering; use stdlib::iter; impl PartialEq for BigDecimal { fn eq(&self, rhs: &BigDecimal) -> bool { self.to_ref() == rhs.to_ref() } } impl<'rhs, T> PartialEq for BigDecimalRef<'_> where T: Into> + Copy { fn eq(&self, rhs: &T) -> bool { let rhs: BigDecimalRef<'rhs> = (*rhs).into(); check_equality_bigdecimal_ref(*self, rhs) } } fn check_equality_bigdecimal_ref(lhs: BigDecimalRef, rhs: BigDecimalRef) -> bool { match (lhs.sign(), rhs.sign()) { // both zero (Sign::NoSign, Sign::NoSign) => return true, // signs are different (a, b) if a != b => return false, // signs are same, do nothing _ => {} } let unscaled_int; let scaled_int; let trailing_zero_count; match arithmetic::checked_diff(lhs.scale, rhs.scale) { (Ordering::Equal, _) => { return lhs.digits == rhs.digits; } (Ordering::Greater, Some(scale_diff)) => { unscaled_int = lhs.digits; scaled_int = rhs.digits; trailing_zero_count = scale_diff; } (Ordering::Less, Some(scale_diff)) => { unscaled_int = rhs.digits; scaled_int = lhs.digits; trailing_zero_count = scale_diff; } _ => { // all other cases imply overflow in difference of scale, // numbers must not be equal return false; } } debug_assert_ne!(trailing_zero_count, 0); // test if unscaled_int is guaranteed to be less than // scaled_int*10^trailing_zero_count based on highest bit if highest_bit_lessthan_scaled(unscaled_int, scaled_int, trailing_zero_count) { return false; } // try compare without allocating if trailing_zero_count < 20 { let pow = ten_to_the_u64(trailing_zero_count as u8); let mut a_digits = unscaled_int.iter_u32_digits(); let mut b_digits = scaled_int.iter_u32_digits(); let mut carry = 0; loop { match (a_digits.next(), b_digits.next()) { (Some(next_a), Some(next_b)) => { let wide_b = match (next_b as u64).checked_mul(pow) { Some(tmp) => tmp + carry, None => break, }; let true_b = wide_b as u32; if next_a != true_b { return false; } carry = wide_b >> 32; } (None, Some(_)) => { return false; } (Some(a_digit), None) => { if a_digit != (carry as u32) { return false } carry = 0; } (None, None) => { return carry == 0; } } } // we broke out of loop due to overflow - compare via allocation let scaled_int = scaled_int * pow; return &scaled_int == unscaled_int; } let trailing_zero_count = trailing_zero_count.to_usize().unwrap(); let unscaled_digits = unscaled_int.to_radix_le(10); if trailing_zero_count > unscaled_digits.len() { return false; } // split into digits below the other value, and digits overlapping let (low_digits, overlap_digits) = unscaled_digits.split_at(trailing_zero_count); // if any of the low digits are zero, they are not equal if low_digits.iter().any(|&d| d != 0) { return false; } let scaled_digits = scaled_int.to_radix_le(10); // different lengths with trailing zeros if overlap_digits.len() != scaled_digits.len() { return false; } // return true if all digits are the same overlap_digits.iter().zip(scaled_digits.iter()).all(|(digit_a, digit_b)| digit_a == digit_b) } impl PartialOrd for BigDecimal { #[inline] fn partial_cmp(&self, other: &BigDecimal) -> Option { Some(self.cmp(other)) } } impl PartialOrd for BigDecimalRef<'_> { fn partial_cmp(&self, other: &BigDecimalRef<'_>) -> Option { Some(self.cmp(other)) } } impl Ord for BigDecimal { #[inline] fn cmp(&self, other: &BigDecimal) -> Ordering { self.to_ref().cmp(&other.to_ref()) } } impl Ord for BigDecimalRef<'_> { /// Complete ordering implementation for BigDecimal /// /// # Example /// /// ``` /// use std::str::FromStr; /// /// let a = bigdecimal::BigDecimal::from_str("-1").unwrap(); /// let b = bigdecimal::BigDecimal::from_str("1").unwrap(); /// assert!(a < b); /// assert!(b > a); /// let c = bigdecimal::BigDecimal::from_str("1").unwrap(); /// assert!(b >= c); /// assert!(c >= b); /// let d = bigdecimal::BigDecimal::from_str("10.0").unwrap(); /// assert!(d > c); /// let e = bigdecimal::BigDecimal::from_str(".5").unwrap(); /// assert!(e < c); /// ``` #[inline] fn cmp(&self, other: &BigDecimalRef) -> Ordering { use Ordering::*; let scmp = self.sign().cmp(&other.sign()); if scmp != Ordering::Equal { return scmp; } if self.sign() == Sign::NoSign { return Ordering::Equal; } let result = match arithmetic::checked_diff(self.scale, other.scale) { (Greater, Some(scale_diff)) | (Equal, Some(scale_diff)) => { compare_scaled_biguints(self.digits, other.digits, scale_diff) } (Less, Some(scale_diff)) => { compare_scaled_biguints(other.digits, self.digits, scale_diff).reverse() } (res, None) => { // The difference in scale does not fit in a u64, // we can safely assume the value of digits do not matter // (unless we have a 2^64 (i.e. ~16 exabyte) long number // larger scale means smaller number, reverse this ordering res.reverse() } }; if other.sign == Sign::Minus { result.reverse() } else { result } } } /// compare scaled uints: a <=> b * 10^{scale_diff} /// fn compare_scaled_biguints(a: &BigUint, b: &BigUint, scale_diff: u64) -> Ordering { use Ordering::*; if scale_diff == 0 { return a.cmp(b); } // check if highest bit of a is less than b * 10^scale_diff if highest_bit_lessthan_scaled(a, b, scale_diff) { return Ordering::Less; } // if biguints fit it u64 or u128, compare using those (avoiding allocations) if let Some(result) = compare_scalar_biguints(a, b, scale_diff) { return result; } let a_digit_count = count_decimal_digits_uint(a); let b_digit_count = count_decimal_digits_uint(b); let digit_count_cmp = a_digit_count.cmp(&(b_digit_count + scale_diff)); if digit_count_cmp != Equal { return digit_count_cmp; } let a_digits = a.to_radix_le(10); let b_digits = b.to_radix_le(10); debug_assert_eq!(a_digits.len(), a_digit_count as usize); debug_assert_eq!(b_digits.len(), b_digit_count as usize); let mut a_it = a_digits.iter().rev(); let mut b_it = b_digits.iter().rev(); loop { match (a_it.next(), b_it.next()) { (Some(ai), Some(bi)) => { match ai.cmp(bi) { Equal => continue, result => return result, } } (Some(&ai), None) => { if ai == 0 && a_it.all(Zero::is_zero) { return Equal; } else { return Greater; } } (None, Some(&bi)) => { if bi == 0 && b_it.all(Zero::is_zero) { return Equal; } else { return Less; } } (None, None) => { return Equal; } } } } /// Try fitting biguints into primitive integers, using those for ordering if possible fn compare_scalar_biguints(a: &BigUint, b: &BigUint, scale_diff: u64) -> Option { let scale_diff = scale_diff.to_usize()?; // try u64, then u128 compare_scaled_uints::(a, b, scale_diff) .or_else(|| compare_scaled_uints::(a, b, scale_diff)) } /// Implementation comparing biguints cast to generic type fn compare_scaled_uints<'a, T>(a: &'a BigUint, b: &'a BigUint, scale_diff: usize) -> Option where T: num_traits::PrimInt + TryFrom<&'a BigUint> { let ten = T::from(10).unwrap(); let a = T::try_from(a).ok(); let b = T::try_from(b).ok().and_then( |b| num_traits::checked_pow(ten, scale_diff).and_then( |p| b.checked_mul(&p))); match (a, b) { (Some(a), Some(scaled_b)) => Some(a.cmp(&scaled_b)), // if scaled_b doesn't fit in size T, while 'a' does, then a is certainly less (Some(_), None) => Some(Ordering::Less), // if a doesn't fit in size T, while 'scaled_b' does, then a is certainly greater (None, Some(_)) => Some(Ordering::Greater), // neither fits, cannot determine relative size (None, None) => None, } } /// Return highest_bit(a) < highest_bit(b * 10^{scale}) /// /// Used for optimization when comparing scaled integers /// /// ```math /// a < b * 10^{scale} /// log(a) < log(b) + scale * log(10) /// ``` /// fn highest_bit_lessthan_scaled(a: &BigUint, b: &BigUint, scale: u64) -> bool { let a_bits = a.bits(); let b_bits = b.bits(); if a_bits < b_bits { return true; } let log_scale = LOG2_10 * scale as f64; match b_bits.checked_add(log_scale as u64) { Some(scaled_b_bit) => a_bits < scaled_b_bit, None => true, // overflowing u64 means we are definitely bigger } } #[cfg(test)] mod test { use super::*; mod compare_scaled_biguints { use super::*; macro_rules! impl_test { ($name:ident: $a:literal > $b:literal e $e:literal) => { impl_test!($name: $a Greater $b e $e); }; ($name:ident: $a:literal < $b:literal e $e:literal) => { impl_test!($name: $a Less $b e $e); }; ($name:ident: $a:literal = $b:literal e $e:literal) => { impl_test!($name: $a Equal $b e $e); }; ($name:ident: $a:literal $op:ident $b:literal e $e:literal) => { #[test] fn $name() { let a: BigUint = $a.parse().unwrap(); let b: BigUint = $b.parse().unwrap(); let result = compare_scaled_biguints(&a, &b, $e); assert_eq!(result, Ordering::$op); } }; } impl_test!(case_500_51e1: "500" < "51" e 1); impl_test!(case_500_44e1: "500" > "44" e 1); impl_test!(case_5000_50e2: "5000" = "50" e 2); impl_test!(case_1234e9_12345e9: "1234000000000" < "12345" e 9); 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::*; macro_rules! impl_test { ($name:ident: $a:literal < $b:literal) => { #[test] fn $name() { let a: BigDecimal = $a.parse().unwrap(); let b: BigDecimal = $b.parse().unwrap(); assert!(&a < &b); assert!(&b > &a); assert_ne!(a, b); } }; } impl_test!(case_diff_signs: "-1" < "1"); impl_test!(case_n1_0: "-1" < "0"); impl_test!(case_0_1: "0" < "1"); impl_test!(case_1d2345_1d2346: "1.2345" < "1.2346"); impl_test!(case_compare_extreme: "1e-9223372036854775807" < "1"); impl_test!(case_compare_extremes: "1e-9223372036854775807" < "1e9223372036854775807"); impl_test!(case_small_difference: "472697816888807260.1604" < "472697816888807260.16040000000000000000001"); impl_test!(case_very_small_diff: "-1.0000000000000000000000000000000000000000000000000001" < "-1"); impl_test!(case_1_2p128: "1" < "340282366920938463463374607431768211455"); impl_test!(case_1_1e39: "1000000000000000000000000000000000000000" < "1e41"); impl_test!(case_1d414xxx573: "1.414213562373095048801688724209698078569671875376948073176679730000000000000000000000000000000000000" < "1.41421356237309504880168872420969807856967187537694807317667974000000000"); impl_test!(case_11d414xxx573: "1.414213562373095048801688724209698078569671875376948073176679730000000000000000000000000000000000000" < "11.41421356237309504880168872420969807856967187537694807317667974000000000"); } mod eq { use super::*; macro_rules! impl_test { ($name:ident: $a:literal = $b:literal) => { #[test] fn $name() { let a: BigDecimal = $a.parse().unwrap(); let b: BigDecimal = $b.parse().unwrap(); assert_eq!(&a, &b); assert_eq!(a, b); } }; } impl_test!(case_zero: "0" = "0.00"); impl_test!(case_1_1d00: "1" = "1.00"); impl_test!(case_n1_n1000en3: "-1" = "-1000e-3"); impl_test!(case_0d000034500_345en7: "0.000034500" = "345e-7"); } #[test] fn test_borrow_neg_cmp() { let x: BigDecimal = "1514932018891593.916341142773".parse().unwrap(); let y: BigDecimal = "1514932018891593916341142773e-12".parse().unwrap(); assert_eq!(x, y); let x_ref = x.to_ref(); assert_eq!(x_ref, &y); assert_ne!(x_ref.neg(), x_ref); assert_eq!(x_ref.neg().neg(), x_ref); } #[cfg(property_tests)] mod prop { use super::*; use proptest::prelude::*; proptest! { #![proptest_config(ProptestConfig { cases: 5000, ..Default::default() })] #[test] fn cmp_matches_f64( f in proptest::num::f64::NORMAL | proptest::num::f64::SUBNORMAL | proptest::num::f64::ZERO, g in proptest::num::f64::NORMAL | proptest::num::f64::SUBNORMAL | proptest::num::f64::ZERO ) { let a: BigDecimal = BigDecimal::from_f64(f).unwrap(); let b: BigDecimal = BigDecimal::from_f64(g).unwrap(); let expected = PartialOrd::partial_cmp(&f, &g).unwrap(); let value = a.cmp(&b); prop_assert_eq!(expected, value) } } } } bigdecimal-0.4.7/src/impl_convert.rs000064400000000000000000000034341046102023000155560ustar 00000000000000//! Code for implementing From/To BigDecimals use crate::BigDecimal; use crate::stdlib::convert::TryFrom; use num_bigint::BigInt; macro_rules! impl_from_int_primitive { ($t:ty) => { impl From<$t> for BigDecimal { fn from(n: $t) -> Self { BigDecimal { int_val: n.into(), scale: 0, } } } impl From<&$t> for BigDecimal { fn from(n: &$t) -> Self { BigDecimal { int_val: (*n).into(), scale: 0, } } } }; } impl_from_int_primitive!(u8); impl_from_int_primitive!(u16); impl_from_int_primitive!(u32); impl_from_int_primitive!(u64); impl_from_int_primitive!(u128); impl_from_int_primitive!(i8); impl_from_int_primitive!(i16); impl_from_int_primitive!(i32); impl_from_int_primitive!(i64); impl_from_int_primitive!(i128); impl TryFrom for BigDecimal { type Error = super::ParseBigDecimalError; #[inline] fn try_from(n: f32) -> Result { crate::parsing::try_parse_from_f32(n) } } impl TryFrom for BigDecimal { type Error = super::ParseBigDecimalError; #[inline] fn try_from(n: f64) -> Result { crate::parsing::try_parse_from_f64(n) } } impl From for BigDecimal { fn from(int_val: BigInt) -> Self { BigDecimal { int_val: int_val, scale: 0, } } } // Anything that may be a big-integer paired with a scale // parameter may be a bigdecimal impl> From<(T, i64)> for BigDecimal { fn from((int_val, scale): (T, i64)) -> Self { Self { int_val: int_val.into(), scale: scale, } } } bigdecimal-0.4.7/src/impl_fmt.rs000064400000000000000000001452761046102023000146770ustar 00000000000000//! Implementation of std::fmt traits & other stringification functions //! use crate::*; use rounding::{NonDigitRoundingData, InsigData}; use stdlib::fmt::Write; use stdlib::num::NonZeroUsize; // const EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD: usize = ${RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD} or 5; // const EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD: usize = ${RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD} or 15; // const FMT_MAX_INTEGER_PADDING: usize = = ${RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING} or 1000; include!(concat!(env!("OUT_DIR"), "/exponential_format_threshold.rs")); impl fmt::Display for BigDecimal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { dynamically_format_decimal( self.to_ref(), f, EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD, EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD, ) } } impl fmt::Display for BigDecimalRef<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { dynamically_format_decimal( *self, f, EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD, EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD, ) } } impl fmt::LowerExp for BigDecimal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::LowerExp::fmt(&self.to_ref(), f) } } impl fmt::LowerExp for BigDecimalRef<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let abs_int = self.digits.to_str_radix(10); format_exponential(*self, f, abs_int, "e") } } impl fmt::UpperExp for BigDecimal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::UpperExp::fmt(&self.to_ref(), f) } } impl fmt::UpperExp for BigDecimalRef<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let abs_int = self.digits.to_str_radix(10); format_exponential(*self, f, abs_int, "E") } } impl fmt::Debug for BigDecimal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if f.alternate() { write!(f, "BigDecimal(\"{}e{:}\")", self.int_val, -self.scale) } else { write!(f, "BigDecimal(sign={:?}, scale={}, digits={:?})", self.sign(), self.scale, self.int_val.magnitude().to_u64_digits() ) } } } fn dynamically_format_decimal( this: BigDecimalRef, f: &mut fmt::Formatter, leading_zero_threshold: usize, trailing_zero_threshold: usize, ) -> fmt::Result { // Acquire the absolute integer as a decimal string let abs_int = this.digits.to_str_radix(10); // number of zeros between most significant digit and decimal point let leading_zero_count = this.scale .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_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, // 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 format_dotless_exponential(f, abs_int, this.sign, this.scale, "e") } else { format_full_scale(this, f, abs_int) } } 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, abs_int: String, ) -> fmt::Result { use stdlib::cmp::Ordering::*; let mut digits = abs_int.into_bytes(); let mut exp = 0; let non_negative = matches!(this.sign, Sign::Plus | Sign::NoSign); debug_assert_ne!(digits.len(), 0); let rounder = NonDigitRoundingData::default_with_sign(this.sign); if this.scale <= 0 { exp = (this.scale as i128).neg(); // format an integer value by adding trailing zeros to the right zero_right_pad_integer_ascii_digits(&mut digits, &mut exp, f.precision()); } else { let scale = this.scale as u64; // std::fmt 'precision' has same meaning as bigdecimal 'scale' // // interpret 'no-precision' to mean the same as matching the scale // of the deicmal (i.e. no padding or rounding) let target_scale = f.precision().and_then(|prec| prec.to_u64()).unwrap_or(scale); if scale < digits.len() as u64 { // format both integer and fractional digits (always 'trim' to precision) format_ascii_digits_with_integer_and_fraction(&mut digits, scale, target_scale, rounder); } else { // format only fractional digits format_ascii_digits_no_integer(&mut digits, scale, target_scale, rounder); } } // move digits back into String form let mut buf = String::from_utf8(digits).unwrap(); // add exp part to buffer (if not zero) if exp != 0 { write!(buf, "e{:+}", exp)?; } // write buffer to formatter f.pad_integral(non_negative, "", &buf) } /// Fill appropriate number of zeros and decimal point into Vec of (ascii/utf-8) digits /// /// Exponent is set to zero if zeros were added /// fn zero_right_pad_integer_ascii_digits( digits: &mut Vec, exp: &mut i128, // number of zeros after the decimal point target_scale: Option, ) { debug_assert!(*exp >= 0); debug_assert_ne!(digits.len(), 0); let integer_zero_count = match exp.to_usize() { Some(n) => n, None => { return; } }; // did not explicitly request precision, so we'll only // implicitly right-pad if less than this threshold. if target_scale.is_none() && integer_zero_count > 20 { // no padding return; } let fraction_zero_char_count; let decimal_place_idx; if let Some(frac_zero_count) = target_scale.and_then(NonZeroUsize::new) { // add one char for '.' if target_scale is not zero fraction_zero_char_count = frac_zero_count.get() + 1; // indicate we'll need to add a decimal point decimal_place_idx = Some(digits.len() + integer_zero_count); } else { fraction_zero_char_count = 0; decimal_place_idx = None; } let total_additional_zeros = integer_zero_count.saturating_add(fraction_zero_char_count); // no padding if out of bounds if total_additional_zeros > FMT_MAX_INTEGER_PADDING { return; } digits.resize(digits.len() + total_additional_zeros, b'0'); if let Some(decimal_place_idx) = decimal_place_idx { digits[decimal_place_idx] = b'.'; } // set exp to zero so it won't be written in `format_full_scale` *exp = 0; } /// Insert decimal point into digits_ascii_be, rounding or padding with zeros when necessary /// /// (digits_ascii_be, scale) represents a decimal with both integer and fractional digits. /// fn format_ascii_digits_with_integer_and_fraction( digits_ascii_be: &mut Vec, scale: u64, target_scale: u64, rounder: NonDigitRoundingData, ) { debug_assert!(scale < digits_ascii_be.len() as u64, "No integer digits"); let mut digit_scale = scale; // decimal has more fractional digits than requested: round (trimming insignificant digits) if target_scale < scale { let digit_count_to_remove = (scale - target_scale) .to_usize() .expect("Precision exceeds maximum usize"); let rounding_idx = NonZeroUsize::new(digits_ascii_be.len() - digit_count_to_remove) .expect("decimal must have integer digits"); // round and trim the digits at the 'rounding index' let scale_diff = round_ascii_digits(digits_ascii_be, rounding_idx, rounder); match scale_diff.checked_sub(digit_scale as usize) { None | Some(0) => { digit_scale -= scale_diff as u64; } Some(zeros_to_add) => { digits_ascii_be.resize(digits_ascii_be.len() + zeros_to_add, b'0'); digit_scale = 0; } } } // ignore the decimal point if the target scale is zero if target_scale != 0 { // there are both integer and fractional digits let integer_digit_count = (digits_ascii_be.len() as u64 - digit_scale) .to_usize() .expect("Number of digits exceeds maximum usize"); digits_ascii_be.insert(integer_digit_count, b'.'); } if digit_scale < target_scale { let trailing_zero_count = (target_scale - digit_scale) .to_usize() .expect("Too Big"); // precision required beyond scale digits_ascii_be.resize(digits_ascii_be.len() + trailing_zero_count, b'0'); digit_scale += trailing_zero_count as u64; } debug_assert_eq!(digit_scale, target_scale); } /// Insert decimal point into digits_ascii_be, rounding or padding with zeros when necessary /// /// (digits_ascii_be, scale) represents a decimal with only fractional digits. /// fn format_ascii_digits_no_integer( digits_ascii_be: &mut Vec, scale: u64, target_scale: u64, rounder: NonDigitRoundingData, ) { use stdlib::cmp::Ordering::*; debug_assert!(scale >= digits_ascii_be.len() as u64); let leading_zeros = scale - digits_ascii_be.len() as u64; match arithmetic::diff(target_scale, leading_zeros) { // handle rounding point before the start of digits (Less, intermediate_zeros) | (Equal, intermediate_zeros) => { // get insignificant digit let (insig_digit, trailing_digits) = if intermediate_zeros > 0 { (0, digits_ascii_be.as_slice()) } else { (digits_ascii_be[0] - b'0', &digits_ascii_be[1..]) }; let insig_data = InsigData::from_digit_and_lazy_trailing_zeros( rounder, insig_digit, || trailing_digits.iter().all(|&d| d == b'0') ); let rounded_digit = insig_data.round_digit(0); debug_assert_ne!(rounded_digit, 10); digits_ascii_be.clear(); if target_scale > 0 { digits_ascii_be.resize(target_scale as usize + 1, b'0'); } digits_ascii_be.push(rounded_digit + b'0'); if target_scale > 0 { digits_ascii_be[1] = b'.'; } } (Greater, sig_digit_count) => { let significant_digit_count = sig_digit_count .to_usize() .and_then(NonZeroUsize::new) .expect("Request overflow in sig_digit_count"); let mut digit_scale = scale; // if 'digits_ascii_be' has more digits than requested, round if significant_digit_count.get() < digits_ascii_be.len() { let removed_digit_count = round_ascii_digits( digits_ascii_be, significant_digit_count, rounder ); digit_scale -= removed_digit_count as u64; } // number of zeros to keep after the significant digits let trailing_zeros = target_scale - digit_scale; // expected length is target scale (number of digits after decimal point) + "0." let dest_len = target_scale as usize + 2; // number of significant digits is whatever is left in the digit vector let sig_digit_count = digits_ascii_be.len(); // index where to store significant digits let sig_digit_idx = dest_len - trailing_zeros as usize - sig_digit_count; // fill output with zeros digits_ascii_be.resize(dest_len, b'0'); // very likely case where there are digits after the decimal point if digit_scale != 0 { // copy significant digits to their index location digits_ascii_be.copy_within(..sig_digit_count, sig_digit_idx); // clear copied values fill_slice(&mut digits_ascii_be[..sig_digit_count.min(sig_digit_idx)], b'0'); } else { debug_assert_eq!(sig_digit_count, 1); } // add decimal point digits_ascii_be[1] = b'.'; } } } #[cfg(rust_1_50)] fn fill_slice(v: &mut [T], value: T) { v.fill(value); } #[cfg(not(rust_1_50))] fn fill_slice(v: &mut [T], value: T) { for i in v.iter_mut() { *i = value.clone(); } } /// Format integer as {int}e+{exp} /// /// Slightly different than scientific notation, /// fn format_dotless_exponential( f: &mut fmt::Formatter, mut abs_int: String, sign: Sign, scale: i64, e_symbol: &str, ) -> fmt::Result { debug_assert!(scale <= 0); write!(abs_int, "{}{:+}", e_symbol, -scale).unwrap(); let non_negative = matches!(sign, Sign::Plus | Sign::NoSign); f.pad_integral(non_negative, "", &abs_int) } fn format_exponential( this: BigDecimalRef, f: &mut fmt::Formatter, abs_int: String, e_symbol: &str, ) -> fmt::Result { // Steps: // 1. Truncate integer based on precision // 2. calculate exponent from the scale and the length of the internal integer // 3. Place decimal point after a single digit of the number, or omit if there is only a single digit // 4. Append `E{exponent}` and format the resulting string based on some `Formatter` flags let exp = (this.scale as i128).neg(); let digits = abs_int.into_bytes(); format_exponential_bigendian_ascii_digits( digits, this.sign, exp, f, e_symbol ) } fn format_exponential_bigendian_ascii_digits( mut digits: Vec, sign: Sign, mut exp: i128, f: &mut fmt::Formatter, e_symbol: &str, ) -> fmt::Result { // how many zeros to pad at the end of the decimal let mut extra_trailing_zero_count = 0; if let Some(prec) = f.precision() { // 'prec' is number of digits after the decimal point let total_prec = prec + 1; if total_prec < digits.len() { // round to smaller precision let rounder = NonDigitRoundingData::default_with_sign(sign); let target_scale = NonZeroUsize::new(total_prec).unwrap(); let delta_exp = round_ascii_digits(&mut digits, target_scale, rounder); exp += delta_exp as i128; } extra_trailing_zero_count = total_prec - digits.len(); } let needs_decimal_point = digits.len() > 1 || extra_trailing_zero_count > 0; let mut abs_int = String::from_utf8(digits).unwrap(); // Determine the exponent value based on the scale // // # First case: the integer representation falls completely behind the // decimal point. // // Example of this.scale > abs_int.len(): // 0.000001234509876 // abs_int.len() = 10 // scale = 15 // target is 1.234509876 // exponent = -6 // // Example of this.scale == abs_int.len(): // 0.333333333333333314829616256247390992939472198486328125 // abs_int.len() = 54 // scale = 54 // target is 3.33333333333333314829616256247390992939472198486328125 // exponent = -1 // // # Second case: the integer representation falls around, or before the // decimal point // // ## Case 2.1, entirely before the decimal point. // Example of (abs_int.len() - this.scale) > abs_int.len(): // 123450987600000 // abs_int.len() = 10 // scale = -5 // location = 15 // target is 1.234509876 // exponent = 14 // // ## Case 2.2, somewhere around the decimal point. // Example of (abs_int.len() - this.scale) < abs_int.len(): // 12.339999999999999857891452847979962825775146484375 // abs_int.len() = 50 // scale = 48 // target is 1.2339999999999999857891452847979962825775146484375 // exponent = 1 // // For the (abs_int.len() - this.scale) == abs_int.len() I couldn't // come up with an example let exponent = abs_int.len() as i128 + exp - 1; if needs_decimal_point { // only add decimal point if there is more than 1 decimal digit abs_int.insert(1, '.'); } if extra_trailing_zero_count > 0 { abs_int.extend(stdlib::iter::repeat('0').take(extra_trailing_zero_count)); } // always print exponent in exponential mode write!(abs_int, "{}{:+}", e_symbol, exponent)?; let non_negative = matches!(sign, Sign::Plus | Sign::NoSign); //pad_integral does the right thing although we have a decimal f.pad_integral(non_negative, "", &abs_int) } /// Round big-endian ascii digits to given significant digit count, /// updating the scale appropriately /// /// Returns the number of digits removed; this should be treated as the /// change in the decimal's scale, and should be subtracted from the scale /// when appropriate. /// fn round_ascii_digits( // bigendian ascii digits digits_ascii_be: &mut Vec, // number of significant digits to keep significant_digit_count: NonZeroUsize, // how to round rounder: NonDigitRoundingData, ) -> usize { debug_assert!(significant_digit_count.get() < digits_ascii_be.len()); let (sig_digits, insig_digits) = digits_ascii_be.split_at(significant_digit_count.get()); let (&insig_digit, trailing_digits) = insig_digits.split_first().unwrap_or((&b'0', &[])); let insig_data = InsigData::from_digit_and_lazy_trailing_zeros( rounder, insig_digit - b'0', || trailing_digits.iter().all(|&d| d == b'0') ); let rounding_digit_pos = significant_digit_count.get() - 1; let sig_digit = sig_digits[rounding_digit_pos] - b'0'; let rounded_digit = insig_data.round_digit(sig_digit); // record how many digits to remove (changes the 'scale') let mut removed_digit_count = insig_digits.len(); // discard insignificant digits (and rounding digit) digits_ascii_be.truncate(rounding_digit_pos); if rounded_digit < 10 { // simple case: no carrying/overflow, push rounded digit digits_ascii_be.push(rounded_digit + b'0'); return removed_digit_count; } // handle carrying the 1 debug_assert!(rounded_digit == 10); // carry the one past trailing 9's: replace them with zeros let next_non_nine_rev_pos = digits_ascii_be.iter().rev().position(|&d| d != b'9'); match next_non_nine_rev_pos { // number of nines to remove Some(backwards_nine_count) => { let digits_to_trim = backwards_nine_count + 1; let idx = digits_ascii_be.len() - digits_to_trim; // increment least significant non-nine zero digits_ascii_be[idx] += 1; // remove trailing nines digits_ascii_be.truncate(idx + 1); // count truncation removed_digit_count += digits_to_trim; } // all nines! overflow to 1.000 None => { digits_ascii_be.clear(); digits_ascii_be.push(b'1'); // count digit clearning removed_digit_count += significant_digit_count.get(); } } // return the number of removed digits return removed_digit_count; } #[inline(never)] pub(crate) fn write_scientific_notation(n: &BigDecimal, w: &mut W) -> fmt::Result { if n.is_zero() { return w.write_str("0e0"); } if n.int_val.sign() == Sign::Minus { w.write_str("-")?; } let digits = n.int_val.magnitude(); let dec_str = digits.to_str_radix(10); let (first_digit, remaining_digits) = dec_str.as_str().split_at(1); w.write_str(first_digit)?; if !remaining_digits.is_empty() { w.write_str(".")?; w.write_str(remaining_digits)?; } write!(w, "e{}", remaining_digits.len() as i128 - n.scale as i128) } #[inline(never)] pub(crate) fn write_engineering_notation(n: &BigDecimal, out: &mut W) -> fmt::Result { if n.is_zero() { return out.write_str("0e0"); } if n.int_val.sign() == Sign::Minus { out.write_char('-')?; } let digits = n.int_val.magnitude(); let dec_str = digits.to_str_radix(10); let digit_count = dec_str.len(); let top_digit_exponent = digit_count as i128 - n.scale as i128; let shift_amount = match top_digit_exponent.rem_euclid(3) { 0 => 3, i => i as usize, }; let exp = top_digit_exponent - shift_amount as i128; // handle adding zero padding if let Some(padding_zero_count) = shift_amount.checked_sub(dec_str.len()) { let zeros = &"000"[..padding_zero_count]; out.write_str(&dec_str)?; out.write_str(zeros)?; return write!(out, "e{}", exp); } let (head, rest) = dec_str.split_at(shift_amount); debug_assert_eq!(exp % 3, 0); out.write_str(head)?; if !rest.is_empty() { out.write_char('.')?; out.write_str(rest)?; } return write!(out, "e{}", exp); } #[cfg(test)] #[allow(non_snake_case)] mod test { use super::*; use paste::*; /// test case builder for mapping decimal-string to formatted-string /// define test_fmt_function! macro to test your function #[cfg(test)] macro_rules! impl_case { ($name:ident : $in:literal => $ex:literal) => { #[test] fn $name() { let n: BigDecimal = $in.parse().unwrap(); let s = test_fmt_function!(n); assert_eq!(&s, $ex); } }; } /// "Mock" Formatter /// /// Given callable, forwards formatter to callable. /// Required work-around due to lack of constructor in fmt::Formatter /// struct Fmt(F) where F: Fn(&mut fmt::Formatter) -> fmt::Result; impl fmt::Display for Fmt where F: Fn(&mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // call closure with given formatter (self.0)(f) } } impl fmt::Debug for Fmt where F: Fn(&mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (self.0)(f) } } mod dynamic_fmt { use super::*; macro_rules! test_fmt_function { ($n:ident) => {{ format!("{}", Fmt(|f| dynamically_format_decimal($n.to_ref(), f, 2, 9))) }}; } impl_case!(case_0d123: "0.123" => "0.123"); impl_case!(case_0d0123: "0.0123" => "0.0123"); impl_case!(case_0d00123: "0.00123" => "0.00123"); impl_case!(case_0d000123: "0.000123" => "1.23E-4"); impl_case!(case_123d: "123." => "123"); impl_case!(case_123de1: "123.e1" => "1230"); } mod fmt_options { use super::*; macro_rules! impl_case { ($name:ident: $fmt:literal => $expected:literal) => { #[test] fn $name() { let x = test_input(); let y = format!($fmt, x); assert_eq!(y, $expected); } }; } mod dec_1 { use super::*; fn test_input() -> BigDecimal { "1".parse().unwrap() } impl_case!(fmt_default: "{}" => "1"); impl_case!(fmt_d1: "{:.1}" => "1.0"); impl_case!(fmt_d4: "{:.4}" => "1.0000"); impl_case!(fmt_4d1: "{:4.1}" => " 1.0"); impl_case!(fmt_r4d1: "{:>4.1}" => " 1.0"); impl_case!(fmt_l4d1: "{:<4.1}" => "1.0 "); impl_case!(fmt_p05d1: "{:+05.1}" => "+01.0"); impl_case!(fmt_e: "{:e}" => "1e+0"); impl_case!(fmt_E: "{:E}" => "1E+0"); } mod dec_1e1 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(1.into(), -1) } impl_case!(fmt_default: "{}" => "10"); impl_case!(fmt_debug: "{:?}" => "BigDecimal(sign=Plus, scale=-1, digits=[1])"); impl_case!(fmt_debug_alt: "{:#?}" => "BigDecimal(\"1e1\")"); impl_case!(fmt_d0: "{:.0}" => "10"); impl_case!(fmt_d1: "{:.1}" => "10.0"); impl_case!(fmt_d2: "{:.2}" => "10.00"); } mod dec_1en1 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(1.into(), 1) } impl_case!(fmt_default: "{}" => "0.1"); impl_case!(fmt_d0: "{:.0}" => "0"); impl_case!(fmt_d1: "{:.1}" => "0.1"); impl_case!(fmt_d2: "{:.2}" => "0.10"); } mod dec_9en1 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(9.into(), 1) } impl_case!(fmt_default: "{}" => "0.9"); impl_case!(fmt_d0: "{:.0}" => "1"); impl_case!(fmt_d1: "{:.1}" => "0.9"); impl_case!(fmt_d4: "{:.4}" => "0.9000"); } mod dec_800en3 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(800.into(), 3) } impl_case!(fmt_default: "{}" => "0.800"); impl_case!(fmt_d0: "{:.0}" => "1"); impl_case!(fmt_d1: "{:.1}" => "0.8"); impl_case!(fmt_d3: "{:.3}" => "0.800"); impl_case!(fmt_d9: "{:.9}" => "0.800000000"); } mod dec_123456 { use super::*; fn test_input() -> BigDecimal { "123456".parse().unwrap() } impl_case!(fmt_default: "{}" => "123456"); impl_case!(fmt_d1: "{:.1}" => "123456.0"); impl_case!(fmt_d4: "{:.4}" => "123456.0000"); impl_case!(fmt_4d1: "{:4.1}" => "123456.0"); impl_case!(fmt_15d2: "{:15.2}" => " 123456.00"); impl_case!(fmt_r15d2: "{:>15.2}" => " 123456.00"); impl_case!(fmt_l15d2: "{:<15.2}" => "123456.00 "); impl_case!(fmt_p05d1: "{:+05.7}" => "+123456.0000000"); } mod dec_d099995 { use super::*; fn test_input() -> BigDecimal { ".099995".parse().unwrap() } impl_case!(fmt_default: "{}" => "0.099995"); impl_case!(fmt_d0: "{:.0}" => "0"); impl_case!(fmt_d1: "{:.1}" => "0.1"); impl_case!(fmt_d3: "{:.3}" => "0.100"); } mod dec_d9999999 { use super::*; fn test_input() -> BigDecimal { ".9999999".parse().unwrap() } impl_case!(fmt_default: "{}" => "0.9999999"); impl_case!(fmt_d0: "{:.0}" => "1"); impl_case!(fmt_d1: "{:.1}" => "1.0"); impl_case!(fmt_d3: "{:.3}" => "1.000"); } mod dec_9999999 { use super::*; fn test_input() -> BigDecimal { "9999999".parse().unwrap() } 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"); impl_case!(fmt_E: "{:E}" => "9.999999E+6"); impl_case!(fmt_d0e: "{:.0e}" => "1e+7"); impl_case!(fmt_d1e: "{:.1e}" => "1.0e+7"); impl_case!(fmt_d2e: "{:.2e}" => "1.00e+7"); impl_case!(fmt_d4e: "{:.4e}" => "1.0000e+7"); impl_case!(fmt_d6e: "{:.6e}" => "9.999999e+6"); impl_case!(fmt_d7e: "{:.7e}" => "9.9999990e+6"); impl_case!(fmt_d10e: "{:.10e}" => "9.9999990000e+6"); } mod dec_19073d97235939614856 { use super::*; fn test_input() -> BigDecimal { "19073.97235939614856".parse().unwrap() } impl_case!(fmt_default: "{}" => "19073.97235939614856"); impl_case!(fmt_pd7: "{:+.7}" => "+19073.9723594"); impl_case!(fmt_d0: "{:.0}" => "19074"); impl_case!(fmt_d1: "{:.1}" => "19074.0"); impl_case!(fmt_d3: "{:.3}" => "19073.972"); impl_case!(fmt_d4: "{:.4}" => "19073.9724"); impl_case!(fmt_8d3: "{:8.3}" => "19073.972"); impl_case!(fmt_10d3: "{:10.3}" => " 19073.972"); 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::*; fn test_input() -> BigDecimal { "-90037659.6905".parse().unwrap() } impl_case!(fmt_default: "{}" => "-90037659.6905"); impl_case!(fmt_debug: "{:?}" => "BigDecimal(sign=Minus, scale=4, digits=[900376596905])"); impl_case!(fmt_debug_alt: "{:#?}" => "BigDecimal(\"-900376596905e-4\")"); impl_case!(fmt_pd7: "{:+.7}" => "-90037659.6905000"); impl_case!(fmt_d0: "{:.0}" => "-90037660"); impl_case!(fmt_d3: "{:.3}" => "-90037659.690"); impl_case!(fmt_d4: "{:.4}" => "-90037659.6905"); impl_case!(fmt_14d4: "{:14.4}" => "-90037659.6905"); impl_case!(fmt_15d4: "{:15.4}" => " -90037659.6905"); impl_case!(fmt_l17d5: "{:<17.5}" => "-90037659.69050 "); } mod dec_0d0002394899999500 { use super::*; fn test_input() -> BigDecimal { "0.0002394899999500".parse().unwrap() } impl_case!(fmt_default: "{}" => "0.0002394899999500"); impl_case!(fmt_d0: "{:.0}" => "0"); impl_case!(fmt_d1: "{:.1}" => "0.0"); impl_case!(fmt_d3: "{:.3}" => "0.000"); impl_case!(fmt_d4: "{:.4}" => "0.0002"); impl_case!(fmt_d5: "{:.5}" => "0.00024"); impl_case!(fmt_d10: "{:.10}" => "0.0002394900"); impl_case!(fmt_d13: "{:.13}" => "0.0002394900000"); impl_case!(fmt_d14: "{:.14}" => "0.00023948999995"); impl_case!(fmt_d20: "{:.20}" => "0.00023948999995000000"); } mod dec_1764031078en13 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(1764031078.into(), 13) } impl_case!(fmt_default: "{}" => "0.0001764031078"); impl_case!(fmt_d1: "{:.1}" => "0.0"); impl_case!(fmt_d3: "{:.3}" => "0.000"); impl_case!(fmt_d4: "{:.4}" => "0.0002"); impl_case!(fmt_d5: "{:.5}" => "0.00018"); impl_case!(fmt_d13: "{:.13}" => "0.0001764031078"); impl_case!(fmt_d20: "{:.20}" => "0.00017640310780000000"); } mod dec_1e15 { use super::*; fn test_input() -> BigDecimal { "1e15".parse().unwrap() } impl_case!(fmt_default: "{}" => "1000000000000000"); impl_case!(fmt_d0: "{:.0}" => "1000000000000000"); impl_case!(fmt_d1: "{:.1}" => "1000000000000000.0"); } mod dec_1e16 { use super::*; fn test_input() -> BigDecimal { "1e16".parse().unwrap() } impl_case!(fmt_default: "{}" => "1e+16"); impl_case!(fmt_d0: "{:.0}" => "10000000000000000"); impl_case!(fmt_d2: "{:.2}" => "10000000000000000.00"); } mod dec_491326en12 { use super::*; fn test_input() -> BigDecimal { "491326e-12".parse().unwrap() } 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 { use super::*; fn test_input() -> BigDecimal { "0.00003102564500".parse().unwrap() } impl_case!(fmt_default: "{}" => "0.00003102564500"); impl_case!(fmt_d0: "{:.0}" => "0"); impl_case!(fmt_d1: "{:.1}" => "0.0"); impl_case!(fmt_d2: "{:.2}" => "0.00"); impl_case!(fmt_d4: "{:.4}" => "0.0000"); impl_case!(fmt_d5: "{:.5}" => "0.00003"); impl_case!(fmt_d10: "{:.10}" => "0.0000310256"); impl_case!(fmt_d14: "{:.14}" => "0.00003102564500"); impl_case!(fmt_d17: "{:.17}" => "0.00003102564500000"); impl_case!(fmt_e: "{:e}" => "3.102564500e-5"); impl_case!(fmt_de: "{:.e}" => "3.102564500e-5"); impl_case!(fmt_d0e: "{:.0e}" => "3e-5"); impl_case!(fmt_d1e: "{:.1e}" => "3.1e-5"); impl_case!(fmt_d4e: "{:.4e}" => "3.1026e-5"); } mod dec_1en100000 { use super::*; fn test_input() -> BigDecimal { "1E-10000".parse().unwrap() } impl_case!(fmt_default: "{}" => "1E-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 { use super::*; fn test_input() -> BigDecimal { "1e100000".parse().unwrap() } impl_case!(fmt_default: "{}" => "1e+100000"); impl_case!(fmt_d1: "{:.1}" => "1e+100000"); impl_case!(fmt_d4: "{:.4}" => "1e+100000"); } mod dec_1234506789E5 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(1234506789.into(), -5) } impl_case!(fmt_default: "{}" => "123450678900000"); impl_case!(fmt_d1: "{:.1}" => "123450678900000.0"); impl_case!(fmt_d3: "{:.3}" => "123450678900000.000"); impl_case!(fmt_d4: "{:.4}" => "123450678900000.0000"); impl_case!(fmt_l13d4: "{:<23.4}" => "123450678900000.0000 "); impl_case!(fmt_r13d4: "{:>23.4}" => " 123450678900000.0000"); } mod dec_1234506789E15 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(1234506789.into(), -15) } impl_case!(fmt_default: "{}" => "1234506789000000000000000"); impl_case!(fmt_d1: "{:.1}" => "1234506789000000000000000.0"); impl_case!(fmt_d3: "{:.3}" => "1234506789000000000000000.000"); impl_case!(fmt_l13d4: "{:<+32.2}" => "+1234506789000000000000000.00 "); impl_case!(fmt_r13d4: "{:>+32.2}" => " +1234506789000000000000000.00"); } mod dec_13400476439814628800E2502 { use super::*; fn test_input() -> BigDecimal { BigDecimal::new(13400476439814628800u64.into(), -2502) } impl_case!(fmt_default: "{}" => "13400476439814628800e+2502"); impl_case!(fmt_d1: "{:.1}" => "13400476439814628800e+2502"); } mod dec_d9999 { use super::*; fn test_input() -> BigDecimal { "0.9999".parse().unwrap() } impl_case!(fmt_default: "{}" => "0.9999"); impl_case!(fmt_d0: "{:.0}" => "1"); impl_case!(fmt_d1: "{:.1}" => "1.0"); impl_case!(fmt_d2: "{:.2}" => "1.00"); impl_case!(fmt_d3: "{:.3}" => "1.000"); impl_case!(fmt_d4: "{:.4}" => "0.9999"); impl_case!(fmt_d6: "{:.6}" => "0.999900"); } mod dec_9d99 { use super::*; fn test_input() -> BigDecimal { "9.99".parse().unwrap() } impl_case!(fmt_default: "{}" => "9.99"); impl_case!(fmt_d0: "{:.0}" => "10"); impl_case!(fmt_d1: "{:.1}" => "10.0"); impl_case!(fmt_d2: "{:.2}" => "9.99"); impl_case!(fmt_d3: "{:.3}" => "9.990"); } } mod fmt_boundaries { use super::*; macro_rules! impl_case { ( $name:ident: $src:expr => $expected:literal ) => { #[test] fn $name() { let src = $src; let bd: BigDecimal = src.parse().unwrap(); let result = bd.to_string(); assert_eq!(result, $expected); let round_trip = BigDecimal::from_str(&result).unwrap(); assert_eq!(round_trip, bd); let sci = bd.to_scientific_notation(); let sci_round_trip = BigDecimal::from_str(&sci).unwrap(); assert_eq!(sci_round_trip, bd); let eng = bd.to_engineering_notation(); let eng_round_trip = BigDecimal::from_str(&eng).unwrap(); assert_eq!(eng_round_trip, bd); } }; ( (eng-check-invalid) $name:ident: $src:expr => $expected:literal ) => { #[test] fn $name() { let src = $src; let bd: BigDecimal = src.parse().unwrap(); let result = bd.to_string(); assert_eq!(result, $expected); let round_trip = BigDecimal::from_str(&result).unwrap(); assert_eq!(round_trip, bd); let sci = bd.to_scientific_notation(); let sci_round_trip = BigDecimal::from_str(&sci).unwrap(); assert_eq!(sci_round_trip, bd); let eng = bd.to_engineering_notation(); let eng_round_trip = BigDecimal::from_str(&eng); assert!(eng_round_trip.is_err()); } }; ( (panics) $name:ident: $src:expr ) => { #[test] #[should_panic] fn $name() { let src = $src; let _bd: BigDecimal = src.parse().unwrap(); } }; } impl_case!(test_max: format!("1E{}", i64::MAX) => "1e+9223372036854775807"); impl_case!(test_max_multiple_digits: format!("314156E{}", i64::MAX) => "314156e+9223372036854775807"); impl_case!(test_min_scale: "1E9223372036854775807" => "1e+9223372036854775807"); impl_case!((eng-check-invalid) test_max_scale: "1E-9223372036854775807" => "1E-9223372036854775807"); impl_case!(test_min_multiple_digits: format!("271828182E-{}", i64::MAX) => "2.71828182E-9223372036854775799"); impl_case!((panics) test_max_exp_overflow: "1E9223372036854775809"); impl_case!((panics) test_min_exp_overflow: "1E-9223372036854775808"); } #[test] fn test_fmt() { let vals = vec![ // b s ( {} {:.1} {:.4} {:4.1} {:+05.1} {:<4.1} (1, 0, ( "1", "1.0", "1.0000", " 1.0", "+01.0", "1.0 " )), (1, 1, ( "0.1", "0.1", "0.1000", " 0.1", "+00.1", "0.1 " )), (1, 2, ( "0.01", "0.0", "0.0100", " 0.0", "+00.0", "0.0 " )), (1, -2, ( "100", "100.0", "100.0000", "100.0", "+100.0", "100.0" )), (-1, 0, ( "-1", "-1.0", "-1.0000", "-1.0", "-01.0", "-1.0" )), (-1, 1, ( "-0.1", "-0.1", "-0.1000", "-0.1", "-00.1", "-0.1" )), (-1, 2, ( "-0.01", "-0.0", "-0.0100", "-0.0", "-00.0", "-0.0" )), ]; for (i, scale, results) in vals { let x = BigDecimal::new(num_bigint::BigInt::from(i), scale); assert_eq!(format!("{}", x), results.0); assert_eq!(format!("{:.1}", x), results.1); assert_eq!(format!("{:.4}", x), results.2); assert_eq!(format!("{:4.1}", x), results.3); assert_eq!(format!("{:+05.1}", x), results.4); assert_eq!(format!("{:<4.1}", x), results.5); } } mod fmt_debug { use super::*; macro_rules! impl_case { ($name:ident: $input:literal => $expected:literal => $expected_alt:literal) => { paste! { #[test] fn $name() { let x: BigDecimal = $input.parse().unwrap(); let y = format!("{:?}", x); assert_eq!(y, $expected); } #[test] fn [< $name _alt >]() { let x: BigDecimal = $input.parse().unwrap(); let y = format!("{:#?}", x); assert_eq!(y, $expected_alt); } } } } impl_case!(case_0: "0" => r#"BigDecimal(sign=NoSign, scale=0, digits=[])"# => r#"BigDecimal("0e0")"#); impl_case!(case_n0: "-0" => r#"BigDecimal(sign=NoSign, scale=0, digits=[])"# => r#"BigDecimal("0e0")"#); impl_case!(case_1: "1" => r#"BigDecimal(sign=Plus, scale=0, digits=[1])"# => r#"BigDecimal("1e0")"#); impl_case!(case_123_400: "123.400" => r#"BigDecimal(sign=Plus, scale=3, digits=[123400])"# => r#"BigDecimal("123400e-3")"#); impl_case!(case_123_4en2: "123.4e-2" => r#"BigDecimal(sign=Plus, scale=3, digits=[1234])"# => r#"BigDecimal("1234e-3")"#); impl_case!(case_123_456: "123.456" => r#"BigDecimal(sign=Plus, scale=3, digits=[123456])"# => r#"BigDecimal("123456e-3")"#); impl_case!(case_01_20: "01.20" => r#"BigDecimal(sign=Plus, scale=2, digits=[120])"# => r#"BigDecimal("120e-2")"#); impl_case!(case_1_20: "1.20" => r#"BigDecimal(sign=Plus, scale=2, digits=[120])"# => r#"BigDecimal("120e-2")"#); impl_case!(case_01_2e3: "01.2E3" => r#"BigDecimal(sign=Plus, scale=-2, digits=[12])"# => r#"BigDecimal("12e2")"#); impl_case!(case_avagadro: "6.02214076e1023" => r#"BigDecimal(sign=Plus, scale=-1015, digits=[602214076])"# => r#"BigDecimal("602214076e1015")"#); impl_case!(case_1e99999999999999 : "1e99999999999999" => r#"BigDecimal(sign=Plus, scale=-99999999999999, digits=[1])"# => r#"BigDecimal("1e99999999999999")"#); impl_case!(case_n144d3308279 : "-144.3308279" => r#"BigDecimal(sign=Minus, scale=7, digits=[1443308279])"# => r#"BigDecimal("-1443308279e-7")"#); impl_case!(case_n349983058835858339619e2 : "-349983058835858339619e2" => r#"BigDecimal(sign=Minus, scale=-2, digits=[17941665509086410531, 18])"# => r#"BigDecimal("-349983058835858339619e2")"#); } mod write_scientific_notation { use super::*; macro_rules! test_fmt_function { ($n:expr) => { $n.to_scientific_notation() }; } impl_case!(case_4_1592480782835e9 : "4159248078.2835" => "4.1592480782835e9"); impl_case!(case_1_234e_5 : "0.00001234" => "1.234e-5"); impl_case!(case_0 : "0" => "0e0"); impl_case!(case_1 : "1" => "1e0"); impl_case!(case_2_00e0 : "2.00" => "2.00e0"); impl_case!(case_neg_5_70e1 : "-57.0" => "-5.70e1"); } mod write_engineering_notation { use super::*; macro_rules! test_fmt_function { ($n:expr) => { $n.to_engineering_notation() }; } impl_case!(case_4_1592480782835e9 : "4159248078.2835" => "4.1592480782835e9"); impl_case!(case_12_34e_6 : "0.00001234" => "12.34e-6"); impl_case!(case_0 : "0" => "0e0"); impl_case!(case_1 : "1" => "1e0"); impl_case!(case_2_00e0 : "2.00" => "2.00e0"); impl_case!(case_neg_5_70e1 : "-57.0" => "-57.0e0"); impl_case!(case_5_31e4 : "5.31e4" => "53.1e3"); impl_case!(case_5_31e5 : "5.31e5" => "531e3"); impl_case!(case_5_31e6 : "5.31e6" => "5.31e6"); impl_case!(case_5_31e7 : "5.31e7" => "53.1e6"); impl_case!(case_1e2 : "1e2" => "100e0"); impl_case!(case_1e119 : "1e19" => "10e18"); impl_case!(case_1e3000 : "1e3000" => "1e3000"); impl_case!(case_4_2e7 : "4.2e7" => "42e6"); impl_case!(case_4_2e8 : "4.2e8" => "420e6"); impl_case!(case_4e99999999999999 : "4e99999999999999" => "4e99999999999999"); impl_case!(case_4e99999999999998 : "4e99999999999998" => "400e99999999999996"); impl_case!(case_44e99999999999998 : "44e99999999999998" => "4.4e99999999999999"); impl_case!(case_4e99999999999997 : "4e99999999999997" => "40e99999999999996"); impl_case!(case_41e99999999999997 : "41e99999999999997" => "410e99999999999996"); impl_case!(case_413e99999999999997 : "413e99999999999997" => "4.13e99999999999999"); // impl_case!(case_413e99999999999997 : "413e99999999999997" => "4.13e99999999999999"); } } #[cfg(all(test, property_tests))] mod proptests { use super::*; use paste::paste; use proptest::prelude::*; use proptest::num::f64::NORMAL as NormalF64; macro_rules! impl_parsing_test { ($t:ty) => { paste! { proptest! { #[test] fn [< roudtrip_to_str_and_back_ $t >](n: $t) { let original = BigDecimal::from(n); let display = format!("{}", original); let parsed = display.parse::().unwrap(); prop_assert_eq!(&original, &parsed); } } } }; (from-float $t:ty) => { paste! { proptest! { #[test] fn [< roudtrip_to_str_and_back_ $t >](n: $t) { let original = BigDecimal::try_from(n).unwrap(); let display = format!("{}", original); let parsed = display.parse::().unwrap(); prop_assert_eq!(&original, &parsed); } } } }; } impl_parsing_test!(u8); impl_parsing_test!(u16); impl_parsing_test!(u32); impl_parsing_test!(u64); impl_parsing_test!(u128); impl_parsing_test!(i8); impl_parsing_test!(i16); impl_parsing_test!(i32); impl_parsing_test!(i64); impl_parsing_test!(i128); 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))] #[test] fn scientific_notation_roundtrip(f: f64) { prop_assume!(!f.is_nan() && !f.is_infinite()); let n = BigDecimal::from_f64(f).unwrap(); let s = n.to_scientific_notation(); let m: BigDecimal = s.parse().unwrap(); prop_assert_eq!(n, m); } #[test] fn engineering_notation_roundtrip(f: f64) { prop_assume!(!f.is_nan() && !f.is_infinite()); let n = BigDecimal::from_f64(f).unwrap(); let s = n.to_engineering_notation(); let m: BigDecimal = s.parse().unwrap(); prop_assert_eq!(n, m); } } } bigdecimal-0.4.7/src/impl_num.rs000064400000000000000000000304531046102023000146760ustar 00000000000000//! Code for num_traits use num_traits::{Zero, Num, Signed, FromPrimitive, ToPrimitive, AsPrimitive}; use num_bigint::{BigInt, Sign, ToBigInt}; #[cfg(not(feature = "std"))] use num_traits::float::FloatCore; use crate::stdlib; use stdlib::str::FromStr; use stdlib::string::{String, ToString}; use stdlib::convert::TryFrom; use stdlib::ops::Neg; use stdlib::cmp::Ordering; use crate::BigDecimal; use crate::BigDecimalRef; use crate::ParseBigDecimalError; #[cfg(not(feature = "std"))] // f64::powi is only available in std, no_std must use libm fn powi(x: f64, n: i32) -> f64 { libm::pow(x, n as f64) } #[cfg(feature = "std")] fn powi(x: f64, n: i32) -> f64 { x.powi(n) } impl Num for BigDecimal { type FromStrRadixErr = ParseBigDecimalError; /// Creates and initializes a BigDecimal. #[inline] fn from_str_radix(s: &str, radix: u32) -> Result { if radix != 10 { return Err(ParseBigDecimalError::Other(String::from( "The radix for decimal MUST be 10", ))); } let exp_separator: &[_] = &['e', 'E']; // split slice into base and exponent parts let (base_part, exponent_value) = match s.find(exp_separator) { // exponent defaults to 0 if (e|E) not found None => (s, 0), // split and parse exponent field Some(loc) => { // slice up to `loc` and 1 after to skip the 'e' char let (base, e_exp) = s.split_at(loc); (base, i128::from_str(&e_exp[1..])?) } }; // TEMPORARY: Test for emptiness - remove once BigInt supports similar error if base_part.is_empty() { return Err(ParseBigDecimalError::Empty); } let mut digit_buffer = String::new(); let last_digit_loc = base_part.len() - 1; // split decimal into a digit string and decimal-point offset let (digits, decimal_offset) = match base_part.find('.') { // No dot! pass directly to BigInt None => (base_part, 0), // dot at last digit, pass all preceding digits to BigInt Some(loc) if loc == last_digit_loc => { (&base_part[..last_digit_loc], 0) } // decimal point found - necessary copy into new string buffer Some(loc) => { // split into leading and trailing digits let (lead, trail) = (&base_part[..loc], &base_part[loc + 1..]); digit_buffer.reserve(lead.len() + trail.len()); // copy all leading characters into 'digits' string digit_buffer.push_str(lead); // copy all trailing characters after '.' into the digits string digit_buffer.push_str(trail); // count number of trailing digits let trail_digits = trail.chars().filter(|c| *c != '_').count(); (digit_buffer.as_str(), trail_digits as i128) } }; // Calculate scale by subtracing the parsed exponential // value from the number of decimal digits. // Return error if anything overflows outside i64 boundary. let scale = decimal_offset .checked_sub(exponent_value) .and_then(|scale| scale.to_i64()) .ok_or_else(|| ParseBigDecimalError::Other( format!("Exponent overflow when parsing '{}'", s)) )?; let big_int = BigInt::from_str_radix(digits, radix)?; Ok(BigDecimal::new(big_int, scale)) } } impl ToPrimitive for BigDecimal { fn to_i64(&self) -> Option { self.to_ref().to_i64() } fn to_i128(&self) -> Option { self.to_ref().to_i128() } fn to_u64(&self) -> Option { self.to_ref().to_u64() } fn to_u128(&self) -> Option { self.to_ref().to_u128() } fn to_f64(&self) -> Option { self.to_ref().to_f64() } } impl ToPrimitive for BigDecimalRef<'_> { fn to_i64(&self) -> Option { match self.sign() { Sign::Plus if self.scale == 0 => self.digits.to_i64(), Sign::Minus if self.scale == 0 => { self.digits.to_u64().and_then( |d| match d.cmp(&(i64::MAX as u64 + 1)) { Ordering::Less => Some((d as i64).neg()), Ordering::Equal => Some(i64::MIN), Ordering::Greater => None, } ) } Sign::Plus | Sign::Minus => self.to_owned_with_scale(0).int_val.to_i64(), Sign::NoSign => Some(0), } } fn to_i128(&self) -> Option { match self.sign() { Sign::Plus if self.scale == 0 => self.digits.to_i128(), Sign::Minus if self.scale == 0 => { self.digits.to_u128().and_then( |d| match d.cmp(&(i128::MAX as u128 + 1)) { Ordering::Less => Some((d as i128).neg()), Ordering::Equal => Some(i128::MIN), Ordering::Greater => None, } ) } Sign::Plus | Sign::Minus => self.to_owned_with_scale(0).int_val.to_i128(), Sign::NoSign => Some(0), } } fn to_u64(&self) -> Option { match self.sign() { Sign::Plus if self.scale == 0 => self.digits.to_u64(), Sign::Plus => self.to_owned_with_scale(0).int_val.to_u64(), Sign::NoSign => Some(0), Sign::Minus => None, } } fn to_u128(&self) -> Option { match self.sign() { Sign::Plus if self.scale == 0 => self.digits.to_u128(), Sign::Plus => self.to_owned_with_scale(0).int_val.to_u128(), Sign::NoSign => Some(0), Sign::Minus => None, } } fn to_f64(&self) -> Option { let copy_sign_to_float = |f: f64| if self.sign == Sign::Minus { f.neg() } else { f }; if self.digits.is_zero() { return Some(0.0); } if self.scale == 0 { return self.digits.to_f64().map(copy_sign_to_float); } // borrow bugint value let (mut int_cow, mut scale) = self.to_cow_biguint_and_scale(); // approximate number of base-10 digits let digit_count = ((int_cow.bits() + 1) as f64 * stdlib::f64::consts::LOG10_2).floor() as u64; // trim trailing digits, 19 at a time, leaving about 25 // which should be more than accurate enough for direct // conversion to f64 const N: u64 = 25; let digits_to_remove = digit_count.saturating_sub(N); let ten_to_19 = 10u64.pow(19); let iter_count = digits_to_remove / 19; for _ in 0..iter_count { *int_cow.to_mut() /= ten_to_19; scale -= 19; } match scale.to_i32().and_then(|x| x.checked_neg()) { Some(pow) if 0 <= pow => { // 'simple' integer case let f = int_cow.to_f64().map(copy_sign_to_float)?; (f * powi(10.0, pow)).into() } Some(exp) => { // format decimal as floating point and let the default parser generate the f64 #[cfg(not(feature = "std"))] { let s = format!("{}e{}", int_cow, exp); s.parse().map(copy_sign_to_float).ok() } #[cfg(feature = "std")] { use std::io::Write; // save allocation of a String by using local buffer of bytes // since we know the size will be small // // ~ 1 '-' + (N+19) digits + 1 'e' + 11 i32 digits = 32 + N // (plus a little extra for safety) let mut buf = [0u8; 50 + N as usize]; write!(&mut buf[..], "{}e{}", int_cow, exp).ok()?; let i = buf.iter().position(|&c| c == 0)?; let s = stdlib::str::from_utf8(&buf[..i]).ok()?; s.parse().map(copy_sign_to_float).ok() } } None => { // exponenent too big for i32: return appropriate infinity let result = if self.sign != Sign::Minus { f64::INFINITY } else { f64::NEG_INFINITY }; result.into() } } } } impl FromPrimitive for BigDecimal { #[inline] fn from_i64(n: i64) -> Option { Some(BigDecimal::from(n)) } #[inline] fn from_u64(n: u64) -> Option { Some(BigDecimal::from(n)) } #[inline] fn from_i128(n: i128) -> Option { Some(BigDecimal::from(n)) } #[inline] fn from_u128(n: u128) -> Option { Some(BigDecimal::from(n)) } #[inline] fn from_f32(n: f32) -> Option { BigDecimal::try_from(n).ok() } #[inline] fn from_f64(n: f64) -> Option { BigDecimal::try_from(n).ok() } } impl ToBigInt for BigDecimal { fn to_bigint(&self) -> Option { Some(self.with_scale(0).int_val) } } #[cfg(test)] mod test { use super::*; mod from_str_radix { use super::*; #[test] fn out_of_bounds() { let d = BigDecimal::from_str_radix("1e-9223372036854775808", 10); assert_eq!(d.unwrap_err(), ParseBigDecimalError::Other("Exponent overflow when parsing '1e-9223372036854775808'".to_string())); } } mod to_f64 { use super::*; use paste::paste; use crate::stdlib; macro_rules! impl_case { ($name:ident: $f:expr) => { #[test] fn $name() { let f: f64 = $f; let s = format!("{}", f); let n: BigDecimal = s.parse().unwrap(); let result = n.to_f64().unwrap(); assert_eq!(result, f, "src='{}'", s); } }; ($name:ident: $src:literal => $expected:expr) => { #[test] fn $name() { let n: BigDecimal = $src.parse().unwrap(); assert_eq!(n.to_f64().unwrap(), $expected); } }; } impl_case!(case_zero: 0.0); impl_case!(case_neg_zero: -0.0); impl_case!(case_875en6: 0.000875); impl_case!(case_f64_min: f64::MIN); impl_case!(case_f64_max: f64::MAX); impl_case!(case_f64_min_pos: f64::MIN_POSITIVE); impl_case!(case_pi: stdlib::f64::consts::PI); impl_case!(case_neg_e: -stdlib::f64::consts::E); impl_case!(case_1en500: 1e-500); impl_case!(case_3en310: 3e-310); impl_case!(case_0d001: 0.001); impl_case!(case_pos2_224en320: 2.224e-320); impl_case!(case_neg2_224en320: -2.224e-320); impl_case!(case_12d34: "12.34" => 12.34); impl_case!(case_0d14: "0.14" => 0.14); impl_case!(case_3d14: "3.14" => 3.14); impl_case!(case_54e23: "54e23" => 54e23); impl_case!(case_n54e23: "-54e23" => -54e23); impl_case!(case_12en78: "12e-78" => 12e-78); impl_case!(case_n12en78: "-12e-78" => -1.2e-77); impl_case!(case_n1en320: "-1e-320" => -1e-320); impl_case!(case_1d0001en920: "1.0001e-920" => 0.0); impl_case!(case_50000d0000: "50000.0000" => 50000.0); impl_case!(case_13100e4: "13100e4" => 131000000.0); impl_case!(case_44223e9999: "44223e9999" => f64::INFINITY); impl_case!(case_neg44223e9999: "-44223e9999" => f64::NEG_INFINITY); } } #[cfg(all(test, property_tests))] mod proptests { use super::*; use paste::paste; use proptest::prelude::*; use proptest::num::f64::{NORMAL as NormalF64, SUBNORMAL as SubnormalF64}; proptest! { #![proptest_config(ProptestConfig::with_cases(20_000))] #[test] fn to_f64_roundtrip(f in NormalF64 | SubnormalF64) { let d = BigDecimal::from_f64(f).unwrap(); let v = d.to_f64(); prop_assert!(v.is_some()); prop_assert_eq!(f, v.unwrap()); } } } bigdecimal-0.4.7/src/impl_ops.rs000064400000000000000000000306531046102023000147020ustar 00000000000000//! Implement math operations: Add,Sub, etc use crate::*; macro_rules! impl_add_for_primitive { ($t:ty) => { impl_add_for_primitive!(IMPL:ADD $t); impl_add_for_primitive!(IMPL:ADD-ASSIGN $t); impl_add_for_primitive!(IMPL:ADD &$t); impl_add_for_primitive!(IMPL:ADD-ASSIGN &$t); }; (IMPL:ADD $t:ty) => { impl Add<$t> for BigDecimal { type Output = BigDecimal; fn add(mut self, rhs: $t) -> BigDecimal { self += rhs; self } } impl Add<$t> for &BigDecimal { type Output = BigDecimal; fn add(self, rhs: $t) -> BigDecimal { self.to_ref() + rhs } } impl Add<$t> for BigDecimalRef<'_> { type Output = BigDecimal; fn add(self, rhs: $t) -> BigDecimal { BigDecimal::from(rhs) + self } } impl Add for $t { type Output = BigDecimal; fn add(self, rhs: BigDecimal) -> BigDecimal { rhs + self } } impl Add<&BigDecimal> for $t { type Output = BigDecimal; fn add(self, rhs: &BigDecimal) -> BigDecimal { rhs + self } } }; (IMPL:ADD-ASSIGN &$t:ty) => { // special case for the ref types impl AddAssign<&$t> for BigDecimal { fn add_assign(&mut self, rhs: &$t) { *self += *rhs; } } }; (IMPL:ADD-ASSIGN $t:ty) => { impl AddAssign<$t> for BigDecimal { fn add_assign(&mut self, rhs: $t) { if rhs == 0 { // no-op } else if self.scale == 0 { self.int_val += rhs; } else { *self += BigDecimal::from(rhs); } } } }; } impl_add_for_primitive!(u8); impl_add_for_primitive!(u16); impl_add_for_primitive!(u32); impl_add_for_primitive!(u64); impl_add_for_primitive!(u128); impl_add_for_primitive!(i8); impl_add_for_primitive!(i16); impl_add_for_primitive!(i32); impl_add_for_primitive!(i64); impl_add_for_primitive!(i128); macro_rules! impl_sub_for_primitive { ($t:ty) => { impl_sub_for_primitive!(IMPL:SUB $t); impl_sub_for_primitive!(IMPL:SUB-ASSIGN $t); impl_sub_for_primitive!(IMPL:SUB &$t); impl_sub_for_primitive!(IMPL:SUB-ASSIGN &$t); }; (IMPL:SUB $t:ty) => { impl Sub<$t> for BigDecimal { type Output = BigDecimal; fn sub(mut self, rhs: $t) -> BigDecimal { self -= rhs; self } } impl Sub<$t> for &BigDecimal { type Output = BigDecimal; fn sub(self, rhs: $t) -> BigDecimal { let res = BigDecimal::from(rhs).neg(); res + self } } impl Sub for $t { type Output = BigDecimal; fn sub(self, rhs: BigDecimal) -> BigDecimal { rhs.neg() + self } } impl Sub<&BigDecimal> for $t { type Output = BigDecimal; fn sub(self, rhs: &BigDecimal) -> BigDecimal { rhs.neg() + self } } }; (IMPL:SUB-ASSIGN &$t:ty) => { impl SubAssign<&$t> for BigDecimal { fn sub_assign(&mut self, rhs: &$t) { *self -= *rhs; } } }; (IMPL:SUB-ASSIGN $t:ty) => { impl SubAssign<$t> for BigDecimal { fn sub_assign(&mut self, rhs: $t) { if self.scale == 0 { self.int_val -= rhs; } else { *self -= BigDecimal::from(rhs); } } } }; } impl_sub_for_primitive!(u8); impl_sub_for_primitive!(u16); impl_sub_for_primitive!(u32); impl_sub_for_primitive!(u64); impl_sub_for_primitive!(u128); impl_sub_for_primitive!(i8); impl_sub_for_primitive!(i16); impl_sub_for_primitive!(i32); impl_sub_for_primitive!(i64); impl_sub_for_primitive!(i128); macro_rules! impl_mul_for_primitive { ($t:ty) => { impl_mul_for_primitive!(IMPL:MUL $t); impl_mul_for_primitive!(IMPL:MUL-ASSIGN $t); impl_mul_for_primitive!(IMPL:MUL &$t); impl_mul_for_primitive!(IMPL:MUL-ASSIGN &$t); }; (IMPL:MUL $t:ty) => { impl Mul<$t> for BigDecimal { type Output = BigDecimal; fn mul(mut self, rhs: $t) -> BigDecimal { self *= rhs; self } } impl Mul<$t> for &BigDecimal { type Output = BigDecimal; fn mul(self, rhs: $t) -> BigDecimal { let res = BigDecimal::from(rhs); res * self } } impl Mul for $t { type Output = BigDecimal; fn mul(self, rhs: BigDecimal) -> BigDecimal { rhs * self } } impl Mul<&BigDecimal> for $t { type Output = BigDecimal; fn mul(self, rhs: &BigDecimal) -> BigDecimal { rhs * self } } }; (IMPL:MUL-ASSIGN $t:ty) => { impl MulAssign<$t> for BigDecimal { fn mul_assign(&mut self, rhs: $t) { if rhs.is_zero() { *self = BigDecimal::zero() } else if rhs.is_one() { // no-op } else { *self *= BigDecimal::from(rhs); } } } }; } impl_mul_for_primitive!(u8); impl_mul_for_primitive!(u16); impl_mul_for_primitive!(u32); impl_mul_for_primitive!(u64); impl_mul_for_primitive!(u128); impl_mul_for_primitive!(i8); impl_mul_for_primitive!(i16); impl_mul_for_primitive!(i32); impl_mul_for_primitive!(i64); impl_mul_for_primitive!(i128); macro_rules! impl_div_for_primitive { (f32) => { impl_div_for_primitive!(IMPL:DIV:FLOAT f32); impl_div_for_primitive!(IMPL:DIV:REF &f32); }; (f64) => { impl_div_for_primitive!(IMPL:DIV:FLOAT f64); impl_div_for_primitive!(IMPL:DIV:REF &f64); }; ($t:ty) => { impl_div_for_primitive!(IMPL:DIV $t); impl_div_for_primitive!(IMPL:DIV:REF &$t); impl_div_for_primitive!(IMPL:DIV-ASSIGN $t); }; (IMPL:DIV $t:ty) => { impl Div<$t> for BigDecimal { type Output = BigDecimal; #[cfg(rustc_1_70)] // Option::is_some_and #[allow(clippy::incompatible_msrv)] fn div(self, denom: $t) -> BigDecimal { if denom.is_one() { self } else if denom.checked_neg().is_some_and(|n| n == 1) { self.neg() } else if denom.clone() == 2 { self.half() } else if denom.checked_neg().is_some_and(|n| n == 2) { self.half().neg() } else { self / BigDecimal::from(denom) } } #[cfg(not(rustc_1_70))] fn div(self, denom: $t) -> BigDecimal { if denom.is_one() { self } else if denom.checked_neg().map(|n| n == 1).unwrap_or(false) { self.neg() } else if denom.clone() == 2 { self.half() } else if denom.checked_neg().map(|n| n == 2).unwrap_or(false) { self.half().neg() } else { self / BigDecimal::from(denom) } } } impl Div<$t> for &BigDecimal { type Output = BigDecimal; fn div(self, denom: $t) -> BigDecimal { self.clone() / denom } } impl Div for $t { type Output = BigDecimal; fn div(self, denom: BigDecimal) -> BigDecimal { if self.is_one() { denom.inverse() } else { BigDecimal::from(self) / denom } } } impl Div<&BigDecimal> for $t { type Output = BigDecimal; fn div(self, denom: &BigDecimal) -> BigDecimal { self / denom.clone() } } }; (IMPL:DIV-ASSIGN $t:ty) => { impl DivAssign<$t> for BigDecimal { fn div_assign(&mut self, rhs: $t) { if rhs.is_zero() { *self = BigDecimal::zero() } else if rhs.is_one() { // no-op } else { *self = self.clone() / BigDecimal::from(rhs); } } } }; (IMPL:DIV:REF $t:ty) => { impl Div<$t> for BigDecimal { type Output = BigDecimal; fn div(self, denom: $t) -> BigDecimal { self / *denom } } impl Div for $t { type Output = BigDecimal; fn div(self, denom: BigDecimal) -> Self::Output { *self / denom } } impl Div<&BigDecimal> for $t { type Output = BigDecimal; fn div(self, denom: &BigDecimal) -> Self::Output { *self / denom } } impl DivAssign<$t> for BigDecimal { fn div_assign(&mut self, denom: $t) { self.div_assign(*denom) } } }; (IMPL:DIV:FLOAT $t:ty) => { impl Div<$t> for BigDecimal { type Output = BigDecimal; #[allow(clippy::float_cmp)] fn div(self, denom: $t) -> BigDecimal { if !denom.is_normal() { BigDecimal::zero() } else if denom == (1.0 as $t) { self } else if denom == (-1.0 as $t) { self.neg() } else if denom == (2.0 as $t) { self.half() } else if denom == (-2.0 as $t) { self.half().neg() } else { self / BigDecimal::try_from(denom).unwrap() } } } impl Div<$t> for &BigDecimal { type Output = BigDecimal; fn div(self, denom: $t) -> BigDecimal { self.clone() / denom } } impl Div for $t { type Output = BigDecimal; fn div(self, denom: BigDecimal) -> Self::Output { if !self.is_normal() { BigDecimal::zero() } else if self.is_one() { denom.inverse() } else { BigDecimal::try_from(self).unwrap() / denom } } } impl Div<&BigDecimal> for $t { type Output = BigDecimal; fn div(self, denom: &BigDecimal) -> Self::Output { if !self.is_normal() { BigDecimal::zero() } else if self.is_one() { denom.inverse() } else { BigDecimal::try_from(self).unwrap() / denom } } } impl DivAssign<$t> for BigDecimal { fn div_assign(&mut self, denom: $t) { if !denom.is_normal() { *self = BigDecimal::zero() } else { *self = self.clone() / BigDecimal::try_from(denom).unwrap() }; } } }; } impl_div_for_primitive!(u8); impl_div_for_primitive!(u16); impl_div_for_primitive!(u32); impl_div_for_primitive!(u64); impl_div_for_primitive!(u128); impl_div_for_primitive!(i8); impl_div_for_primitive!(i16); impl_div_for_primitive!(i32); impl_div_for_primitive!(i64); impl_div_for_primitive!(i128); impl_div_for_primitive!(f32); impl_div_for_primitive!(f64); impl Neg for BigDecimal { type Output = BigDecimal; #[inline] fn neg(mut self) -> BigDecimal { self.int_val = -self.int_val; self } } impl Neg for &BigDecimal { type Output = BigDecimal; #[inline] fn neg(self) -> BigDecimal { -self.clone() } } impl Neg for BigDecimalRef<'_> { type Output = Self; fn neg(self) -> Self::Output { Self { sign: self.sign.neg(), digits: self.digits, scale: self.scale, } } } bigdecimal-0.4.7/src/impl_ops_add.rs000064400000000000000000000217721046102023000155140ustar 00000000000000//! Addition operator trait implementation //! use super::*; use stdlib::borrow::ToOwned; impl Add for BigDecimal { type Output = BigDecimal; #[inline] fn add(self, rhs: BigDecimal) -> BigDecimal { arithmetic::addition::add_bigdecimals(self, rhs) } } impl<'a, T: Into>> Add for BigDecimal { type Output = BigDecimal; fn add(mut self, rhs: T) -> BigDecimal { self.add_assign(rhs); self } } impl Add for BigDecimal { type Output = BigDecimal; #[inline] fn add(self, rhs: BigInt) -> BigDecimal { self + BigDecimal::from(rhs) } } impl Add for &'_ BigDecimal { type Output = BigDecimal; #[inline] fn add(self, rhs: BigDecimal) -> BigDecimal { rhs + self } } impl<'a, T: Into>> Add for &'_ BigDecimal { type Output = BigDecimal; fn add(self, rhs: T) -> BigDecimal { arithmetic::addition::add_bigdecimal_refs(self, rhs, None) } } impl Add for &'_ BigDecimal { type Output = BigDecimal; #[inline] fn add(self, rhs: BigInt) -> BigDecimal { self.to_ref() + rhs } } impl Add for BigDecimalRef<'_> { type Output = BigDecimal; #[inline] fn add(self, rhs: BigDecimal) -> BigDecimal { rhs + self } } impl<'a, T: Into>> Add for BigDecimalRef<'_> { type Output = BigDecimal; fn add(self, rhs: T) -> BigDecimal { arithmetic::addition::add_bigdecimal_refs(self, rhs, None) } } impl Add for BigDecimalRef<'_> { type Output = BigDecimal; #[inline] fn add(self, rhs: BigInt) -> BigDecimal { self + BigDecimal::from(rhs) } } impl Add for BigInt { type Output = BigDecimal; #[inline] fn add(self, rhs: BigDecimal) -> BigDecimal { BigDecimal::from(self) + rhs } } impl Add<&BigDecimal> for BigInt { type Output = BigDecimal; fn add(self, rhs: &BigDecimal) -> BigDecimal { BigDecimal::from(self) + rhs } } impl Add> for BigInt { type Output = BigDecimal; fn add(self, rhs: BigDecimalRef<'_>) -> BigDecimal { BigDecimal::from(self) + rhs } } impl Add for &BigInt { type Output = BigDecimal; #[inline] fn add(self, rhs: BigDecimal) -> BigDecimal { rhs + self } } impl Add<&BigDecimal> for &BigInt { type Output = BigDecimal; #[inline] fn add(self, rhs: &BigDecimal) -> BigDecimal { rhs + self } } impl Add> for &BigInt { type Output = BigDecimal; #[inline] fn add(self, rhs: BigDecimalRef<'_>) -> BigDecimal { rhs + self } } impl AddAssign for BigDecimal { fn add_assign(&mut self, rhs: BigDecimal) { arithmetic::addition::addassign_bigdecimals(self, rhs) } } impl<'a, N: Into>> AddAssign for BigDecimal { #[inline] fn add_assign(&mut self, rhs: N) { arithmetic::addition::addassign_bigdecimal_ref(self, rhs) } } impl AddAssign for BigDecimal { #[inline] fn add_assign(&mut self, rhs: BigInt) { self.add_assign(BigDecimal::from(rhs)); } } #[cfg(test)] mod test { use super::*; use paste::paste; macro_rules! impl_case { ( $name:ident: $a:literal + $b:literal => $c:literal ) => { #[test] fn $name() { let a: BigDecimal = $a.parse().unwrap(); let b: BigDecimal = $b.parse().unwrap(); let c: BigDecimal = $c.parse().unwrap(); assert_eq!(c, a.clone() + b.clone()); assert_eq!(c, a.clone() + b.to_ref()); assert_eq!(c, a.clone() + &b); assert_eq!(c, &a + b.clone()); assert_eq!(c, &a + b.to_ref()); assert_eq!(c, &a + &b); assert_eq!(c, a.to_ref() + b.clone()); assert_eq!(c, a.to_ref() + b.to_ref()); assert_eq!(c, a.to_ref() + &b); // Reversed assert_eq!(c, b.clone() + a.clone()); assert_eq!(c, b.clone() + a.to_ref()); assert_eq!(c, b.clone() + &a); assert_eq!(c, &b + a.clone()); assert_eq!(c, &b + a.to_ref()); assert_eq!(c, &b + &a); assert_eq!(c, b.to_ref() + a.clone()); assert_eq!(c, b.to_ref() + a.to_ref()); assert_eq!(c, b.to_ref() + &a); let mut n = a.clone(); n += b.clone(); assert_eq!(c, n); let mut n = a.clone(); n += &b; assert_eq!(c, n); let mut n = a.clone(); n += b.to_ref(); assert_eq!(c, n); let mut n = b.clone(); n += a.clone(); assert_eq!(c, n); let mut n = b.clone(); n += &a; assert_eq!(c, n); let mut n = b.clone(); n += a.to_ref(); assert_eq!(c, n); } }; ( $name:ident: $a:literal + (int) $b:literal => $c:literal ) => { #[test] fn $name() { let a: BigDecimal = $a.parse().unwrap(); let b: BigInt = $b.parse().unwrap(); let c: BigDecimal = $c.parse().unwrap(); assert_eq!(c, a.clone() + b.clone()); assert_eq!(c, a.clone() + &b); assert_eq!(c, &a + &b); assert_eq!(c, &a + b.clone()); assert_eq!(c, a.to_ref() + &b); assert_eq!(c, b.clone() + a.clone()); assert_eq!(c, b.clone() + a.to_ref()); assert_eq!(c, b.clone() + &a); assert_eq!(c, &b + a.clone()); assert_eq!(c, &b + a.to_ref()); assert_eq!(c, &b + &a); let mut n = a.clone(); n += b.clone(); assert_eq!(c, n); let mut n = a.clone(); n += &b; assert_eq!(c, n); } }; } impl_case!(case_1234en2_1234en3: "12.34" + "1.234" => "13.574"); impl_case!(case_1234en2_n1234en3: "12.34" + "-1.234" => "11.106"); impl_case!(case_1234en2_n1234en2: "12.34" + "-12.34" => "0"); impl_case!(case_1234e6_1234en6: "1234e6" + "1234e-6" => "1234000000.001234"); impl_case!(case_1234en6_1234e6: "1234e6" + "1234e-6" => "1234000000.001234"); impl_case!(case_18446744073709551616_1: "18446744073709551616.0" + "1" => "18446744073709551617"); impl_case!(case_184467440737e3380_0: "184467440737e3380" + "0" => "184467440737e3380"); impl_case!(case_0_776en1: "0" + "77.6" => "77.6"); impl_case!(case_80802295e5_int0: "80802295e5" + (int)"0" => "80802295e5"); impl_case!(case_239200en4_intneg101: "23.9200" + (int)"-101" => "-77.0800"); impl_case!(case_46636423395767125en15_int0: "46.636423395767125" + (int)"123" => "169.636423395767125"); #[cfg(property_tests)] mod prop { use super::*; use proptest::*; use num_traits::FromPrimitive; proptest! { #[test] fn add_refs_and_owners(f: f32, g: f32) { // ignore non-normal numbers prop_assume!(f.is_normal()); prop_assume!(g.is_normal()); let a = BigDecimal::from_f32(f).unwrap(); let b = BigDecimal::from_f32(g).unwrap(); let own_plus_ref = a.clone() + &b; let ref_plus_own = &a + b.clone(); let mut c = a.clone(); c += &b; let mut d = a.clone(); d += b; prop_assert_eq!(&own_plus_ref, &ref_plus_own); prop_assert_eq!(&c, &ref_plus_own); prop_assert_eq!(&d, &ref_plus_own); } #[test] fn addition_is_communative(f: f32, g: f32) { // ignore non-normal numbers prop_assume!(f.is_normal()); prop_assume!(g.is_normal()); let a = BigDecimal::from_f32(f).unwrap(); let b = BigDecimal::from_f32(g).unwrap(); let a_plus_b = &a + &b; let b_plus_a = &b + &a; prop_assert_eq!(a_plus_b, b_plus_a) } #[test] fn addition_is_associative(f: f32, g: f32, h: f32) { // ignore non-normal numbers prop_assume!(f.is_normal()); prop_assume!(g.is_normal()); prop_assume!(h.is_normal()); let a = BigDecimal::from_f32(f).unwrap(); let b = BigDecimal::from_f32(g).unwrap(); let c = BigDecimal::from_f32(h).unwrap(); let ab = &a + &b; let ab_c = ab + &c; let bc = &b + &c; let a_bc = a + bc; prop_assert_eq!(ab_c, a_bc) } } } } bigdecimal-0.4.7/src/impl_ops_div.rs000064400000000000000000000114401046102023000155350ustar 00000000000000//! Implement division use super::*; impl Div for BigDecimal { type Output = BigDecimal; #[inline] fn div(self, other: BigDecimal) -> BigDecimal { if other.is_zero() { panic!("Division by zero"); } if self.is_zero() || other.is_one() { return self; } let scale = self.scale - other.scale; if self.int_val == other.int_val { return BigDecimal { int_val: 1.into(), scale: scale, }; } let max_precision = DEFAULT_PRECISION; return impl_division(self.int_val, &other.int_val, scale, max_precision); } } impl<'a> Div<&'a BigDecimal> for BigDecimal { type Output = BigDecimal; #[inline] fn div(self, other: &'a BigDecimal) -> BigDecimal { if other.is_zero() { panic!("Division by zero"); } if self.is_zero() || other.is_one() { return self; } let scale = self.scale - other.scale; if self.int_val == other.int_val { return BigDecimal { int_val: 1.into(), scale: scale, }; } let max_precision = DEFAULT_PRECISION; return impl_division(self.int_val, &other.int_val, scale, max_precision); } } forward_ref_val_binop!(impl Div for BigDecimal, div); impl Div<&BigDecimal> for &BigDecimal { type Output = BigDecimal; #[inline] fn div(self, other: &BigDecimal) -> BigDecimal { if other.is_zero() { panic!("Division by zero"); } // TODO: Fix setting scale if self.is_zero() || other.is_one() { return self.clone(); } let scale = self.scale - other.scale; let num_int = &self.int_val; let den_int = &other.int_val; if num_int == den_int { return BigDecimal { int_val: 1.into(), scale: scale, }; } let max_precision = DEFAULT_PRECISION; return impl_division(num_int.clone(), den_int, scale, max_precision); } } #[cfg(test)] mod test { use super::*; #[test] fn test_div() { let vals = vec![ ("0", "1", "0"), ("0", "10", "0"), ("2", "1", "2"), ("2e1", "1", "2e1"), ("10", "10", "1"), ("100", "10.0", "1e1"), ("20.0", "200", ".1"), ("4", "2", "2.0"), ("15", "3", "5.0"), ("1", "2", "0.5"), ("1", "2e-2", "5e1"), ("1", "0.2", "5"), ("1.0", "0.02", "50"), ("1", "0.020", "5e1"), ("5.0", "4.00", "1.25"), ("5.0", "4.000", "1.25"), ("5", "4.000", "1.25"), ("5", "4", "125e-2"), ("100", "5", "20"), ("-50", "5", "-10"), ("200", "-5", "-40."), ("1", "3", ".3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333"), ("-2", "-3", ".6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667"), ("-12.34", "1.233", "-10.00811030008110300081103000811030008110300081103000811030008110300081103000811030008110300081103001"), ("125348", "352.2283", "355.8714617763535752237966114591019517738921035021887792661748076460636467881768727839301952739175132"), ]; for &(x, y, z) in vals.iter() { let a = BigDecimal::from_str(x).unwrap(); let b = BigDecimal::from_str(y).unwrap(); let c = BigDecimal::from_str(z).unwrap(); assert_eq!(a.clone() / b.clone(), c); assert_eq!(a.clone() / &b, c); assert_eq!(&a / b.clone(), c); assert_eq!(&a / &b, c); // assert_eq!(q.scale, c.scale); // let mut q = a; // q /= b; // assert_eq!(q, c); } } #[test] #[should_panic(expected = "Division by zero")] fn test_division_by_zero_panics() { let x = BigDecimal::from_str("3.14").unwrap(); let _r = x / 0; } #[test] #[should_panic(expected = "Division by zero")] fn test_division_by_zero_panics_v2() { let x = BigDecimal::from_str("3.14").unwrap(); let _r = x / BigDecimal::zero(); } #[test] fn test_division_by_large_number() { let n = 1u8; let d: BigDecimal = "79437738588056219546528239237352667078".parse().unwrap(); let quotient_n_ref_d = n / &d; let quotient_n_d = n / d.clone(); assert_eq!(quotient_n_ref_d, quotient_n_d); assert_eq!(quotient_n_ref_d, "1.258847517281104957975270408416632052090243053529147458917576143852500316808428812104171430669001064E-38".parse().unwrap()); } } bigdecimal-0.4.7/src/impl_ops_mul.rs000064400000000000000000000175461046102023000155650ustar 00000000000000//! Multiplication operator trait implementation //! use super::*; use crate::stdlib::mem::swap; impl Mul for BigDecimal { type Output = BigDecimal; #[inline] fn mul(mut self, rhs: BigDecimal) -> BigDecimal { if self.is_one() { rhs } else if rhs.is_one() { self } else { self.scale += rhs.scale; self.int_val *= rhs.int_val; self } } } impl<'a> Mul<&'a BigDecimal> for BigDecimal { type Output = BigDecimal; #[inline] fn mul(mut self, rhs: &'a BigDecimal) -> BigDecimal { if self.is_one() { self.scale = rhs.scale; self.int_val.set_zero(); self.int_val += &rhs.int_val; } else if rhs.is_zero() { self.scale = 0; self.int_val.set_zero(); } else if !self.is_zero() && !rhs.is_one() { self.scale += rhs.scale; self.int_val *= &rhs.int_val; } self } } impl Mul for &BigDecimal { type Output = BigDecimal; #[inline] fn mul(self, rhs: BigDecimal) -> BigDecimal { rhs * self } } impl Mul<&BigDecimal> for &BigDecimal { type Output = BigDecimal; #[inline] fn mul(self, rhs: &BigDecimal) -> BigDecimal { if self.is_one() { rhs.normalized() } else if rhs.is_one() { self.normalized() } else { let scale = self.scale + rhs.scale; BigDecimal::new(&self.int_val * &rhs.int_val, scale) } } } impl Mul for BigDecimal { type Output = BigDecimal; #[inline] fn mul(mut self, rhs: BigInt) -> BigDecimal { self.int_val *= rhs; self } } impl Mul<&BigInt> for BigDecimal { type Output = BigDecimal; #[inline] fn mul(mut self, rhs: &BigInt) -> BigDecimal { self.int_val *= rhs; self } } impl Mul for &BigDecimal { type Output = BigDecimal; #[inline] fn mul(self, mut rhs: BigInt) -> BigDecimal { rhs *= &self.int_val; BigDecimal::new(rhs, self.scale) } } impl Mul<&BigInt> for &BigDecimal { type Output = BigDecimal; #[inline] fn mul(self, rhs: &BigInt) -> BigDecimal { if rhs.is_one() { self.normalized() } else if self.is_one() { BigDecimal::new(rhs.clone(), 0) } else { let value = &self.int_val * rhs; BigDecimal::new(value, self.scale) } } } impl Mul for BigInt { type Output = BigDecimal; #[inline] fn mul(mut self, mut rhs: BigDecimal) -> BigDecimal { if rhs.is_one() { rhs.scale = 0; swap(&mut rhs.int_val, &mut self); } else if !self.is_one() { rhs.int_val *= self; } rhs } } impl Mul for &BigInt { type Output = BigDecimal; #[inline] fn mul(self, mut rhs: BigDecimal) -> BigDecimal { if self.is_one() { rhs.normalized() } else if rhs.is_one() { rhs.int_val.set_zero(); rhs.int_val += self; rhs.scale = 0; rhs } else { rhs.int_val *= self; rhs } } } impl Mul<&BigDecimal> for &BigInt { type Output = BigDecimal; #[inline] fn mul(self, rhs: &BigDecimal) -> BigDecimal { if self.is_one() { rhs.normalized() } else if rhs.is_one() { BigDecimal::new(self.clone(), 0) } else { let value = &rhs.int_val * self; BigDecimal::new(value, rhs.scale) } } } impl Mul<&BigDecimal> for BigInt { type Output = BigDecimal; #[inline] fn mul(mut self, rhs: &BigDecimal) -> BigDecimal { if self.is_one() { rhs.normalized() } else if rhs.is_one() { BigDecimal::new(self, 0) } else { self *= &rhs.int_val; BigDecimal::new(self, rhs.scale) } } } forward_val_assignop!(impl MulAssign for BigDecimal, mul_assign); impl MulAssign<&BigDecimal> for BigDecimal { #[inline] fn mul_assign(&mut self, rhs: &BigDecimal) { if rhs.is_one() { return; } self.scale += rhs.scale; self.int_val = &self.int_val * &rhs.int_val; } } impl MulAssign<&BigInt> for BigDecimal { #[inline] fn mul_assign(&mut self, rhs: &BigInt) { if rhs.is_one() { return; } self.int_val *= rhs; } } impl MulAssign for BigDecimal { #[inline] fn mul_assign(&mut self, rhs: BigInt) { *self *= &rhs } } #[cfg(test)] #[allow(non_snake_case)] mod bigdecimal_tests { use super::*; use num_traits::{ToPrimitive, FromPrimitive, Signed, Zero, One}; use num_bigint; use paste::paste; macro_rules! impl_test { ($name:ident; $a:literal * $b:literal => $expected:literal) => { #[test] fn $name() { let mut a: BigDecimal = $a.parse().unwrap(); let b: BigDecimal = $b.parse().unwrap(); let expected = $expected.parse().unwrap(); let prod = a.clone() * b.clone(); assert_eq!(prod, expected); assert_eq!(prod.scale, expected.scale); assert_eq!(a.clone() * &b, expected); assert_eq!(&a * b.clone(), expected); assert_eq!(&a * &b, expected); a *= b; assert_eq!(a, expected); assert_eq!(a.scale, expected.scale); } }; ($name:ident; $bigt:ty; $a:literal * $b:literal => $expected:literal) => { #[test] fn $name() { let a: BigDecimal = $a.parse().unwrap(); let b: $bigt = $b.parse().unwrap(); let c = $expected.parse().unwrap(); let prod = a.clone() * b.clone(); assert_eq!(prod, c); assert_eq!(prod.scale, c.scale); assert_eq!(b.clone() * a.clone(), c); assert_eq!(a.clone() * &b, c); assert_eq!(b.clone() * &a, c); assert_eq!(&a * b.clone(), c); assert_eq!(&b * a.clone(), c); assert_eq!(&a * &b, c); assert_eq!(&b * &a, c); } }; } impl_test!(case_2_1; "2" * "1" => "2"); impl_test!(case_12d34_1d234; "12.34" * "1.234" => "15.22756"); impl_test!(case_2e1_1; "2e1" * "1" => "2e1"); impl_test!(case_3_d333333; "3" * ".333333" => "0.999999"); impl_test!(case_2389472934723_209481029831; "2389472934723" * "209481029831" => "500549251119075878721813"); impl_test!(case_1ed450_1e500; "1e-450" * "1e500" => "0.1e51"); impl_test!(case_n995052931ddd_4d523087321; "-995052931372975485719.533153137" * "4.523087321" => "-4500711297616988541501.836966993116075977"); impl_test!(case_995052931ddd_n4d523087321; "995052931372975485719.533153137" * "-4.523087321" => "-4500711297616988541501.836966993116075977"); impl_test!(case_n8d37664968_n4d523087321; "-8.37664968" * "-1.9086963714056968482094712882596748" => "15.988480848752691653730876239769592670324064"); impl_test!(case_n8d37664968_0; "-8.37664968" * "0" => "0.00000000"); impl_test!(case_8d561_10; BigInt; "8.561" * "10" => "85.610"); // Test multiplication between big decimal and big integer impl_test!(case_10000_638655273892892437; BigInt; "10000" * "638655273892892437" => "6386552738928924370000"); impl_test!(case_1en10_n9056180052657301; BigInt; "1e-10" * "-9056180052657301" => "-905618.0052657301"); impl_test!(case_n9en1_n368408638655273892892437473; BigInt; "-9e-1" * "-368408638655273892892437473" => "331567774789746503603193725.7"); impl_test!(case_n1d175470587012343730098_577575785; BigInt; "-1.175470587012343730098" * "577575785" => "-678923347.038065234601180476930"); } bigdecimal-0.4.7/src/impl_ops_rem.rs000064400000000000000000000107561046102023000155470ustar 00000000000000//! Remainder implementations use super::*; impl Rem for BigDecimal { type Output = BigDecimal; #[inline] fn rem(self, other: BigDecimal) -> BigDecimal { let scale = cmp::max(self.scale, other.scale); let num = self.take_and_scale(scale).int_val; let den = other.take_and_scale(scale).int_val; BigDecimal::new(num % den, scale) } } impl Rem<&BigDecimal> for BigDecimal { type Output = BigDecimal; #[inline] fn rem(self, other: &BigDecimal) -> BigDecimal { let scale = cmp::max(self.scale, other.scale); let num = self.take_and_scale(scale).int_val; let den = &other.int_val; let result = if scale == other.scale { num % den } else { num % (den * ten_to_the((scale - other.scale) as u64)) }; BigDecimal::new(result, scale) } } impl Rem for &BigDecimal { type Output = BigDecimal; #[inline] fn rem(self, other: BigDecimal) -> BigDecimal { let scale = cmp::max(self.scale, other.scale); let num = &self.int_val; let den = other.take_and_scale(scale).int_val; let result = if scale == self.scale { num % den } else { let scaled_num = num * ten_to_the((scale - self.scale) as u64); scaled_num % den }; BigDecimal::new(result, scale) } } impl Rem<&BigDecimal> for &BigDecimal { type Output = BigDecimal; #[inline] fn rem(self, other: &BigDecimal) -> BigDecimal { let scale = cmp::max(self.scale, other.scale); let num = &self.int_val; let den = &other.int_val; let result = match self.scale.cmp(&other.scale) { Ordering::Equal => num % den, Ordering::Less => { let scaled_num = num * ten_to_the((scale - self.scale) as u64); scaled_num % den } Ordering::Greater => { let scaled_den = den * ten_to_the((scale - other.scale) as u64); num % scaled_den } }; BigDecimal::new(result, scale) } } impl RemAssign<&BigDecimal> for BigDecimal { fn rem_assign(&mut self, other: &BigDecimal) { let rem = (&*self).rem(other); *self = rem; } } #[cfg(test)] mod test { use super::*; use paste::paste; macro_rules! impl_case { ($a:literal % $b:literal => $c:literal ) => { paste! { impl_case!([< case_ $a _ $b >]: $a % $b => $c); } }; ($name:ident: $a:literal % $b:literal => $c:literal ) => { #[test] fn $name() { let mut a: BigDecimal = $a.parse().unwrap(); let b: BigDecimal = $b.parse().unwrap(); let c: BigDecimal = $c.parse().unwrap(); assert_eq!(a.clone() % b.clone(), c); assert_eq!(a.clone() % &b, c); assert_eq!(&a % b.clone(), c); assert_eq!(&a % &b, c); a %= &b; assert_eq!(a, c); } }; } impl_case!("100" % "5" => "0"); impl_case!("2e1" % "1" => "0"); impl_case!("2" % "1" => "0"); impl_case!("1" % "3" => "1"); impl_case!("1" % "5e-1" => "0"); impl_case!("15e-1" % "1" => "0.5"); impl_case!("1" % "3e-2" => "1e-2"); impl_case!("10" % "3e-3" => "0.001"); impl_case!("3" % "2" => "1"); impl_case!("1234e-2" % "1233e-3" => "0.01"); impl_case!(case_neg3_2: "-3" % "2" => "-1"); impl_case!(case_3_neg2: "3" % "-2" => "1"); impl_case!(case_neg3_neg2: "3" % "-2" => "1"); impl_case!(case_neg95eneg1_515eneg2: "-9.5" % "5.15" => "-4.35"); #[cfg(property_tests)] mod prop { use super::*; use proptest::*; use num_traits::FromPrimitive; proptest! { #[test] fn quotient_and_remainder(f: f32, g: f32) { // ignore non-normal numbers prop_assume!(f.is_normal()); prop_assume!(g.is_normal()); prop_assume!(!g.is_zero()); let (f, g) = if f.abs() > g.abs() { (f, g) } else { (g, f) }; let a = BigDecimal::from_f32(f).unwrap(); let b = BigDecimal::from_f32(g).unwrap(); let r = &a % &b; let q = (&a / &b).with_scale(0); assert_eq!(a, q * b + r); } } } } bigdecimal-0.4.7/src/impl_ops_sub.rs000064400000000000000000000225201046102023000155450ustar 00000000000000//! //! Subtraction operator trait implementation //! use crate::*; impl Sub for BigDecimal { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigDecimal) -> BigDecimal { if rhs.is_zero() { return self; } if self.is_zero() { return rhs.neg(); } let mut lhs = self; match lhs.scale.cmp(&rhs.scale) { Ordering::Equal => { lhs.int_val -= rhs.int_val; lhs } Ordering::Less => { lhs.take_and_scale(rhs.scale) - rhs } Ordering::Greater => { let rhs = rhs.take_and_scale(lhs.scale); lhs - rhs }, } } } impl Sub for &'_ BigDecimal { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigDecimal) -> BigDecimal { self.to_ref() - rhs } } impl Sub for BigDecimalRef<'_> { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigDecimal) -> BigDecimal { (rhs - self).neg() } } impl<'a, T: Into>> Sub for BigDecimal { type Output = BigDecimal; fn sub(mut self, rhs: T) -> BigDecimal { self.sub_assign(rhs); self } } impl<'a, T: Into>> Sub for &'_ BigDecimal { type Output = BigDecimal; fn sub(self, rhs: T) -> BigDecimal { let rhs = rhs.into(); match self.scale.cmp(&rhs.scale) { Ordering::Equal => { self.clone() - rhs } Ordering::Less => { self.with_scale(rhs.scale) - rhs } Ordering::Greater => { self - rhs.to_owned_with_scale(self.scale) } } } } impl<'a, T: Into>> Sub for BigDecimalRef<'_> { type Output = BigDecimal; fn sub(self, rhs: T) -> BigDecimal { let rhs = rhs.into(); match self.scale.cmp(&rhs.scale) { Ordering::Equal => self.to_owned() - rhs, Ordering::Less => self.to_owned_with_scale(rhs.scale) - rhs, Ordering::Greater => self - rhs.to_owned_with_scale(self.scale), } } } impl Sub for BigDecimal { type Output = BigDecimal; fn sub(mut self, rhs: BigInt) -> BigDecimal { self.sub_assign(rhs); self } } impl Sub for &'_ BigDecimal { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigInt) -> BigDecimal { self.to_ref() - rhs } } impl Sub for BigDecimalRef<'_> { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigInt) -> BigDecimal { self - BigDecimal::from(rhs) } } impl Sub for BigInt { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigDecimal) -> BigDecimal { (rhs - self).neg() } } impl Sub for &BigInt { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigDecimal) -> BigDecimal { (rhs - self).neg() } } impl<'a> Sub> for BigInt { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigDecimalRef<'a>) -> BigDecimal { (rhs - &self).neg() } } impl<'a> Sub> for &BigInt { type Output = BigDecimal; #[inline] fn sub(self, rhs: BigDecimalRef<'a>) -> BigDecimal { (rhs - self).neg() } } impl SubAssign for BigDecimal { #[inline] fn sub_assign(&mut self, rhs: BigDecimal) { if rhs.is_zero() { return; } if self.is_zero() { *self = rhs.neg(); return; } match self.scale.cmp(&rhs.scale) { Ordering::Equal => { self.int_val -= rhs.int_val; } Ordering::Less => { self.int_val *= ten_to_the((rhs.scale - self.scale) as u64); self.int_val -= rhs.int_val; self.scale = rhs.scale; } Ordering::Greater => { let mut rhs_int_val = rhs.int_val; rhs_int_val *= ten_to_the((self.scale - rhs.scale) as u64); self.int_val -= rhs_int_val; } } } } impl<'rhs, T: Into>> SubAssign for BigDecimal { #[inline] fn sub_assign(&mut self, rhs: T) { let rhs = rhs.into(); if rhs.is_zero() { return; } if self.is_zero() { *self = rhs.neg().to_owned(); return; } match self.scale.cmp(&rhs.scale) { Ordering::Equal => { self.int_val -= rhs.to_owned().int_val; } Ordering::Less => { self.int_val *= ten_to_the((rhs.scale - self.scale) as u64); self.int_val -= rhs.to_owned().int_val; self.scale = rhs.scale; } Ordering::Greater => { *self -= rhs.to_owned_with_scale(self.scale); } } } } impl SubAssign for BigDecimal { #[inline(always)] fn sub_assign(&mut self, rhs: BigInt) { *self -= BigDecimal::new(rhs, 0) } } #[cfg(test)] mod test { use super::*; use paste::paste; macro_rules! impl_case { ($name:ident: $a:literal - $b:literal => $c:literal ) => { #[test] fn $name() { let a: BigDecimal = $a.parse().unwrap(); let b: BigDecimal = $b.parse().unwrap(); let c: BigDecimal = $c.parse().unwrap(); assert_eq!(c, a.clone() - b.clone()); assert_eq!(c, a.clone() - &b); assert_eq!(c, &a - b.clone()); assert_eq!(c, &a - &b); assert_eq!(c, a.to_ref() - &b); assert_eq!(c, &a - b.to_ref()); assert_eq!(c, a.to_ref() - b.to_ref()); let mut n = a.clone(); n -= b.to_ref(); assert_eq!(n, c); let mut n = a.clone(); n -= &b; assert_eq!(n, c); let mut n = a.clone(); n -= b.clone(); assert_eq!(n, c); let mut n = a.clone(); (&mut n).sub_assign(b.clone()); assert_eq!(n, c); } }; ($name:ident: $a:literal - (int) $b:literal => $c:literal ) => { #[test] fn $name() { let a: BigDecimal = $a.parse().unwrap(); let b: BigInt = $b.parse().unwrap(); let expected: BigDecimal = $c.parse().unwrap(); assert_eq!(expected, a.clone() - b.clone()); assert_eq!(expected, a.clone() - &b); assert_eq!(expected, &a - &b); assert_eq!(expected, &a - b.clone()); assert_eq!(expected, a.to_ref() - &b); let expected_neg = expected.clone().neg(); assert_eq!(expected_neg, b.clone() - a.clone()); assert_eq!(expected_neg, &b - a.to_ref()); assert_eq!(expected_neg, &b - a.clone()); } }; } impl_case!(case_1234en2_1234en3: "12.34" - "1.234" => "11.106"); impl_case!(case_1234en2_n1234en3: "12.34" - "-1.234" => "13.574"); impl_case!(case_1234e6_1234en6: "1234e6" - "1234e-6" => "1233999999.998766"); impl_case!(case_1234en6_1234e6: "1234e-6" - "1234e6" => "-1233999999.998766"); impl_case!(case_712911676en6_4856259269250829: "712911676e-6" - "4856259269250829" => "-4856259269250116.088324"); impl_case!(case_85616001e4_0: "85616001e4" - "0" => "85616001e4"); impl_case!(case_0_520707672en5: "0" - "5207.07672" => "-520707672e-5"); impl_case!(case_99291289e5_int0: "99291289e5" - (int)"0" => "99291289e5"); impl_case!(case_7051277471570131en16_int1: "0.7051277471570131" - (int)"1" => "-0.2948722528429869"); impl_case!(case_4068603022763836en8_intneg10: "40686030.22763836" - (int)"-10" => "40686040.22763836"); #[cfg(property_tests)] mod prop { use super::*; use proptest::*; use num_traits::FromPrimitive; proptest! { #[test] fn sub_refs_and_owners(f: f32, g: f32) { // ignore non-normal numbers prop_assume!(f.is_normal()); prop_assume!(g.is_normal()); let a = BigDecimal::from_f32(f).unwrap(); let b = BigDecimal::from_f32(g).unwrap(); let own_minus_ref = a.clone() - &b; let ref_minus_own = &a - b.clone(); let mut c = a.clone(); c -= &b; let mut d = a.clone(); d -= b; prop_assert_eq!(&own_minus_ref, &ref_minus_own); prop_assert_eq!(&c, &ref_minus_own); prop_assert_eq!(&d, &ref_minus_own); } #[test] fn subtraction_is_anticommunative(f: f32, g: f32) { // ignore non-normal numbers prop_assume!(f.is_normal()); prop_assume!(g.is_normal()); let a = BigDecimal::from_f32(f).unwrap(); let b = BigDecimal::from_f32(g).unwrap(); let a_minus_b = &a - &b; let b_minus_a = &b - &a; prop_assert_eq!(a_minus_b, -b_minus_a) } } } } bigdecimal-0.4.7/src/impl_serde.rs000064400000000000000000000351771046102023000152110ustar 00000000000000//! //! Support for serde implementations //! use crate::*; use serde_crate::{self as serde, de, ser, Serialize, Deserialize}; // const SERDE_SCALE_LIMIT: usize = = ${RUST_BIGDECIMAL_SERDE_SCALE_LIMIT} or 150_000; #[cfg(feature = "serde_json")] include!(concat!(env!("OUT_DIR"), "/serde_scale_limit.rs")); impl ser::Serialize for BigDecimal { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { serializer.collect_str(&self) } } /// Used by SerDe to construct a BigDecimal struct BigDecimalVisitor; impl<'de> de::Visitor<'de> for BigDecimalVisitor { type Value = BigDecimal; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a number or formatted decimal string") } fn visit_str(self, value: &str) -> Result where E: de::Error, { BigDecimal::from_str(value).map_err(|err| E::custom(format!("{}", err))) } fn visit_u64(self, value: u64) -> Result where E: de::Error, { Ok(BigDecimal::from(value)) } fn visit_i64(self, value: i64) -> Result where E: de::Error, { Ok(BigDecimal::from(value)) } fn visit_u128(self, value: u128) -> Result where E: de::Error, { Ok(BigDecimal::from(value)) } fn visit_i128(self, value: i128) -> Result where E: de::Error, { Ok(BigDecimal::from(value)) } fn visit_f32(self, value: f32) -> Result where E: de::Error, { BigDecimal::try_from(value).map_err(|err| E::custom(format!("{}", err))) } fn visit_f64(self, value: f64) -> Result where E: de::Error, { BigDecimal::try_from(value).map_err(|err| E::custom(format!("{}", err))) } fn visit_map(self, mut map: A) -> Result where A: de::MapAccess<'de>, { match map.next_key::<&str>() { Ok(Some("$serde_json::private::Number")) => { map.next_value::() } _ => { Err(de::Error::invalid_type(de::Unexpected::Map, &self)) } } } } #[cfg(not(feature = "string-only"))] impl<'de> de::Deserialize<'de> for BigDecimal { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { d.deserialize_any(BigDecimalVisitor) } } #[cfg(feature = "string-only")] impl<'de> de::Deserialize<'de> for BigDecimal { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { d.deserialize_str(BigDecimalVisitor) } } #[cfg(test)] mod test { use super::*; use paste::paste; use serde_test::{ Token, assert_tokens, assert_de_tokens, assert_de_tokens_error }; mod serde_serialize_deserialize_str { use super::*; macro_rules! impl_case { ($name:ident : $input:literal => $output:literal) => { #[test] fn $name() { let expected = Token::Str($output); let decimal: BigDecimal = $input.parse().unwrap(); assert_tokens(&decimal, &[expected]); } } } impl_case!(case_1d0: "1.0" => "1.0"); impl_case!(case_0d5: "0.5" => "0.5"); impl_case!(case_50: "50" => "50"); impl_case!(case_50000: "50000" => "50000"); impl_case!(case_1en3: "1e-3" => "0.001"); impl_case!(case_10e11: "10e11" => "1000000000000"); impl_case!(case_d25: ".25" => "0.25"); impl_case!(case_12d34e1: "12.34e1" => "123.4"); impl_case!(case_40d0010: "40.0010" => "40.0010"); } #[cfg(not(feature = "string-only"))] mod serde_deserialize_int { use super::*; macro_rules! impl_case { ( $( $ttype:ident ),+ : -$input:literal ) => { $( paste! { impl_case!([< case_n $input _ $ttype:lower >] : $ttype : -$input); } )* }; ( $( $ttype:ident ),+ : $input:literal ) => { $( paste! { impl_case!([< case_ $input _ $ttype:lower >] : $ttype : $input); } )* }; ($name:ident : $type:ident : $input:literal) => { #[test] fn $name() { let expected = BigDecimal::from($input); let token = Token::$type($input); assert_de_tokens(&expected, &[token]); } }; } impl_case!(I8, I16, I32, I64, U8, U16, U32, U64 : 0); impl_case!(I8, I16, I32, I64, U8, U16, U32, U64 : 1); impl_case!(I8, I16, I32, I64 : -1); impl_case!(I64: -99999999999i64); impl_case!(I64: -9_223_372_036_854_775_808i64); } #[cfg(not(feature = "string-only"))] mod serde_deserialize_float { use super::*; macro_rules! impl_case { ( $name:ident : $input:literal => $ttype:ident : $expected:literal ) => { paste! { #[test] fn [< $name _ $ttype:lower >]() { let expected: BigDecimal = $expected.parse().unwrap(); let token = Token::$ttype($input); assert_de_tokens(&expected, &[token]); } } }; ( $name:ident : $input:literal => $( $ttype:ident : $expected:literal )+ ) => { $( impl_case!($name : $input => $ttype : $expected); )* }; ( $name:ident : $input:literal => $( $ttype:ident ),+ : $expected:literal ) => { $( impl_case!($name : $input => $ttype : $expected); )* }; } impl_case!(case_1d0 : 1.0 => F32, F64 : "1"); impl_case!(case_1d1 : 1.1 => F32 : "1.10000002384185791015625" F64 : "1.100000000000000088817841970012523233890533447265625"); impl_case!(case_0d001834988943300: 0.001834988943300 => F32 : "0.001834988943301141262054443359375" F64 : "0.00183498894330000003084768511740776375518180429935455322265625"); impl_case!(case_n869651d9131236838: -869651.9131236838 => F32 : "-869651.9375" F64 : "-869651.91312368377111852169036865234375"); impl_case!(case_n1en20: -1e-20 => F32 : "-9.999999682655225388967887463487205224055287544615566730499267578125E-21" F64 : "-999999999999999945153271454209571651729503702787392447107715776066783064379706047475337982177734375e-119"); } #[cfg(not(feature = "string-only"))] mod serde_deserialize_nan { use super::*; #[test] fn case_f32() { let tokens = [ Token::F32(f32::NAN) ]; assert_de_tokens_error::(&tokens, "NAN"); } #[test] fn case_f64() { let tokens = [ Token::F64(f64::NAN) ]; assert_de_tokens_error::(&tokens, "NAN"); } } #[cfg(feature = "serde_json")] mod json_support { use super::*; use impl_serde::{Serialize, Deserialize}; use serde_json; #[derive(Serialize,Deserialize)] struct TestStruct { name: String, value: BigDecimal, #[serde(with = "crate::serde::json_num")] number: BigDecimal, } #[test] fn test_struct_parsing() { let json_src = r#" { "name": "foo", "value": 0.0008741329382918, "number": "12.34" } "#; let my_struct: TestStruct = serde_json::from_str(&json_src).unwrap(); assert_eq!(&my_struct.name, "foo"); assert_eq!(&my_struct.value, &"0.0008741329382918".parse().unwrap()); assert_eq!(&my_struct.number, &"12.34".parse().unwrap()); let s = serde_json::to_string(&my_struct).unwrap(); assert_eq!(s, r#"{"name":"foo","value":"0.0008741329382918","number":12.34}"#); } } } /// Serialize/deserialize [`BigDecimal`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`. /// // The following example is ignored as it requires derives which we don't import and aren't compatible // with our locked versions of rust due to proc_macro2. /// ```ignore /// # extern crate serde; /// # use serde::{Serialize, Deserialize}; /// # use bigdecimal::BigDecimal; /// # use std::str::FromStr; /// /// #[derive(Serialize, Deserialize)] /// pub struct ArbitraryExample { /// #[serde(with = "bigdecimal::serde::json_num")] /// value: BigDecimal, /// } /// /// let value = ArbitraryExample { value: BigDecimal::from_str("123.400").unwrap() }; /// assert_eq!( /// &serde_json::to_string(&value).unwrap(), /// r#"{"value":123.400}"# /// ); /// ``` #[cfg(feature = "serde_json")] pub mod arbitrary_precision { use super::*; pub fn deserialize<'de, D>(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { let n = BigDecimal::deserialize(deserializer)?; if n.scale.abs() > SERDE_SCALE_LIMIT && SERDE_SCALE_LIMIT > 0 { let msg = format!("Calculated exponent '{}' out of bounds", -n.scale); Err(serde::de::Error::custom(msg)) } else { Ok(n) } } pub fn serialize(value: &BigDecimal, serializer: S) -> Result where S: serde::Serializer, { serde_json::Number::from_str(&value.to_string()) .map_err(ser::Error::custom)? .serialize(serializer) } } /// Serialize/deserialize [`Option`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`. /// // The following example is ignored as it requires derives which we don't import and aren't compatible // with our locked versions of rust due to proc_macro2. /// ```ignore /// # extern crate serde; /// # use serde::{Serialize, Deserialize}; /// # use bigdecimal::BigDecimal; /// # use std::str::FromStr; /// /// #[derive(Serialize, Deserialize)] /// pub struct ArbitraryExample { /// #[serde(with = "bigdecimal::impl_serde::arbitrary_precision_option")] /// value: Option, /// } /// /// let value = ArbitraryExample { value: Some(BigDecimal::from_str("123.400").unwrap()) }; /// assert_eq!( /// &serde_json::to_string(&value).unwrap(), /// r#"{"value":123.400}"# /// ); /// /// let value = ArbitraryExample { value: None }; /// assert_eq!( /// &serde_json::to_string(&value).unwrap(), /// r#"{"value":null}"# /// ); /// ``` #[cfg(feature = "serde_json")] pub mod arbitrary_precision_option { use super::*; pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::de::Deserializer<'de>, { Option::::deserialize(deserializer)? .map(|num| num.as_str().parse().map_err(serde::de::Error::custom)) .transpose() } pub fn serialize(value: &Option, serializer: S) -> Result where S: serde::Serializer, { match *value { Some(ref decimal) => { serde_json::Number::from_str(&decimal.to_string()) .map_err(serde::ser::Error::custom)? .serialize(serializer) } None => serializer.serialize_none(), } } } #[cfg(all(test, feature = "serde_json"))] mod test_jsonification { use super::*; extern crate serde_json; mod deserialize_f64 { use super::*; macro_rules! impl_case { ($name:ident : $input:expr) => { impl_case!($name: $input => $input); }; ($name:ident : $input:expr => $expected:literal) => { #[test] fn $name() { let mut json_deserialize = serde_json::Deserializer::from_str($input); let value = arbitrary_precision::deserialize(&mut json_deserialize) .expect("should parse JSON"); let expected: BigDecimal = $expected.parse().unwrap(); assert_eq!(expected, value); } }; } impl_case!(case_1d0: "1.0" => "1.0"); impl_case!(case_0d001: "0.001" => "0.001"); impl_case!(case_41009d2207etc: "41009.22075436852032769878903135430037010"); } mod serde_struct_decimals { use super::*; use crate as bigdecimal; #[derive(Serialize, Deserialize)] pub struct TestExample { #[serde(with = "bigdecimal::serde::json_num")] value: BigDecimal, } macro_rules! impl_case { ($name:ident : $input:expr => (error)) => { #[test] fn $name() { let res = serde_json::from_str::($input); assert!(res.is_err()); } }; ($name:ident : $input:expr => $expected:expr) => { #[test] fn $name() { let obj: TestExample = serde_json::from_str($input).unwrap(); let expected: BigDecimal = $expected.parse().unwrap(); assert_eq!(expected, obj.value); // expect output to be input with all spaces removed let s = serde_json::to_string(&obj).unwrap(); let input_stripped = $input.replace(" ", ""); assert_eq!(&s, &input_stripped); } }; } impl_case!(case_1d0: r#"{ "value": 1.0 }"# => "1.0" ); impl_case!(case_2d01: r#"{ "value": 2.01 }"# => "2.01" ); impl_case!(case_50: r#"{ "value": 50 }"# => "50" ); impl_case!(case_high_prec: r#"{ "value": 64771126779.35857825871133263810255301911 }"# => "64771126779.35857825871133263810255301911" ); impl_case!(case_nan: r#"{ "value": nan }"# => (error) ); #[test] fn scale_out_of_bounds() { use serde_crate::de::Error; let src = r#"{ "value": 1e92233720392233 }"#; let parse_err = serde_json::from_str::(&src).err().unwrap(); let err_str = parse_err.to_string(); assert!(err_str.starts_with("Calculated exponent '92233720392233' out of bounds"), "{}", err_str); } } } bigdecimal-0.4.7/src/impl_trait_from_str.rs000064400000000000000000000055061046102023000171360ustar 00000000000000use crate::*; use stdlib::str::FromStr; impl FromStr for BigDecimal { type Err = ParseBigDecimalError; #[inline] fn from_str(s: &str) -> Result { // implemented in impl_num.rs BigDecimal::from_str_radix(s, 10) } } #[cfg(test)] mod tests { use super::*; macro_rules! impl_case { ($name:ident: $input:literal => $int:literal E $exp:literal) => { #[test] fn $name() { let dec = BigDecimal::from_str($input).unwrap(); assert_eq!(dec.int_val, $int.into()); assert_eq!(dec.scale, -($exp)); } }; } impl_case!(case_1331d107: "1331.107" => 1331107 E -3 ); impl_case!(case_1d0: "1.0" => 10 E -1 ); impl_case!(case_2e1: "2e1" => 2 E 1 ); impl_case!(case_0d00123: "0.00123" => 123 E -5); impl_case!(case_n123: "-123" => -123 E -0); impl_case!(case_n1230: "-1230" => -1230 E -0); impl_case!(case_12d3: "12.3" => 123 E -1); impl_case!(case_123en1: "123e-1" => 123 E -1); impl_case!(case_1d23ep1: "1.23e+1" => 123 E -1); impl_case!(case_1d23ep3: "1.23E+3" => 123 E 1); impl_case!(case_1d23en8: "1.23E-8" => 123 E -10); impl_case!(case_n1d23en10: "-1.23E-10" => -123 E -12); impl_case!(case_123_: "123_" => 123 E -0); impl_case!(case_31_862_140d830_686_979: "31_862_140.830_686_979" => 31862140830686979i128 E -9); impl_case!(case_n1_1d2_2: "-1_1.2_2" => -1122 E -2); impl_case!(case_999d521_939: "999.521_939" => 999521939 E -6); impl_case!(case_679d35_84_03en2: "679.35_84_03E-2" => 679358403 E -8); impl_case!(case_271576662d_e4: "271576662.__E4" => 271576662 E 4); impl_case!(case_1_d_2: "1_._2" => 12 E -1); } #[cfg(test)] mod test_invalid { use super::*; macro_rules! impl_case { ($name:ident: $input:literal => $exp:literal) => { #[test] #[should_panic(expected = $exp)] fn $name() { BigDecimal::from_str($input).unwrap(); } }; } impl_case!(case_bad_string_empty : "" => "Empty"); impl_case!(case_bad_string_empty_exponent : "123.123E" => "Empty"); impl_case!(case_bad_string_only_decimal_point : "." => "Empty"); impl_case!(test_bad_string_only_decimal_and_exponent : ".e4" => "Empty"); impl_case!(test_bad_string_only_decimal_and_underscore : "_._" => "InvalidDigit"); impl_case!(case_bad_string_hello : "hello" => "InvalidDigit"); impl_case!(case_bad_string_nan : "nan" => "InvalidDigit"); impl_case!(case_bad_string_invalid_char : "12z3.12" => "InvalidDigit"); impl_case!(case_bad_string_nan_exponent : "123.123eg" => "InvalidDigit"); impl_case!(case_bad_string_multiple_decimal_points : "123.12.45" => "InvalidDigit"); impl_case!(case_bad_string_hex : "0xCafeBeef" => "InvalidDigit"); } bigdecimal-0.4.7/src/lib.rs000064400000000000000000002122101046102023000136150ustar 00000000000000// Copyright 2016 Adam Sunderland // 2016-2023 Andrew Kubera // 2017 Ruben De Smet // See the COPYRIGHT file at the top-level directory of this // distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A Big Decimal //! //! `BigDecimal` allows storing any real number to arbitrary precision; which //! avoids common floating point errors (such as 0.1 + 0.2 ≠ 0.3) at the //! cost of complexity. //! //! Internally, `BigDecimal` uses a `BigInt` object, paired with a 64-bit //! integer which determines the position of the decimal point. Therefore, //! the precision *is not* actually arbitrary, but limited to 263 //! decimal places. //! //! Common numerical operations are overloaded, so we can treat them //! the same way we treat other numbers. //! //! It is not recommended to convert a floating point number to a decimal //! directly, as the floating point representation may be unexpected. //! //! # Example //! //! ``` //! use bigdecimal::BigDecimal; //! use std::str::FromStr; //! //! let input = "0.8"; //! let dec = BigDecimal::from_str(&input).unwrap(); //! let float = f32::from_str(&input).unwrap(); //! //! println!("Input ({}) with 10 decimals: {} vs {})", input, dec, float); //! ``` #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::style)] #![allow(clippy::excessive_precision)] #![allow(clippy::unreadable_literal)] #![allow(clippy::unusual_byte_groupings)] #![allow(clippy::needless_late_init)] #![allow(clippy::needless_return)] #![allow(clippy::suspicious_arithmetic_impl)] #![allow(clippy::suspicious_op_assign_impl)] #![allow(clippy::redundant_field_names)] #![allow(clippy::approx_constant)] #![allow(clippy::wrong_self_convention)] #![cfg_attr(test, allow(clippy::useless_vec))] #![allow(unused_imports)] pub extern crate num_bigint; pub extern crate num_traits; extern crate num_integer; #[cfg(test)] extern crate paste; #[cfg(feature = "serde")] extern crate serde as serde_crate; #[cfg(all(test, any(feature = "serde", feature = "serde_json")))] extern crate serde_test; #[cfg(all(test, feature = "serde_json"))] extern crate serde_json; #[cfg(feature = "std")] include!("./with_std.rs"); #[cfg(not(feature = "std"))] include!("./without_std.rs"); // make available some standard items use self::stdlib::cmp::{self, Ordering}; use self::stdlib::convert::TryFrom; use self::stdlib::default::Default; use self::stdlib::hash::{Hash, Hasher}; use self::stdlib::num::{ParseFloatError, ParseIntError}; use self::stdlib::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign, Rem, RemAssign, }; use self::stdlib::iter::Sum; use self::stdlib::str::FromStr; use self::stdlib::string::{String, ToString}; use self::stdlib::fmt; use self::stdlib::Vec; use self::stdlib::borrow::Cow; use num_bigint::{BigInt, BigUint, ParseBigIntError, Sign}; use num_integer::Integer as IntegerTrait; pub use num_traits::{FromPrimitive, Num, One, Pow, Signed, ToPrimitive, Zero}; use stdlib::f64::consts::LOG2_10; // const DEFAULT_PRECISION: u64 = ${RUST_BIGDECIMAL_DEFAULT_PRECISION} or 100; include!(concat!(env!("OUT_DIR"), "/default_precision.rs")); #[macro_use] mod macros; // "low level" functions mod arithmetic; // From, To, TryFrom impls mod impl_convert; mod impl_trait_from_str; // Add, Sub, etc... mod impl_ops; mod impl_ops_add; mod impl_ops_sub; mod impl_ops_mul; mod impl_ops_div; mod impl_ops_rem; // PartialEq mod impl_cmp; // Implementations of num_traits mod impl_num; // Implementations of std::fmt traits and stringificaiton routines mod impl_fmt; // Implementations for deserializations and serializations #[cfg(any(feature = "serde", feature="serde_json"))] pub mod impl_serde; /// re-export serde-json derive modules #[cfg(feature = "serde_json")] pub mod serde { /// Parse JSON number directly to BigDecimal pub use impl_serde::arbitrary_precision as json_num; /// Parse JSON (number | null) directly to Option pub use impl_serde::arbitrary_precision_option as json_num_option; } // construct BigDecimals from strings and floats mod parsing; // Routines for rounding pub mod rounding; pub use rounding::RoundingMode; // Mathematical context mod context; pub use context::Context; use arithmetic::{ ten_to_the, ten_to_the_uint, ten_to_the_u64, diff, diff_usize, count_decimal_digits, count_decimal_digits_uint, }; /// Internal function used for rounding /// /// returns 1 if most significant digit is >= 5, otherwise 0 /// /// This is used after dividing a number by a power of ten and /// rounding the last digit. /// #[inline(always)] fn get_rounding_term(num: &BigInt) -> u8 { if num.is_zero() { return 0; } let digits = (num.bits() as f64 / LOG2_10) as u64; let mut n = ten_to_the(digits); // loop-method loop { if *num < n { return 1; } n *= 5; if *num < n { return 0; } n *= 2; } // string-method // let s = format!("{}", num); // let high_digit = u8::from_str(&s[0..1]).unwrap(); // if high_digit < 5 { 0 } else { 1 } } /// A big decimal type. /// #[derive(Clone, Eq)] pub struct BigDecimal { int_val: BigInt, // A positive scale means a negative power of 10 scale: i64, } #[cfg(not(feature = "std"))] // f64::exp2 is only available in std, we have to use an external crate like libm fn exp2(x: f64) -> f64 { libm::exp2(x) } #[cfg(feature = "std")] fn exp2(x: f64) -> f64 { x.exp2() } impl BigDecimal { /// Creates and initializes a `BigDecimal`. /// /// The more explicit method `from_bigint` should be preferred, as new /// may change in the future. /// #[inline] pub fn new(digits: BigInt, scale: i64) -> BigDecimal { BigDecimal::from_bigint(digits, scale) } /// Construct BigDecimal from BigInt and a scale pub fn from_bigint(digits: BigInt, scale: i64) -> BigDecimal { BigDecimal { int_val: digits, scale: scale, } } /// Construct positive BigDecimal from BigUint and a scale pub fn from_biguint(digits: BigUint, scale: i64) -> BigDecimal { let n = BigInt::from_biguint(Sign::Plus, digits); BigDecimal::from_bigint(n, scale) } /// Make a BigDecimalRef of this value pub fn to_ref(&self) -> BigDecimalRef<'_> { // search for "From<&'a BigDecimal> for BigDecimalRef<'a>" self.into() } /// Returns the scale of the BigDecimal, the total number of /// digits to the right of the decimal point (including insignificant /// leading zeros) /// /// # Examples /// /// ``` /// use bigdecimal::BigDecimal; /// use std::str::FromStr; /// /// let a = BigDecimal::from(12345); // No fractional part /// let b = BigDecimal::from_str("123.45").unwrap(); // Fractional part /// let c = BigDecimal::from_str("0.0000012345").unwrap(); // Completely fractional part /// let d = BigDecimal::from_str("5e9").unwrap(); // Negative-fractional part /// /// assert_eq!(a.fractional_digit_count(), 0); /// assert_eq!(b.fractional_digit_count(), 2); /// assert_eq!(c.fractional_digit_count(), 10); /// assert_eq!(d.fractional_digit_count(), -9); /// ``` #[inline] pub fn fractional_digit_count(&self) -> i64 { self.scale } /// Creates and initializes a `BigDecimal`. /// /// Decodes using `str::from_utf8` and forwards to `BigDecimal::from_str_radix`. /// Only base-10 is supported. /// /// # Examples /// /// ``` /// use bigdecimal::{BigDecimal, Zero}; /// /// assert_eq!(BigDecimal::parse_bytes(b"0", 10).unwrap(), BigDecimal::zero()); /// assert_eq!(BigDecimal::parse_bytes(b"13", 10).unwrap(), BigDecimal::from(13)); /// ``` #[inline] pub fn parse_bytes(buf: &[u8], radix: u32) -> Option { stdlib::str::from_utf8(buf) .ok() .and_then(|s| BigDecimal::from_str_radix(s, radix).ok()) } /// Return a new BigDecimal object equivalent to self, with internal /// scaling set to the number specified. /// If the new_scale is lower than the current value (indicating a larger /// power of 10), digits will be dropped (as precision is lower) /// #[inline] pub fn with_scale(&self, new_scale: i64) -> BigDecimal { if self.int_val.is_zero() { return BigDecimal::new(BigInt::zero(), new_scale); } match new_scale.cmp(&self.scale) { Ordering::Greater => { let scale_diff = new_scale - self.scale; let int_val = &self.int_val * ten_to_the(scale_diff as u64); BigDecimal::new(int_val, new_scale) } Ordering::Less => { let scale_diff = self.scale - new_scale; let int_val = &self.int_val / ten_to_the(scale_diff as u64); BigDecimal::new(int_val, new_scale) } Ordering::Equal => self.clone(), } } /// Return a new BigDecimal after shortening the digits and rounding /// /// ``` /// # use bigdecimal::*; /// /// let n: BigDecimal = "129.41675".parse().unwrap(); /// /// assert_eq!(n.with_scale_round(2, RoundingMode::Up), "129.42".parse().unwrap()); /// assert_eq!(n.with_scale_round(-1, RoundingMode::Down), "120".parse().unwrap()); /// assert_eq!(n.with_scale_round(4, RoundingMode::HalfEven), "129.4168".parse().unwrap()); /// ``` pub fn with_scale_round(&self, new_scale: i64, mode: RoundingMode) -> BigDecimal { use stdlib::cmp::Ordering::*; if self.int_val.is_zero() { return BigDecimal::new(BigInt::zero(), new_scale); } match new_scale.cmp(&self.scale) { Ordering::Equal => { self.clone() } Ordering::Greater => { // increase number of zeros let scale_diff = new_scale - self.scale; let int_val = &self.int_val * ten_to_the(scale_diff as u64); BigDecimal::new(int_val, new_scale) } Ordering::Less => { let (sign, mut digits) = self.int_val.to_radix_le(10); let digit_count = digits.len(); let int_digit_count = digit_count as i64 - self.scale; let rounded_int = match int_digit_count.cmp(&-new_scale) { Equal => { let (&last_digit, remaining) = digits.split_last().unwrap(); let trailing_zeros = remaining.iter().all(Zero::is_zero); let rounded_digit = mode.round_pair(sign, (0, last_digit), trailing_zeros); BigInt::new(sign, vec![rounded_digit as u32]) } Less => { debug_assert!(!digits.iter().all(Zero::is_zero)); let rounded_digit = mode.round_pair(sign, (0, 0), false); BigInt::new(sign, vec![rounded_digit as u32]) } Greater => { // location of new rounding point let scale_diff = (self.scale - new_scale) as usize; let low_digit = digits[scale_diff - 1]; let high_digit = digits[scale_diff]; let trailing_zeros = digits[0..scale_diff-1].iter().all(Zero::is_zero); let rounded_digit = mode.round_pair(sign, (high_digit, low_digit), trailing_zeros); debug_assert!(rounded_digit <= 10); if rounded_digit < 10 { digits[scale_diff] = rounded_digit; } else { digits[scale_diff] = 0; let mut i = scale_diff + 1; loop { if i == digit_count { digits.push(1); break; } if digits[i] < 9 { digits[i] += 1; break; } digits[i] = 0; i += 1; } } BigInt::from_radix_le(sign, &digits[scale_diff..], 10).unwrap() } }; BigDecimal::new(rounded_int, new_scale) } } } /// Return a new BigDecimal object with same value and given scale, /// padding with zeros or truncating digits as needed /// /// Useful for aligning decimals before adding/subtracting. /// fn take_and_scale(mut self, new_scale: i64) -> BigDecimal { self.set_scale(new_scale); self } /// Change to requested scale by multiplying or truncating fn set_scale(&mut self, new_scale: i64) { if self.int_val.is_zero() { self.scale = new_scale; return; } match diff(new_scale, self.scale) { (Ordering::Greater, scale_diff) => { self.scale = new_scale; if scale_diff < 20 { self.int_val *= ten_to_the_u64(scale_diff as u8); } else { self.int_val *= ten_to_the(scale_diff); } } (Ordering::Less, scale_diff) => { self.scale = new_scale; if scale_diff < 20 { self.int_val /= ten_to_the_u64(scale_diff as u8); } else { self.int_val /= ten_to_the(scale_diff); } } (Ordering::Equal, _) => {}, } } /// set scale only if new_scale is greater than current pub(crate) fn extend_scale_to(&mut self, new_scale: i64) { if new_scale > self.scale { self.set_scale(new_scale) } } /// Take and return bigdecimal with the given sign /// /// The Sign value `NoSign` is ignored: only use Plus & Minus /// pub(crate) fn take_with_sign(self, sign: Sign) -> BigDecimal { let BigDecimal { scale, mut int_val } = self; if int_val.sign() != sign && sign != Sign::NoSign { int_val = int_val.neg(); } BigDecimal { int_val: int_val, scale: scale, } } /// Return a new BigDecimal object with precision set to new value /// /// ``` /// # use bigdecimal::*; /// /// let n: BigDecimal = "129.41675".parse().unwrap(); /// /// assert_eq!(n.with_prec(2), "130".parse().unwrap()); /// /// let n_p12 = n.with_prec(12); /// let (i, scale) = n_p12.as_bigint_and_exponent(); /// assert_eq!(n_p12, "129.416750000".parse().unwrap()); /// assert_eq!(i, 129416750000_u64.into()); /// assert_eq!(scale, 9); /// ``` pub fn with_prec(&self, prec: u64) -> BigDecimal { let digits = self.digits(); match digits.cmp(&prec) { Ordering::Greater => { let diff = digits - prec; let p = ten_to_the(diff); let (mut q, r) = self.int_val.div_rem(&p); // check for "leading zero" in remainder term; otherwise round if p < 10 * &r { q += get_rounding_term(&r); } BigDecimal { int_val: q, scale: self.scale - diff as i64, } } Ordering::Less => { let diff = prec - digits; BigDecimal { int_val: &self.int_val * ten_to_the(diff), scale: self.scale + diff as i64, } } Ordering::Equal => self.clone(), } } /// Return this BigDecimal with the given precision, rounding if needed #[cfg(rustc_1_46)] // Option::zip #[allow(clippy::incompatible_msrv)] pub fn with_precision_round(&self, prec: stdlib::num::NonZeroU64, round: RoundingMode) -> BigDecimal { let digit_count = self.digits(); let new_prec = prec.get().to_i64(); let new_scale = new_prec .zip(digit_count.to_i64()) .and_then(|(new_prec, old_prec)| new_prec.checked_sub(old_prec)) .and_then(|prec_diff| self.scale.checked_add(prec_diff)) .expect("precision overflow"); self.with_scale_round(new_scale, round) } #[cfg(not(rustc_1_46))] pub fn with_precision_round(&self, prec: stdlib::num::NonZeroU64, round: RoundingMode) -> BigDecimal { let new_scale = self.digits().to_i64().and_then( |old_prec| { prec.get().to_i64().and_then( |new_prec| { new_prec.checked_sub(old_prec) })}) .and_then(|prec_diff| self.scale.checked_add(prec_diff)) .expect("precision overflow"); self.with_scale_round(new_scale, round) } /// Return the sign of the `BigDecimal` as `num::bigint::Sign`. /// /// ``` /// # use bigdecimal::{BigDecimal, num_bigint::Sign}; /// /// fn sign_of(src: &str) -> Sign { /// let n: BigDecimal = src.parse().unwrap(); /// n.sign() /// } /// /// assert_eq!(sign_of("-1"), Sign::Minus); /// assert_eq!(sign_of("0"), Sign::NoSign); /// assert_eq!(sign_of("1"), Sign::Plus); /// ``` #[inline] pub fn sign(&self) -> num_bigint::Sign { self.int_val.sign() } /// Return the internal big integer value and an exponent. Note that a positive /// exponent indicates a negative power of 10. /// /// # Examples /// /// ``` /// use bigdecimal::{BigDecimal, num_bigint::BigInt}; /// /// let n: BigDecimal = "1.23456".parse().unwrap(); /// let expected = ("123456".parse::().unwrap(), 5); /// assert_eq!(n.as_bigint_and_exponent(), expected); /// ``` #[inline] pub fn as_bigint_and_exponent(&self) -> (BigInt, i64) { (self.int_val.clone(), self.scale) } /// Take BigDecimal and split into `num::BigInt` of digits, and the scale /// /// Scale is number of digits after the decimal point, can be negative. /// pub fn into_bigint_and_scale(self) -> (BigInt, i64) { (self.int_val, self.scale) } /// Return digits as borrowed Cow of integer digits, and its scale /// /// Scale is number of digits after the decimal point, can be negative. /// pub fn as_bigint_and_scale(&self) -> (Cow<'_, BigInt>, i64) { let cow_int = Cow::Borrowed(&self.int_val); (cow_int, self.scale) } /// Convert into the internal big integer value and an exponent. Note that a positive /// exponent indicates a negative power of 10. /// /// # Examples /// /// ``` /// use bigdecimal::{BigDecimal, num_bigint::BigInt}; /// /// let n: BigDecimal = "1.23456".parse().unwrap(); /// let expected = ("123456".parse::().unwrap(), 5); /// assert_eq!(n.into_bigint_and_exponent(), expected); /// ``` #[inline] pub fn into_bigint_and_exponent(self) -> (BigInt, i64) { (self.int_val, self.scale) } /// Number of digits in the non-scaled integer representation /// #[inline] pub fn digits(&self) -> u64 { count_decimal_digits(&self.int_val) } /// Compute the absolute value of number /// /// ``` /// # use bigdecimal::BigDecimal; /// let n: BigDecimal = "123.45".parse().unwrap(); /// assert_eq!(n.abs(), "123.45".parse().unwrap()); /// /// let n: BigDecimal = "-123.45".parse().unwrap(); /// assert_eq!(n.abs(), "123.45".parse().unwrap()); /// ``` #[inline] pub fn abs(&self) -> BigDecimal { BigDecimal { int_val: self.int_val.abs(), scale: self.scale, } } /// Multiply decimal by 2 (efficiently) /// /// ``` /// # use bigdecimal::BigDecimal; /// let n: BigDecimal = "123.45".parse().unwrap(); /// assert_eq!(n.double(), "246.90".parse().unwrap()); /// ``` pub fn double(&self) -> BigDecimal { if self.is_zero() { self.clone() } else { BigDecimal { int_val: self.int_val.clone() * 2, scale: self.scale, } } } /// Divide decimal by 2 (efficiently) /// /// *Note*: If the last digit in the decimal is odd, the precision /// will increase by 1 /// /// ``` /// # use bigdecimal::BigDecimal; /// let n: BigDecimal = "123.45".parse().unwrap(); /// assert_eq!(n.half(), "61.725".parse().unwrap()); /// ``` #[inline] pub fn half(&self) -> BigDecimal { if self.is_zero() { self.clone() } else if self.int_val.is_even() { BigDecimal { int_val: self.int_val.clone().div(2u8), scale: self.scale, } } else { BigDecimal { int_val: self.int_val.clone().mul(5u8), scale: self.scale + 1, } } } /// Square a decimal: *x²* /// /// No rounding or truncating of digits; this is the full result /// of the squaring operation. /// /// *Note*: doubles the scale of bigdecimal, which might lead to /// accidental exponential-complexity if used in a loop. /// /// ``` /// # use bigdecimal::BigDecimal; /// let n: BigDecimal = "1.1156024145937225657484".parse().unwrap(); /// assert_eq!(n.square(), "1.24456874744734405154288399835406316085210256".parse().unwrap()); /// /// let n: BigDecimal = "-9.238597585E+84".parse().unwrap(); /// assert_eq!(n.square(), "8.5351685337567832225E+169".parse().unwrap()); /// ``` pub fn square(&self) -> BigDecimal { if self.is_zero() || self.is_one() { self.clone() } else { BigDecimal { int_val: self.int_val.clone() * &self.int_val, scale: self.scale * 2, } } } /// Cube a decimal: *x³* /// /// No rounding or truncating of digits; this is the full result /// of the cubing operation. /// /// *Note*: triples the scale of bigdecimal, which might lead to /// accidental exponential-complexity if used in a loop. /// /// ``` /// # use bigdecimal::BigDecimal; /// let n: BigDecimal = "1.1156024145937225657484".parse().unwrap(); /// assert_eq!(n.cube(), "1.388443899780141911774491376394890472130000455312878627147979955904".parse().unwrap()); /// /// let n: BigDecimal = "-9.238597585E+84".parse().unwrap(); /// assert_eq!(n.cube(), "-7.88529874035334084567570176625E+254".parse().unwrap()); /// ``` pub fn cube(&self) -> BigDecimal { if self.is_zero() || self.is_one() { self.clone() } else { BigDecimal { int_val: self.int_val.clone() * &self.int_val * &self.int_val, scale: self.scale * 3, } } } /// Take the square root of the number /// /// Uses default-precision, set from build time environment variable //// `RUST_BIGDECIMAL_DEFAULT_PRECISION` (defaults to 100) /// /// If the value is < 0, None is returned /// /// ``` /// # use bigdecimal::BigDecimal; /// let n: BigDecimal = "1.1156024145937225657484".parse().unwrap(); /// assert_eq!(n.sqrt().unwrap(), "1.056220817156016181190291268045893004363809142172289919023269377496528394924695970851558013658193913".parse().unwrap()); /// /// let n: BigDecimal = "-9.238597585E+84".parse().unwrap(); /// assert_eq!(n.sqrt(), None); /// ``` #[inline] pub fn sqrt(&self) -> Option { self.sqrt_with_context(&Context::default()) } /// Take the square root of the number, using context for precision and rounding /// pub fn sqrt_with_context(&self, ctx: &Context) -> Option { if self.is_zero() || self.is_one() { return Some(self.clone()); } if self.is_negative() { return None; } let uint = self.int_val.magnitude(); let result = arithmetic::sqrt::impl_sqrt(uint, self.scale, ctx); Some(result) } /// Take the cube root of the number, using default context /// #[inline] pub fn cbrt(&self) -> BigDecimal { self.cbrt_with_context(&Context::default()) } /// Take cube root of self, using properties of context pub fn cbrt_with_context(&self, ctx: &Context) -> BigDecimal { if self.is_zero() || self.is_one() { return self.clone(); } arithmetic::cbrt::impl_cbrt_int_scale(&self.int_val, self.scale, ctx) } /// Compute the reciprical of the number: x-1 #[inline] pub fn inverse(&self) -> BigDecimal { self.inverse_with_context(&Context::default()) } /// Return inverse of self, rounding with ctx pub fn inverse_with_context(&self, ctx: &Context) -> BigDecimal { if self.is_zero() || self.is_one() { return self.clone(); } let uint = self.int_val.magnitude(); let result = arithmetic::inverse::impl_inverse_uint_scale(uint, self.scale, ctx); // always copy sign result.take_with_sign(self.sign()) } /// Return given number rounded to 'round_digits' precision after the /// decimal point, using default rounding mode /// /// Default rounding mode is `HalfEven`, but can be configured at compile-time /// by the environment variable: `RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE` /// (or by patching _build.rs_ ) /// pub fn round(&self, round_digits: i64) -> BigDecimal { self.with_scale_round(round_digits, Context::default().rounding_mode()) } /// Return true if this number has zero fractional part (is equal /// to an integer) /// #[inline] pub fn is_integer(&self) -> bool { if self.scale <= 0 { true } else { (self.int_val.clone() % ten_to_the(self.scale as u64)).is_zero() } } /// Evaluate the natural-exponential function ex /// #[inline] pub fn exp(&self) -> BigDecimal { if self.is_zero() { return BigDecimal::one(); } let target_precision = DEFAULT_PRECISION; let precision = self.digits(); let mut term = self.clone(); let mut result = self.clone() + BigDecimal::one(); let mut prev_result = result.clone(); let mut factorial = BigInt::one(); for n in 2.. { term *= self; factorial *= n; // ∑ term=x^n/n! result += impl_division(term.int_val.clone(), &factorial, term.scale, 117 + precision); let trimmed_result = result.with_prec(target_precision + 5); if prev_result == trimmed_result { return trimmed_result.with_prec(target_precision); } prev_result = trimmed_result; } unreachable!("Loop did not converge") } #[must_use] pub fn normalized(&self) -> BigDecimal { if self == &BigDecimal::zero() { return BigDecimal::zero(); } let (sign, mut digits) = self.int_val.to_radix_be(10); let trailing_count = digits.iter().rev().take_while(|i| **i == 0).count(); let trunc_to = digits.len() - trailing_count; digits.truncate(trunc_to); let int_val = BigInt::from_radix_be(sign, &digits, 10).unwrap(); let scale = self.scale - trailing_count as i64; BigDecimal::new(int_val, scale) } ////////////////////////// // Formatting methods /// Create string of decimal in standard decimal notation. /// /// Unlike standard formatter, this never prints the number in /// scientific notation. /// /// # Panics /// If the magnitude of the exponent is _very_ large, this may /// cause out-of-memory errors, or overflowing panics. /// /// # Examples /// ``` /// # use bigdecimal::BigDecimal; /// let n: BigDecimal = "123.45678".parse().unwrap(); /// assert_eq!(&n.to_plain_string(), "123.45678"); /// /// let n: BigDecimal = "1e-10".parse().unwrap(); /// assert_eq!(&n.to_plain_string(), "0.0000000001"); /// ``` pub fn to_plain_string(&self) -> String { let mut output = String::new(); self.write_plain_string(&mut output).expect("Could not write to string"); output } /// Write decimal value in decimal notation to the writer object. /// /// # Panics /// If the exponent is very large or very small, the number of /// this will print that many trailing or leading zeros. /// If exabytes, this will likely panic. /// pub fn write_plain_string(&self, wtr: &mut W) -> fmt::Result { write!(wtr, "{}", impl_fmt::FullScaleFormatter(self.to_ref())) } /// Create string of this bigdecimal in scientific notation /// /// ``` /// # use bigdecimal::BigDecimal; /// let n = BigDecimal::from(12345678); /// assert_eq!(&n.to_scientific_notation(), "1.2345678e7"); /// ``` pub fn to_scientific_notation(&self) -> String { let mut output = String::new(); self.write_scientific_notation(&mut output).expect("Could not write to string"); output } /// Write bigdecimal in scientific notation to writer `w` pub fn write_scientific_notation(&self, w: &mut W) -> fmt::Result { impl_fmt::write_scientific_notation(self, w) } /// Create string of this bigdecimal in engineering notation /// /// Engineering notation is scientific notation with the exponent /// coerced to a multiple of three /// /// ``` /// # use bigdecimal::BigDecimal; /// let n = BigDecimal::from(12345678); /// assert_eq!(&n.to_engineering_notation(), "12.345678e6"); /// ``` /// pub fn to_engineering_notation(&self) -> String { let mut output = String::new(); self.write_engineering_notation(&mut output).expect("Could not write to string"); output } /// Write bigdecimal in engineering notation to writer `w` pub fn write_engineering_notation(&self, w: &mut W) -> fmt::Result { impl_fmt::write_engineering_notation(self, w) } } #[derive(Debug, PartialEq, Clone)] pub enum ParseBigDecimalError { ParseDecimal(ParseFloatError), ParseInt(ParseIntError), ParseBigInt(ParseBigIntError), Empty, Other(String), } impl fmt::Display for ParseBigDecimalError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use ParseBigDecimalError::*; match *self { ParseDecimal(ref e) => e.fmt(f), ParseInt(ref e) => e.fmt(f), ParseBigInt(ref e) => e.fmt(f), Empty => "Failed to parse empty string".fmt(f), Other(ref reason) => reason[..].fmt(f), } } } #[cfg(feature = "std")] impl std::error::Error for ParseBigDecimalError { fn description(&self) -> &str { "failed to parse bigint/biguint" } } impl From for ParseBigDecimalError { fn from(err: ParseFloatError) -> ParseBigDecimalError { ParseBigDecimalError::ParseDecimal(err) } } impl From for ParseBigDecimalError { fn from(err: ParseIntError) -> ParseBigDecimalError { ParseBigDecimalError::ParseInt(err) } } impl From for ParseBigDecimalError { fn from(err: ParseBigIntError) -> ParseBigDecimalError { ParseBigDecimalError::ParseBigInt(err) } } #[allow(deprecated)] // trim_right_match -> trim_end_match impl Hash for BigDecimal { fn hash(&self, state: &mut H) { let mut dec_str = self.int_val.to_str_radix(10); let scale = self.scale; let zero = self.int_val.is_zero(); if scale > 0 && !zero { let mut cnt = 0; dec_str = dec_str .trim_right_matches(|x| { cnt += 1; x == '0' && cnt <= scale }) .to_string(); } else if scale < 0 && !zero { dec_str.push_str(&"0".repeat(self.scale.abs() as usize)); } dec_str.hash(state); } } impl Default for BigDecimal { #[inline] fn default() -> BigDecimal { Zero::zero() } } impl Zero for BigDecimal { #[inline] fn zero() -> BigDecimal { BigDecimal::new(BigInt::zero(), 0) } #[inline] fn is_zero(&self) -> bool { self.int_val.is_zero() } } impl One for BigDecimal { #[inline] fn one() -> BigDecimal { BigDecimal::new(BigInt::one(), 0) } } fn impl_division(mut num: BigInt, den: &BigInt, mut scale: i64, max_precision: u64) -> BigDecimal { // quick zero check if num.is_zero() { return BigDecimal::new(num, 0); } match (num.is_negative(), den.is_negative()) { (true, true) => return impl_division(num.neg(), &den.neg(), scale, max_precision), (true, false) => return -impl_division(num.neg(), den, scale, max_precision), (false, true) => return -impl_division(num, &den.neg(), scale, max_precision), (false, false) => (), } // shift digits until numerator is larger than denominator (set scale appropriately) while num < *den { scale += 1; num *= 10; } // first division let (mut quotient, mut remainder) = num.div_rem(den); // division complete if remainder.is_zero() { return BigDecimal { int_val: quotient, scale: scale, }; } let mut precision = count_decimal_digits("ient); // shift remainder by 1 decimal; // quotient will be 1 digit upon next division remainder *= 10; while !remainder.is_zero() && precision < max_precision { let (q, r) = remainder.div_rem(den); quotient = quotient * 10 + q; remainder = r * 10; precision += 1; scale += 1; } if !remainder.is_zero() { // round final number with remainder quotient += get_rounding_term(&remainder.div(den)); } let result = BigDecimal::new(quotient, scale); // println!(" {} / {}\n = {}\n", self, other, result); return result; } impl Signed for BigDecimal { #[inline] fn abs(&self) -> BigDecimal { match self.sign() { Sign::Plus | Sign::NoSign => self.clone(), Sign::Minus => -self, } } #[inline] fn abs_sub(&self, other: &BigDecimal) -> BigDecimal { if *self <= *other { Zero::zero() } else { self - other } } #[inline] fn signum(&self) -> BigDecimal { match self.sign() { Sign::Plus => One::one(), Sign::NoSign => Zero::zero(), Sign::Minus => -Self::one(), } } #[inline] fn is_positive(&self) -> bool { self.sign() == Sign::Plus } #[inline] fn is_negative(&self) -> bool { self.sign() == Sign::Minus } } impl Sum for BigDecimal { #[inline] fn sum>(iter: I) -> BigDecimal { iter.fold(Zero::zero(), |a, b| a + b) } } impl<'a> Sum<&'a BigDecimal> for BigDecimal { #[inline] fn sum>(iter: I) -> BigDecimal { iter.fold(Zero::zero(), |a, b| a + b) } } /// Immutable big-decimal, referencing a borrowed buffer of digits /// /// The non-digit information like `scale` and `sign` may be changed /// on these objects, which otherwise would require cloning the full /// digit buffer in the BigDecimal. /// /// Built from full `BigDecimal` object using the `to_ref()` method. /// `BigDecimal` not implement `AsRef`, so we will reserve the method /// `as_ref()` for a later time. /// /// May be transformed into full BigDecimal object using the `to_owned()` /// method. /// This clones the bigdecimal digits. /// /// BigDecimalRef (or `Into`) should be preferred over /// using `&BigDecimal` for library functions that need an immutable /// reference to a bigdecimal, as it may be much more efficient. /// /// NOTE: Using `&BigDecimalRef` is redundant, and not recommended. /// /// ## Examples /// /// ``` /// # use bigdecimal::*; use std::ops::Neg; /// fn add_one<'a, N: Into>>(n: N) -> BigDecimal { /// n.into() + 1 /// } /// /// let n: BigDecimal = "123.456".parse().unwrap(); /// /// // call via "standard" reference (implements Into) /// let m = add_one(&n); /// assert_eq!(m, "124.456".parse().unwrap()); /// /// // call by negating the reference (fast: no-digit cloning involved) /// let m = add_one(n.to_ref().neg()); /// assert_eq!(m, "-122.456".parse().unwrap()); /// ``` /// #[derive(Clone, Copy, Debug, Eq)] pub struct BigDecimalRef<'a> { sign: Sign, digits: &'a BigUint, scale: i64, } impl BigDecimalRef<'_> { /// Clone digits to make this reference a full BigDecimal object pub fn to_owned(&self) -> BigDecimal { BigDecimal { scale: self.scale, int_val: BigInt::from_biguint(self.sign, self.digits.clone()), } } /// Clone digits, returning BigDecimal with given scale /// /// ``` /// # use bigdecimal::*; /// /// let n: BigDecimal = "123.45678".parse().unwrap(); /// let r = n.to_ref(); /// assert_eq!(r.to_owned_with_scale(5), n.clone()); /// assert_eq!(r.to_owned_with_scale(0), "123".parse().unwrap()); /// assert_eq!(r.to_owned_with_scale(-1), "12e1".parse().unwrap()); /// /// let x = r.to_owned_with_scale(8); /// assert_eq!(&x, &n); /// assert_eq!(x.fractional_digit_count(), 8); /// ``` pub fn to_owned_with_scale(&self, scale: i64) -> BigDecimal { use stdlib::cmp::Ordering::*; let digits = match arithmetic::diff(self.scale, scale) { (Equal, _) => self.digits.clone(), (Less, scale_diff) => { if scale_diff < 20 { self.digits * ten_to_the_u64(scale_diff as u8) } else { self.digits * ten_to_the_uint(scale_diff) } } (Greater, scale_diff) => { if scale_diff < 20 { self.digits / ten_to_the_u64(scale_diff as u8) } else { self.digits / ten_to_the_uint(scale_diff) } } }; BigDecimal { scale: scale, int_val: BigInt::from_biguint(self.sign, digits), } } /// Borrow digits as Cow pub(crate) fn to_cow_biguint_and_scale(&self) -> (Cow<'_, BigUint>, i64) { let cow_int = Cow::Borrowed(self.digits); (cow_int, self.scale) } /// Sign of decimal pub fn sign(&self) -> Sign { self.sign } /// Return number of digits 'right' of the decimal point /// (including leading zeros) pub fn fractional_digit_count(&self) -> i64 { self.scale } /// Count total number of decimal digits pub fn count_digits(&self) -> u64 { count_decimal_digits_uint(self.digits) } /// Return the number of trailing zeros in the referenced integer #[allow(dead_code)] fn count_trailing_zeroes(&self) -> usize { if self.digits.is_zero() || self.digits.is_odd() { return 0; } let digit_pairs = self.digits.to_radix_le(100); let loc = digit_pairs.iter().position(|&d| d != 0).unwrap_or(0); 2 * loc + usize::from(digit_pairs[loc] % 10 == 0) } /// Split into components pub(crate) fn as_parts(&self) -> (Sign, i64, &BigUint) { (self.sign, self.scale, self.digits) } /// Take absolute value of the decimal (non-negative sign) pub fn abs(&self) -> Self { Self { sign: self.sign * self.sign, digits: self.digits, scale: self.scale, } } /// Create BigDecimal from this reference, rounding to precision and /// with rounding-mode of the given context /// /// pub fn round_with_context(&self, ctx: &Context) -> BigDecimal { ctx.round_decimal_ref(*self) } /// Take square root of this number pub fn sqrt_with_context(&self, ctx: &Context) -> Option { use Sign::*; let (sign, scale, uint) = self.as_parts(); match sign { Minus => None, NoSign => Some(Zero::zero()), Plus => Some(arithmetic::sqrt::impl_sqrt(uint, scale, ctx)), } } /// Take square root of absolute-value of the number pub fn sqrt_abs_with_context(&self, ctx: &Context) -> BigDecimal { use Sign::*; let (_, scale, uint) = self.as_parts(); arithmetic::sqrt::impl_sqrt(uint, scale, ctx) } /// Take square root, copying sign of the initial decimal pub fn sqrt_copysign_with_context(&self, ctx: &Context) -> BigDecimal { use Sign::*; let (sign, scale, uint) = self.as_parts(); let mut result = arithmetic::sqrt::impl_sqrt(uint, scale, ctx); if sign == Minus { result.int_val = result.int_val.neg(); } result } /// Return if the referenced decimal is zero pub fn is_zero(&self) -> bool { self.digits.is_zero() } /// Clone this value into dest pub fn clone_into(&self, dest: &mut BigDecimal) { dest.int_val = num_bigint::BigInt::from_biguint(self.sign, self.digits.clone()); dest.scale = self.scale; } } impl<'a> From<&'a BigDecimal> for BigDecimalRef<'a> { fn from(n: &'a BigDecimal) -> Self { let sign = n.int_val.sign(); let mag = n.int_val.magnitude(); Self { sign: sign, digits: mag, scale: n.scale, } } } impl<'a> From<&'a BigInt> for BigDecimalRef<'a> { fn from(n: &'a BigInt) -> Self { Self { sign: n.sign(), digits: n.magnitude(), scale: 0, } } } /// pair i64 'scale' with some other value #[derive(Clone, Copy)] struct WithScale { pub value: T, pub scale: i64, } impl fmt::Debug for WithScale { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "(scale={} {:?})", self.scale, self.value) } } impl From<(T, i64)> for WithScale { fn from(pair: (T, i64)) -> Self { Self { value: pair.0, scale: pair.1 } } } impl<'a> From> for BigDecimalRef<'a> { fn from(obj: WithScale<&'a BigInt>) -> Self { Self { scale: obj.scale, sign: obj.value.sign(), digits: obj.value.magnitude(), } } } impl<'a> From> for BigDecimalRef<'a> { fn from(obj: WithScale<&'a BigUint>) -> Self { Self { scale: obj.scale, sign: Sign::Plus, digits: obj.value, } } } impl WithScale<&T> { fn is_zero(&self) -> bool { self.value.is_zero() } } #[rustfmt::skip] #[cfg(test)] #[allow(non_snake_case)] mod bigdecimal_tests { use crate::{stdlib, BigDecimal, ToString, FromStr, TryFrom}; use num_traits::{ToPrimitive, FromPrimitive, Signed, Zero, One}; use num_bigint; use paste::paste; mod from_biguint { use super::*; use num_bigint::BigUint; use num_bigint::Sign; macro_rules! impl_case { ($name:ident; $i:literal; $scale:literal) => { impl_case!($name; $i.into(); $scale; Plus); }; ($name:ident; $i:expr; $scale:literal; $sign:ident) => { #[test] fn $name() { let i: BigUint = $i; let d = BigDecimal::from_biguint(i.clone(), $scale); assert_eq!(d.int_val.magnitude(), &i); assert_eq!(d.scale, $scale); assert_eq!(d.sign(), Sign::$sign); } }; } impl_case!(case_0en3; BigUint::zero(); 3; NoSign); impl_case!(case_30e2; 30u8; -2); impl_case!(case_7446124798en5; 7446124798u128; 5); } #[test] fn test_fractional_digit_count() { // Zero value let vals = BigDecimal::from(0); assert_eq!(vals.fractional_digit_count(), 0); assert_eq!(vals.to_ref().fractional_digit_count(), 0); // Fractional part with trailing zeros let vals = BigDecimal::from_str("1.0").unwrap(); assert_eq!(vals.fractional_digit_count(), 1); assert_eq!(vals.to_ref().fractional_digit_count(), 1); // Fractional part let vals = BigDecimal::from_str("1.23").unwrap(); assert_eq!(vals.fractional_digit_count(), 2); assert_eq!(vals.to_ref().fractional_digit_count(), 2); // shifted to 'left' has negative scale let vals = BigDecimal::from_str("123e5").unwrap(); assert_eq!(vals.fractional_digit_count(), -5); assert_eq!(vals.to_ref().fractional_digit_count(), -5); } #[test] fn test_sum() { let vals = vec![ BigDecimal::from_f32(2.5).unwrap(), BigDecimal::from_f32(0.3).unwrap(), BigDecimal::from_f32(0.001).unwrap(), ]; let expected_sum = BigDecimal::from_str("2.801000011968426406383514404296875").unwrap(); let sum = vals.iter().sum::(); assert_eq!(expected_sum, sum); } #[test] fn test_sum1() { let vals = vec![ BigDecimal::from_f32(0.1).unwrap(), BigDecimal::from_f32(0.2).unwrap(), ]; let expected_sum = BigDecimal::from_str("0.300000004470348358154296875").unwrap(); let sum = vals.iter().sum::(); assert_eq!(expected_sum, sum); } #[test] fn test_to_i64() { let vals = vec![ ("12.34", 12), ("3.14", 3), ("50", 50), ("50000", 50000), ("0.001", 0), // TODO: Is the desired behaviour to round? //("0.56", 1), ]; for (s, ans) in vals { let calculated = BigDecimal::from_str(s).unwrap().to_i64().unwrap(); assert_eq!(ans, calculated); } } #[test] fn test_to_i128() { let vals = vec![ ("170141183460469231731687303715884105727", 170141183460469231731687303715884105727), ("-170141183460469231731687303715884105728", -170141183460469231731687303715884105728), ("12.34", 12), ("3.14", 3), ("-123.90", -123), ("50", 50), ("0.001", 0), ]; for (s, ans) in vals { let calculated = BigDecimal::from_str(s).unwrap().to_i128(); assert_eq!(Some(ans), calculated); } } #[test] fn test_to_u128() { let vals = vec![ ("340282366920938463463374607431768211455", 340282366920938463463374607431768211455), ("12.34", 12), ("3.14", 3), ("50", 50), ("0.001", 0), ]; for (s, ans) in vals { let calculated = BigDecimal::from_str(s).unwrap().to_u128().unwrap(); assert_eq!(ans, calculated); } } #[test] fn test_from_i8() { let vals = vec![ ("0", 0), ("1", 1), ("12", 12), ("-13", -13), ("111", 111), ("-128", i8::MIN), ("127", i8::MAX), ]; for (s, n) in vals { let expected = BigDecimal::from_str(s).unwrap(); let value = BigDecimal::from_i8(n).unwrap(); assert_eq!(expected, value); } } #[test] fn test_from_f32() { let vals = vec![ ("0.0", 0.0), ("1.0", 1.0), ("0.5", 0.5), ("0.25", 0.25), ("50.", 50.0), ("50000", 50000.), ("0.001000000047497451305389404296875", 0.001), ("12.340000152587890625", 12.34), ("0.15625", 0.15625), ("3.1415927410125732421875", stdlib::f32::consts::PI), ("31415.927734375", stdlib::f32::consts::PI * 10000.0), ("94247.78125", stdlib::f32::consts::PI * 30000.0), ("1048576", 1048576.), ]; for (s, n) in vals { let expected = BigDecimal::from_str(s).unwrap(); let value = BigDecimal::from_f32(n).unwrap(); assert_eq!(expected, value); } } #[test] fn test_from_f64() { let vals = vec![ ("1.0", 1.0f64), ("0.5", 0.5), ("50", 50.), ("50000", 50000.), ("0.001000000000000000020816681711721685132943093776702880859375", 0.001), ("0.25", 0.25), ("12.339999999999999857891452847979962825775146484375", 12.34), ("0.15625", 5.0 * 0.03125), ("0.333333333333333314829616256247390992939472198486328125", 1.0 / 3.0), ("3.141592653589793115997963468544185161590576171875", stdlib::f64::consts::PI), ("31415.926535897931898944079875946044921875", stdlib::f64::consts::PI * 10000.0f64), ("94247.779607693795696832239627838134765625", stdlib::f64::consts::PI * 30000.0f64), ]; for (s, n) in vals { let expected = BigDecimal::from_str(s).unwrap(); let value = BigDecimal::from_f64(n).unwrap(); assert_eq!(expected, value); // assert_eq!(expected, n); } } #[test] fn test_nan_float() { assert!(BigDecimal::try_from(f32::NAN).is_err()); assert!(BigDecimal::try_from(f64::NAN).is_err()); } mod equals { use super::*; macro_rules! impl_case { ($name:ident: $input_a:literal == $input_b:literal) => { #[test] fn $name() { let a: BigDecimal = $input_a.parse().unwrap(); let b: BigDecimal = $input_b.parse().unwrap(); assert_eq!(&a, &b); assert_eq!(a.clone(), b.clone()); } }; ($name:ident: $input_a:literal != $input_b:literal) => { #[test] fn $name() { let a: BigDecimal = $input_a.parse().unwrap(); let b: BigDecimal = $input_b.parse().unwrap(); assert_ne!(&a, &b); assert_ne!(a.clone(), b.clone()); } }; } impl_case!(case_2: "2" == ".2e1"); impl_case!(case_0e1: "0e1" == "0.0"); impl_case!(case_n0: "-0" == "0.0"); impl_case!(case_n901d3: "-901.3" == "-0.901300e+3"); impl_case!(case_n0901300en3: "-901.3" == "-0901300e-3"); impl_case!(case_2123121e1231: "2123121e1231" == "212.3121e1235"); impl_case!(case_ne_2: "2" != ".2e2"); impl_case!(case_ne_1e45: "1e45" != "1e-900"); impl_case!(case_ne_1e900: "1e+900" != "1e-900"); } #[test] fn test_hash_equal() { use stdlib::DefaultHasher; use stdlib::hash::{Hash, Hasher}; fn hash(obj: &T) -> u64 where T: Hash { let mut hasher = DefaultHasher::new(); obj.hash(&mut hasher); hasher.finish() } let vals = vec![ ("1.1234", "1.1234000"), ("1.12340000", "1.1234"), ("001.1234", "1.1234000"), ("001.1234", "0001.1234"), ("1.1234000000", "1.1234000"), ("1.12340", "1.1234000000"), ("-0901300e-3", "-901.3"), ("-0.901300e+3", "-901.3"), ("100", "100.00"), ("100.00", "100"), ("0.00", "0"), ("0.00", "0.000"), ("-0.00", "0.000"), ("0.00", "-0.000"), ]; for &(x,y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap(); let b = BigDecimal::from_str(y).unwrap(); assert_eq!(a, b); assert_eq!(hash(&a), hash(&b), "hash({}) != hash({})", a, b); } } #[test] fn test_hash_not_equal() { use stdlib::DefaultHasher; use stdlib::hash::{Hash, Hasher}; fn hash(obj: &T) -> u64 where T: Hash { let mut hasher = DefaultHasher::new(); obj.hash(&mut hasher); hasher.finish() } let vals = vec![ ("1.1234", "1.1234001"), ("10000", "10"), ("10", "10000"), ("10.0", "100"), ]; for &(x,y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap(); let b = BigDecimal::from_str(y).unwrap(); assert!(a != b, "{} == {}", a, b); assert!(hash(&a) != hash(&b), "hash({}) == hash({})", a, b); } } #[test] fn test_hash_equal_scale() { use stdlib::DefaultHasher; use stdlib::hash::{Hash, Hasher}; fn hash(obj: &T) -> u64 where T: Hash { let mut hasher = DefaultHasher::new(); obj.hash(&mut hasher); hasher.finish() } let vals = vec![ ("1234.5678", -2, "1200", 0), ("1234.5678", -2, "1200", -2), ("1234.5678", 0, "1234.1234", 0), ("1234.5678", -3, "1200", -3), ("-1234", -2, "-1200", 0), ]; for &(x,xs,y,ys) in vals.iter() { let a = BigDecimal::from_str(x).unwrap().with_scale(xs); let b = BigDecimal::from_str(y).unwrap().with_scale(ys); assert_eq!(a, b); assert_eq!(hash(&a), hash(&b), "hash({}) != hash({})", a, b); } } #[test] fn test_with_prec() { let vals = vec![ ("7", 1, "7"), ("7", 2, "7.0"), ("895", 2, "900"), ("8934", 2, "8900"), ("8934", 1, "9000"), ("1.0001", 5, "1.0001"), ("1.0001", 4, "1"), ("1.00009", 6, "1.00009"), ("1.00009", 5, "1.0001"), ("1.00009", 4, "1.000"), ]; for &(x, p, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap().with_prec(p); assert_eq!(a, BigDecimal::from_str(y).unwrap()); } } #[test] fn test_digits() { let vals = vec![ ("0", 1), ("7", 1), ("10", 2), ("8934", 4), ]; for &(x, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap(); assert_eq!(a.digits(), y); } } #[test] fn test_get_rounding_term() { use num_bigint::BigInt; use super::get_rounding_term; let vals = vec![ ("0", 0), ("4", 0), ("5", 1), ("10", 0), ("15", 0), ("49", 0), ("50", 1), ("51", 1), ("8934", 1), ("9999", 1), ("10000", 0), ("50000", 1), ("99999", 1), ("100000", 0), ("100001", 0), ("10000000000", 0), ("9999999999999999999999999999999999999999", 1), ("10000000000000000000000000000000000000000", 0), ]; for &(x, y) in vals.iter() { let a = BigInt::from_str(x).unwrap(); assert_eq!(get_rounding_term(&a), y, "{}", x); } } #[test] fn test_abs() { let vals = vec![ ("10", "10"), ("-10", "10"), ]; for &(x, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap().abs(); let b = BigDecimal::from_str(y).unwrap(); assert!(a == b, "{} == {}", a, b); } } #[test] fn test_count_decimal_digits() { use num_bigint::BigInt; use super::count_decimal_digits; let vals = vec![ ("10", 2), ("1", 1), ("9", 1), ("999", 3), ("1000", 4), ("9900", 4), ("9999", 4), ("10000", 5), ("99999", 5), ("100000", 6), ("999999", 6), ("1000000", 7), ("9999999", 7), ("999999999999", 12), ("999999999999999999999999", 24), ("999999999999999999999999999999999999999999999999", 48), ("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 96), ("199999911199999999999999999999999999999999999999999999999999999999999999999999999999999999999000", 96), ("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999991", 192), ("199999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 192), ("-1", 1), ("-6", 1), ("-10", 2), ("-999999999999999999999999", 24), ]; for &(x, y) in vals.iter() { let a = BigInt::from_str(x).unwrap(); let b = count_decimal_digits(&a); assert_eq!(b, y); } } #[test] fn test_half() { let vals = vec![ ("100", "50."), ("2", "1"), (".2", ".1"), ("42", "21"), ("3", "1.5"), ("99", "49.5"), ("3.141592653", "1.5707963265"), ("3.1415926536", "1.5707963268"), ]; for &(x, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap().half(); let b = BigDecimal::from_str(y).unwrap(); assert_eq!(a, b); assert_eq!(a.scale, b.scale); } } #[test] fn test_round() { let test_cases = vec![ ("1.45", 1, "1.4"), ("1.444445", 1, "1.4"), ("1.44", 1, "1.4"), ("0.444", 2, "0.44"), ("4.5", 0, "4"), ("4.05", 1, "4.0"), ("4.050", 1, "4.0"), ("4.15", 1, "4.2"), ("0.0045", 2, "0.00"), ("5.5", -1, "10"), ("-1.555", 2, "-1.56"), ("-1.555", 99, "-1.555"), ("5.5", 0, "6"), ("-1", -1, "0"), ("5", -1, "0"), ("44", -1, "40"), ("44", -99, "0"), ("44", 99, "44"), ("1.4499999999", -1, "0"), ("1.4499999999", 0, "1"), ("1.4499999999", 1, "1.4"), ("1.4499999999", 2, "1.45"), ("1.4499999999", 3, "1.450"), ("1.4499999999", 4, "1.4500"), ("1.4499999999", 10, "1.4499999999"), ("1.4499999999", 15, "1.449999999900000"), ("-1.4499999999", 1, "-1.4"), ("1.449999999", 1, "1.4"), ("-9999.444455556666", 10, "-9999.4444555567"), ("-12345678987654321.123456789", 8, "-12345678987654321.12345679"), ("0.33333333333333333333333333333333333333333333333333333333333333333333333333333333333333", 0, "0"), ("0.1165085714285714285714285714285714285714", 0, "0"), ("0.1165085714285714285714285714285714285714", 2, "0.12"), ("0.1165085714285714285714285714285714285714", 5, "0.11651"), ("0.1165085714285714285714285714285714285714", 8, "0.11650857"), ("-1.5", 0, "-2"), ("-1.2", 0, "-1"), ("-0.68", 0, "-1"), ("-0.5", 0, "0"), ("-0.49", 0, "0"), ]; for &(x, digits, y) in test_cases.iter() { let a = BigDecimal::from_str(x).unwrap(); let b = BigDecimal::from_str(y).unwrap(); let rounded = a.round(digits); assert_eq!(rounded, b); } } #[test] fn round_large_number() { use super::BigDecimal; let z = BigDecimal::from_str("3.4613133327063255443352353815722045816611958409944513040035462804475524").unwrap(); let expected = BigDecimal::from_str("11.9806899871705702711783103817684242408972124568942276285200973527647213").unwrap(); let zsq = &z*&z; let zsq = zsq.round(70); debug_assert_eq!(zsq, expected); } #[test] fn test_is_integer() { let true_vals = vec![ "100", "100.00", "1724e4", "31.47e8", "-31.47e8", "-0.0", ]; let false_vals = vec![ "100.1", "0.001", "3147e-3", "3147e-8", "-0.01", "-1e-3", ]; for s in true_vals { let d = BigDecimal::from_str(s).unwrap(); assert!(d.is_integer()); } for s in false_vals { let d = BigDecimal::from_str(s).unwrap(); assert!(!d.is_integer()); } } #[test] fn test_inverse() { let vals = vec![ ("100", "0.01"), ("2", "0.5"), (".2", "5"), ("3.141592653", "0.3183098862435492205742690218851870990799646487459493049686604293188738877535183744268834079171116523"), ]; for &(x, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap(); let i = a.inverse(); let b = BigDecimal::from_str(y).unwrap(); assert_eq!(i, b); assert_eq!(BigDecimal::from(1)/&a, b); assert_eq!(i.inverse(), a); // assert_eq!(a.scale, b.scale, "scale mismatch ({} != {}", a, b); } } mod double { use super::*; include!("lib.tests.double.rs"); } #[test] fn test_square() { let vals = vec![ ("1.00", "1.00"), ("1.5", "2.25"), ("1.50", "2.2500"), ("5", "25"), ("5.0", "25.00"), ("-5.0", "25.00"), ("5.5", "30.25"), ("0.80", "0.6400"), ("0.01234", "0.0001522756"), ("3.1415926", "9.86960406437476"), ]; for &(x, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap().square(); let b = BigDecimal::from_str(y).unwrap(); assert_eq!(a, b); assert_eq!(a.scale, b.scale); } } #[test] fn test_cube() { let vals = vec![ ("1.00", "1.00"), ("1.50", "3.375000"), ("5", "125"), ("5.0", "125.000"), ("5.00", "125.000000"), ("-5", "-125"), ("-5.0", "-125.000"), ("2.01", "8.120601"), ("5.5", "166.375"), ("0.01234", "0.000001879080904"), ("3.1415926", "31.006275093569669642776"), ]; for &(x, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap().cube(); let b = BigDecimal::from_str(y).unwrap(); assert_eq!(a, b); assert_eq!(a.scale, b.scale); } } #[test] fn test_exp() { let vals = vec![ ("0", "1"), ("1", "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427"), ("1.01", "2.745601015016916493989776316660387624073750819595962291667398087987297168243899027802501018008905180"), ("0.5", "1.648721270700128146848650787814163571653776100710148011575079311640661021194215608632776520056366643"), ("-1", "0.3678794411714423215955237701614608674458111310317678345078368016974614957448998033571472743459196437"), ("-0.01", "0.9900498337491680535739059771800365577720790812538374668838787452931477271687452950182155307793838110"), ("-10.04", "0.00004361977305405268676261569570537884674661515701779752139657120453194647205771372804663141467275928595"), //("-1000.04", "4.876927702336787390535723208392195312680380995235400234563172353460484039061383367037381490416091595E-435"), ("-20.07", "1.921806899438469499721914055500607234723811054459447828795824348465763824284589956630853464778332349E-9"), ("10", "22026.46579480671651695790064528424436635351261855678107423542635522520281857079257519912096816452590"), ("20", "485165195.4097902779691068305415405586846389889448472543536108003159779961427097401659798506527473494"), //("777.7", "5.634022488451236612534495413455282583175841288248965283178668787259870456538271615076138061788051442E+337"), ]; for &(x, y) in vals.iter() { let a = BigDecimal::from_str(x).unwrap().exp(); let b = BigDecimal::from_str(y).unwrap(); assert_eq!(a, b); } } mod to_plain_string { use super::*; macro_rules! impl_test { ($name:ident: $input:literal => $expected:literal) => { #[test] fn $name() { let n: BigDecimal = $input.parse().unwrap(); let s = n.to_plain_string(); assert_eq!(&s, $expected); } }; } impl_test!(case_zero: "0" => "0"); impl_test!(case_1en18: "1e-18" => "0.000000000000000001"); impl_test!(case_n72e4: "-72e4" => "-720000"); impl_test!(case_95517338e30: "95517338e30" => "95517338000000000000000000000000000000"); impl_test!(case_29478en30: "29478e-30" => "0.000000000000000000000000029478"); impl_test!(case_30740d4897: "30740.4897" => "30740.4897"); } #[test] fn test_signed() { assert!(!BigDecimal::zero().is_positive()); assert!(!BigDecimal::one().is_negative()); assert!(BigDecimal::one().is_positive()); assert!((-BigDecimal::one()).is_negative()); assert!((-BigDecimal::one()).abs().is_positive()); } mod normalize { use super::*; macro_rules! impl_case { ( $name:ident: ($i:literal, $s:literal) => ($e_int_val:literal, $e_scale:literal) ) => { #[test] fn $name() { let d = BigDecimal::new($i.into(), $s); let n = d.normalized(); assert_eq!(n.int_val, $e_int_val.into()); assert_eq!(n.scale, $e_scale); } } } impl_case!(case_0e3: (0, -3) => (0, 0)); impl_case!(case_0en50: (0, 50) => (0, 0)); impl_case!(case_10en2: (10, 2) => (1, 1)); impl_case!(case_11en2: (11, 2) => (11, 2)); impl_case!(case_132400en4: (132400, 4) => (1324, 2)); impl_case!(case_1_900_000en3: (1_900_000, 3) => (19, -2)); impl_case!(case_834700e4: (834700, -4) => (8347, -6)); impl_case!(case_n834700e4: (-9900, 2) => (-99, 0)); } #[test] fn test_from_i128() { let value = BigDecimal::from_i128(-368934881474191032320).unwrap(); let expected = BigDecimal::from_str("-368934881474191032320").unwrap(); assert_eq!(value, expected); } #[test] fn test_from_u128() { let value = BigDecimal::from_u128(668934881474191032320).unwrap(); let expected = BigDecimal::from_str("668934881474191032320").unwrap(); assert_eq!(value, expected); } #[test] fn test_parse_roundtrip() { let vals = vec![ "1.0", "0.5", "50", "50000", "0.001000000000000000020816681711721685132943093776702880859375", "0.25", "12.339999999999999857891452847979962825775146484375", "0.15625", "0.333333333333333314829616256247390992939472198486328125", "3.141592653589793115997963468544185161590576171875", "31415.926535897931898944079875946044921875", "94247.779607693795696832239627838134765625", "1331.107", "1.0", "2e1", "0.00123", "-123", "-1230", "12.3", "123e-1", "1.23e+1", "1.23E+3", "1.23E-8", "-1.23E-10", "123_", "31_862_140.830_686_979", "-1_1.2_2", "999.521_939", "679.35_84_03E-2", "271576662.__E4", // Large decimals with small text representations "1E10000", "1E-10000", "1.129387461293874682630000000487984723987459E10000", "11293874612938746826340000000087984723987459E10000", ]; for s in vals { let expected = BigDecimal::from_str(s).unwrap(); let display = format!("{}", expected); let parsed = BigDecimal::from_str(&display).unwrap(); assert_eq!(expected, parsed, "[{}] didn't round trip through [{}]", s, display); } } } #[cfg(test)] #[allow(non_snake_case)] mod test_with_scale_round { use super::*; use paste::paste; include!("lib.tests.with_scale_round.rs"); } #[cfg(all(test, property_tests))] extern crate proptest; #[cfg(all(test, property_tests))] mod proptests { use super::*; use paste::paste; use proptest::*; include!("lib.tests.property-tests.rs"); } bigdecimal-0.4.7/src/lib.tests.double.rs000064400000000000000000000016071046102023000162350ustar 00000000000000// Test BigDecimal::double macro_rules! impl_case { ($name:ident : $a:literal => $ex:literal ) => { paste! { #[test] fn $name() { let value = BigDecimal::from_str($a).unwrap(); let expected = BigDecimal::from_str($ex).unwrap(); let result = value.double(); assert_eq!(result, expected); assert_eq!(result.int_val, expected.int_val); assert_eq!(result.scale, expected.scale); } } }; } impl_case!(case_zero : "0" => "0"); impl_case!(case_1 : "1" => "2"); impl_case!(case_100Em2 : "1.00" => "2.00"); impl_case!(case_150Em2 : "1.50" => "3.00"); impl_case!(case_neg150Em2 : "-1.50" => "-3.00"); impl_case!(case_32909E4 : "32909E4" => "6.5818E+8"); impl_case!(case_1_1156024145937225657484 : "1.1156024145937225657484" => "2.2312048291874451314968"); bigdecimal-0.4.7/src/lib.tests.property-tests.rs000064400000000000000000000072471046102023000200150ustar 00000000000000// Property tests to be included by lib.rs (if enabled) mod arithmetic { use super::*; macro_rules! impl_test { ($t:ty) => { paste! { proptest! { #[test] fn [< add_ref $t >](n: $t, m: i128, e: i8) { let d = BigDecimal::new(m.into(), e as i64); let sum = n + &d; let s1 = &d + n; let s2 = d.clone() + n; prop_assert_eq!(&sum, &s1); prop_assert_eq!(&sum, &s2); let mut s = d; s += n; prop_assert_eq!(sum, s); } #[test] fn [< sub_ $t >](n: $t, m: i128, e: i8) { let d = BigDecimal::new(m.into(), e as i64); let diff_n_d = n - &d; let diff_d_n = d.clone() - n; prop_assert_eq!(&diff_n_d, &diff_d_n.neg()); let mut a = d.clone(); a -= n; prop_assert_eq!(&a, &diff_n_d.neg()); } #[test] fn [< mul_ $t >](n: $t, m: i128, e: i8) { let d = BigDecimal::new(m.into(), e as i64); let prod_n_d = n * &d; let prod_d_n = d.clone() * n; prop_assert_eq!(&prod_n_d, &prod_d_n); let mut r = d.clone(); r *= n; prop_assert_eq!(&prod_n_d, &r); let r = d.neg() * n; prop_assert_eq!(prod_n_d.neg(), r); } #[test] fn [< div_ $t >](n: $t, m: i128, e: i8) { prop_assume!(m != 0); let d = BigDecimal::new(m.into(), e as i64); let quotient_n_ref_d = n / &d; let quotient_n_d = n / d.clone(); prop_assert_eq!("ient_n_ref_d, "ient_n_d); let prod = quotient_n_d * &d; let diff = n - ∏ // prop_assert!(dbg!(diff.scale) > 99); prop_assert!(diff.abs() < BigDecimal::new(1.into(), 60)); } } } }; (float-div $t:ty) => { paste! { proptest! { #[test] fn [< div_ $t >](n: $t, m: i128, e: i8) { prop_assume!(m != 0); let d = BigDecimal::new(m.into(), e as i64); let quotient_n_ref_d = n / &d; let quotient_n_d = n / d.clone(); prop_assert_eq!("ient_n_ref_d, "ient_n_d); let d = BigDecimal::new(m.into(), e as i64); let quotient_ref_d_n = &d / n; let quotient_d_n = d.clone() / n; prop_assert_eq!("ient_ref_d_n, "ient_d_n); let mut q = d.clone(); q /= n; prop_assert_eq!(&q, "ient_d_n); } } } }; } impl_test!(u8); impl_test!(u16); impl_test!(u32); impl_test!(u64); impl_test!(u128); impl_test!(i8); impl_test!(i16); impl_test!(i32); impl_test!(i64); impl_test!(i128); impl_test!(float-div f32); impl_test!(float-div f64); proptest! { #[test] fn square(f: f32) { // ignore non-normal numbers prop_assume!(f.is_normal()); let n: BigDecimal = BigDecimal::from_f32(f).unwrap(); let n_times_n = &n * &n; prop_assert_eq!(n_times_n, n.square()) } } } bigdecimal-0.4.7/src/lib.tests.with_scale_round.rs000064400000000000000000000067261046102023000203230ustar 00000000000000// Test BigDecimal::with_scale_round macro_rules! impl_test { ( name=$($name:expr)*; $scale:literal : $mode:ident => $ex:literal ) => { paste! { #[test] fn [< $($name)* _rounding_ $mode >]() { let bigdecimal = test_input(); let result = bigdecimal.with_scale_round($scale as i64, RoundingMode::$mode); let expected = BigDecimal::from_str($ex).unwrap(); assert_eq!(result, expected); assert_eq!(result.int_val, expected.int_val); assert_eq!(result.scale, $scale); } } }; ( -$scale:literal $( : $($modes:ident),+ => $ex:literal )+ ) => { $( $( impl_test!(name=scale_neg_ $scale; -$scale : $modes => $ex); )* )* }; ( $scale:literal $( : $($modes:ident),+ => $ex:literal )+ ) => { $( $( impl_test!(name=scale_ $scale; $scale : $modes => $ex); )* )* }; } mod case_3009788271450eNeg9 { use super::*; fn test_input() -> BigDecimal { BigDecimal::from_str("3009.788271450").unwrap() } impl_test!(10 : Up, Down => "3009.7882714500"); impl_test!(9 : Up, Down => "3009.788271450"); impl_test!(8 : Up, Down, HalfEven => "3009.78827145"); impl_test!(7 : Up, Ceiling, HalfUp => "3009.7882715" : Down, Floor, HalfDown, HalfEven => "3009.7882714"); impl_test!(4 : Up, Ceiling, HalfUp, HalfDown, HalfEven => "3009.7883" : Down, Floor => "3009.7882"); impl_test!(2 : Up => "3009.79" : Down => "3009.78"); impl_test!(1 : Up => "3009.8" : Down => "3009.7"); impl_test!(0 : Up => "3010" : Down => "3009"); impl_test!( -1 : Up => "301e1"); impl_test!( -2 : Up => "31e2"); impl_test!( -3 : Up => "4e3"); impl_test!( -4 : Up => "1e4" ); impl_test!( -5 : Up => "1e5" : Down => "0"); impl_test!( -20 : Up => "1e20" : Down => "0"); } mod case_neg_636652287787259 { use super::*; fn test_input() -> BigDecimal { BigDecimal::from_str("-636652287787259").unwrap() } impl_test!(1 : Up, Down => "-636652287787259.0"); impl_test!(0 : Up, Down => "-636652287787259"); impl_test!(-1 : Up => "-63665228778726e1" : Down => "-63665228778725e1"); impl_test!(-12 : Up => "-637e12" : Down => "-636e12"); } mod case_99999999999999999999999eNeg4 { use super::*; fn test_input() -> BigDecimal { BigDecimal::from_str("99999999999999999999999e-4").unwrap() } impl_test!(4 : Up => "9999999999999999999.9999"); impl_test!(3 : Up => "10000000000000000000.000" : Down => "9999999999999999999.999"); impl_test!(-3 : Up => "10000000000000000e3" : Down => "9999999999999999e3"); } mod case_369708962060657eNeg30 { use super::*; fn test_input() -> BigDecimal { BigDecimal::from_str("369708962060657E-30").unwrap() } impl_test!(4 : Up => "1e-4"); impl_test!(20 : Up => "36971e-20" : Down => "36970e-20"); } mod case_682829560896740e30 { use super::*; fn test_input() -> BigDecimal { BigDecimal::from_str("682829560896740e30").unwrap() } impl_test!(4 : Up => "682829560896740000000000000000000000000000000.0000"); impl_test!(0 : Up => "682829560896740000000000000000000000000000000"); impl_test!(-35 : Up => "6828295609e35"); impl_test!(-36 : Up => "682829561e36"); impl_test!(-100 : Up => "1e100"); } bigdecimal-0.4.7/src/macros.rs000064400000000000000000000033751046102023000143450ustar 00000000000000// \file src/macros.rs //! macros for /* macro_rules! forward_val_val_binop { (impl $imp:ident for $res:ty, $method:ident) => { impl $imp<$res> for $res { type Output = $res; #[inline] fn $method(self, other: $res) -> $res { // forward to val-ref $imp::$method(self, &other) } } }; } */ macro_rules! forward_ref_val_binop { (impl $imp:ident for $res:ty, $method:ident) => { impl<'a> $imp<$res> for &'a $res { type Output = $res; #[inline] fn $method(self, other: $res) -> $res { // forward to ref-ref $imp::$method(self, &other) } } }; } /* macro_rules! forward_val_ref_binop { (impl $imp:ident for $res:ty, $method:ident) => { impl<'a> $imp<&'a $res> for $res { type Output = $res; #[inline] fn $method(self, other: &$res) -> $res { // forward to ref-ref $imp::$method(&self, other) } } }; } // Forward everything to ref-ref, when reusing storage is not helpful macro_rules! forward_all_binop_to_ref_ref { (impl $imp:ident for $res:ty, $method:ident) => { forward_val_val_binop!(impl $imp for $res, $method); forward_val_ref_binop!(impl $imp for $res, $method); forward_ref_val_binop!(impl $imp for $res, $method); }; } */ macro_rules! forward_val_assignop { (impl $imp:ident for $res:ty, $method:ident) => { impl $imp<$res> for $res { #[inline] fn $method(&mut self, other: $res) { // forward to mutref-ref $imp::$method(self, &other) } } }; } bigdecimal-0.4.7/src/parsing.rs000064400000000000000000000165421046102023000145240ustar 00000000000000//! Routines for parsing values into BigDecimals use super::{BigDecimal, ParseBigDecimalError}; use stdlib::num::FpCategory; use stdlib::cmp::{self, Ordering}; use num_bigint::{BigInt, BigUint, Sign}; use num_traits::Zero; /// Try creating bigdecimal from f32 /// /// Non "normal" values will return Error case /// pub(crate) fn try_parse_from_f32(n: f32) -> Result { use stdlib::num::FpCategory::*; match n.classify() { Nan => Err(ParseBigDecimalError::Other("NAN".into())), Infinite => Err(ParseBigDecimalError::Other("Infinite".into())), Subnormal => Ok(parse_from_f32_subnormal(n)), Normal | Zero => Ok(parse_from_f32(n)), } } /// Return mantissa, exponent, and sign of given floating point number /// /// ```math /// f = frac * 2^pow /// ``` /// fn split_f32_into_parts(f: f32) -> (u32, i64, Sign) { let bits = f.to_bits(); let frac = (bits & ((1 << 23) - 1)) + (1 << 23); let exp = (bits >> 23) & 0xFF; let pow = exp as i64 - 127 - 23; let sign_bit = bits & (1 << 31); let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus }; (frac, pow, sign) } /// Create bigdecimal from f32 /// pub(crate) fn parse_from_f32(n: f32) -> BigDecimal { if n.classify() == FpCategory::Subnormal { return parse_from_f32_subnormal(n); } let bits = n.to_bits(); if (bits << 1) == 0 { return Zero::zero(); } // n = frac * 2^pow let (frac, pow, sign) = split_f32_into_parts(n); let result; let scale; match pow.cmp(&0) { Ordering::Equal => { result = BigUint::from(frac); scale = 0; } Ordering::Less => { let trailing_zeros = cmp::min(frac.trailing_zeros(), -pow as u32); let reduced_frac = frac >> trailing_zeros; let reduced_pow = pow + trailing_zeros as i64; debug_assert!(reduced_pow <= 0); let shift = BigUint::from(5u8).pow(-reduced_pow as u32); result = reduced_frac * shift; scale = -reduced_pow; } Ordering::Greater => { let shift = BigUint::from(2u8).pow(pow.abs() as u32); result = frac * shift; scale = 0; } } BigDecimal { int_val: BigInt::from_biguint(sign, result), scale: scale, } } /// Create bigdecimal from subnormal f32 pub(crate) fn parse_from_f32_subnormal(n: f32) -> BigDecimal { debug_assert_eq!(n.classify(), FpCategory::Subnormal); let bits = n.to_bits(); let sign_bit = bits >> 31; debug_assert_eq!(bits >> 24, sign_bit << 7); let frac = bits - (sign_bit << 31); // 5^149 = 5^126 + 5^23 (f32-bit-bias=126, fraction-bits=23) let five_to_149 = BigUint::from_slice(&[ 1466336501, 2126633373, 2856417274, 1232167559, 2512314040, 1644054862, 3843013918, 3873995871, 858643596, 3706384338, 65604258 ]); let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus }; let magnitude = BigUint::from(frac) * five_to_149; let scale = 149; let result = BigDecimal::new(BigInt::from_biguint(sign, magnitude), scale); return result; } #[cfg(test)] #[allow(non_snake_case)] mod test_parse_from_f32 { use super::*; include!("parsing.tests.parse_from_f32.rs"); } /// Try creating bigdecimal from f64 /// /// Non "normal" values will return Error case /// pub(crate) fn try_parse_from_f64(n: f64) -> Result { use stdlib::num::FpCategory::*; match n.classify() { Nan => Err(ParseBigDecimalError::Other("NAN".into())), Infinite => Err(ParseBigDecimalError::Other("Infinite".into())), Subnormal => Ok(parse_from_f64_subnormal(n)), Normal | Zero => Ok(parse_from_f64(n)), } } /// Return mantissa, exponent, and sign of given floating point number /// /// ```math /// f = frac * 2^pow /// ``` /// fn split_f64_into_parts(f: f64) -> (u64, i64, Sign) { let bits = f.to_bits(); let frac = (bits & ((1 << 52) - 1)) + (1 << 52); let exp = (bits >> 52) & 0x7FF; let pow = exp as i64 - 1023 - 52; let sign_bit = bits & (1 << 63); let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus }; (frac, pow, sign) } /// Create bigdecimal from subnormal f64 pub(crate) fn parse_from_f64_subnormal(n: f64) -> BigDecimal { debug_assert_eq!(n.classify(), FpCategory::Subnormal); let bits = n.to_bits(); let sign_bit = bits >> 63; debug_assert_eq!(bits >> 52, sign_bit << 11); // 5^1074 = 5^1022 + 5^52 (f64-bit-bias=1022, fraction-bits=52) let five_to_1074 = BigUint::from_slice(&[ 2993937753, 2678407619, 3969251600, 2340035423, 635686544, 3544357150, 2618749834, 3195461310, 2593678749, 4014721034, 2512738537, 1379014958, 2606506302, 1209795638, 3422246832, 2235398534, 2765471138, 3453720203, 3699786234, 1752628667, 3832472493, 2479745915, 4210941784, 2088904316, 4137646701, 3840319652, 3815898978, 2202136831, 1022273801, 1470939580, 2032173740, 4063736241, 2069243191, 4077145663, 4033014231, 1920904652, 4195885152, 3551517817, 4246423481, 2447790869, 1797774111, 11284306, 195273359, 3811183395, 4065514955, 3382133286, 1078447835, 2100087074, 3915378083, 1127077286, 1409634978, 2331452623, 1301118814, 3692061923, 2506161869, 4270519152, 1066095370, 212429084, 3729063602, 3175008277, 2075072468, 2136773221, 4247151843, 2395660055, 449096848, 2439918400, 1564416362, 3638689409, 3054795416, 1803373736, 1506581328, 2791252870, 3391180271, 1768177410, 3891987426, 3655546435, 3881223940, 903390128 ]); let frac = bits - (sign_bit << 63); let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus }; let magnitude = BigUint::from(frac) * five_to_1074; let scale = 1074; return BigDecimal::new(BigInt::from_biguint(sign, magnitude), scale); } /// Create bigdecimal from f64 /// /// Non "normal" values is undefined behavior /// pub(crate) fn parse_from_f64(n: f64) -> BigDecimal { if n.classify() == FpCategory::Subnormal { return parse_from_f64_subnormal(n); } let bits = n.to_bits(); // shift right by 1 bit to handle -0.0 if (bits << 1) == 0 { return Zero::zero(); } // n = frac * 2^pow let (frac, pow, sign) = split_f64_into_parts(n); debug_assert!(frac > 0); let result; let scale; match pow.cmp(&0) { Ordering::Equal => { result = BigUint::from(frac); scale = 0; } Ordering::Less => { let trailing_zeros = cmp::min(frac.trailing_zeros(), -pow as u32); let reduced_frac = frac >> trailing_zeros; let reduced_pow = pow + trailing_zeros as i64; debug_assert!(reduced_pow <= 0); let shift = BigUint::from(5u8).pow(-reduced_pow as u32); result = reduced_frac * shift; scale = -reduced_pow; } Ordering::Greater => { let shift = BigUint::from(2u8).pow(pow as u32); result = frac * shift; scale = 0; } } BigDecimal { int_val: BigInt::from_biguint(sign, result), scale: scale, } } #[cfg(test)] #[allow(non_snake_case)] mod test_parse_from_f64 { use super::*; include!("parsing.tests.parse_from_f64.rs"); } bigdecimal-0.4.7/src/parsing.tests.parse_from_f32.rs000064400000000000000000000104051046102023000204630ustar 00000000000000// tests for function bigdecimal::parsing::parse_from_f32 use paste::paste; use stdlib::f32; macro_rules! impl_test { ($name:ident : $input:literal == $expected:literal) => { paste! { #[test] fn [< case $name >]() { let n = $input as f32; let d = parse_from_f32(n); assert_eq!(d, $expected.parse().unwrap()); } #[test] fn [< case_neg $name >]() { let n = -($input as f32); let d = parse_from_f32(n); assert_eq!(d, concat!("-", $expected).parse().unwrap()); } } }; } impl_test!(_0 : 0.0 == "0"); impl_test!(_1 : 1.0 == "1"); impl_test!(_5en1 : 0.5 == "0.5"); impl_test!(_25en2 : 0.25 == "0.25"); impl_test!(_50 : 50. == "50"); impl_test!(_1en3 : 0.001 == "0.001000000047497451305389404296875"); impl_test!(_033203125en8 : 0.033203125 == "0.033203125"); impl_test!(_45En1 : 4.5 == "4.5"); impl_test!(_15625En5 : 0.15625 == "0.15625"); impl_test!(_1192092896En7 : 1.192092896e-7 == "1.1920928955078125E-7"); impl_test!(_1401757440 : 1401757440. == "1401757440"); impl_test!(_215092En1 : 21509.2 == "21509.19921875"); impl_test!(_2289620000 : 2289620000.0 == "2289619968"); impl_test!(_10000000 : 10000000. == "10000000"); impl_test!(_1en05 : 1e-5 == "0.00000999999974737875163555145263671875"); impl_test!(_1en1 : 1e-1 == "0.100000001490116119384765625"); impl_test!(_2en1 : 2e-1 == "0.20000000298023223876953125"); impl_test!(_80000197 : 80000197e0 == "80000200"); impl_test!(_23283064En16 : 2.3283064e-10 == "0.00000000023283064365386962890625"); impl_test!(_14693861798803098En17 : 0.14693861798803098 == "0.146938621997833251953125"); impl_test!(_1e20 : 1e20 == "100000002004087734272"); impl_test!(_1e30 : 1e30 == "1000000015047466219876688855040"); impl_test!(_1e38 : 1e38 == "99999996802856924650656260769173209088"); impl_test!(_317e36 : 317e36 == "317000006395220278118691742155288870912"); impl_test!(_23509889819en48 : 2.3509889819e-38 == "2.35098898190426788090088725919040801362055736959656341832065776397049129686767088287524529732763767242431640625E-38"); impl_test!(_235098744048en49 : 2.35098744048e-38 == "2.350987440475957123602109243087866394712812961308427354153308831195379018097479928428583662025630474090576171875E-38"); impl_test!(_6_99999952316 : 6.99999952316 == "6.999999523162841796875"); impl_test!(_317en40 : 317e-40 == "3.1700000098946435501119816090716154772221806896649747100732700841687651538425285480116144753992557525634765625E-38"); impl_test!(_4294967295 : 4294967295. == "4294967296"); impl_test!(_158456325029e18 : 1.58456325029e+29 == "158456325028528675187087900672"); impl_test!(_1_40129846432e_45 : 1.40129846432e-45 == "1.40129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125E-45"); impl_test!(_1_e42 : 1e-42 == "1.0005271035279193886395429224690001177341070264998322610345467546973108330377044694614596664905548095703125e-42"); impl_test!(_3_92E_n39 : 3.92E-39 == "3.91999933059456489828739575494312783522406115751507460249208160269472102366083987590172910131514072418212890625E-39"); impl_test!(_2_81341650018752E_n308 : 2.81341650018752E-308 == "0"); #[test] fn case_f32_min() { let n = f32::MIN; let d = parse_from_f32(n); assert_eq!(d, "-340282346638528859811704183484516925440".parse().unwrap()); } #[test] fn case_f32_max() { let n = f32::MAX; let d = parse_from_f32(n); assert_eq!(d, "340282346638528859811704183484516925440".parse().unwrap()); } #[test] fn case_f32_epsilon() { let n = f32::EPSILON; let d = parse_from_f32(n); assert_eq!(d, "1.1920928955078125E-7".parse().unwrap()); } #[test] fn case_f32_pi() { let n = f32::consts::PI; let d = parse_from_f32(n); assert_eq!(d, "3.1415927410125732421875".parse().unwrap()); } #[test] fn case_nan() { let n = f32::from_bits(0b01111111110000000000000000000000); assert!(n.is_nan()); let d = parse_from_f32(n); assert_eq!(d, "510423550381407695195061911147652317184".parse().unwrap()); } #[test] fn case_try_from_nan() { let n = f32::NAN; let d = try_parse_from_f32(n); assert!(d.is_err()); } #[test] fn case_try_from_infinity() { let n = f32::INFINITY; let d = try_parse_from_f32(n); assert!(d.is_err()); } bigdecimal-0.4.7/src/parsing.tests.parse_from_f64.rs000064400000000000000000000144571046102023000205030ustar 00000000000000// tests for function bigdecimal::parsing::parse_from_f64 use paste::paste; use stdlib::f64; macro_rules! impl_test { ($input:literal == $expected:literal) => { paste! { impl_test!( [< "_" $input >] : $input == $expected); } }; ($name:ident : bits:$input:literal => $expected:literal) => { impl_test!($name : f64::from_bits($input) => $expected); }; ($name:ident : $input:literal == $expected:literal) => { impl_test!($name : ($input as f64) => $expected); }; ($name:ident : $input:expr => $expected:literal) => { paste! { #[test] fn [< case $name >]() { let n = $input; let d = parse_from_f64(n); assert_eq!(d, $expected.parse().unwrap()); } #[test] fn [< case_neg $name >]() { let n = f64::from_bits($input.to_bits() | (1<<63)); let d = parse_from_f64(n); assert_eq!(d, concat!("-", $expected).parse().unwrap()); } } }; } impl_test!(_0 : 0.0 == "0"); impl_test!(_1 : 1.0 == "1"); impl_test!(_2 : 2.0 == "2"); impl_test!(_3 : 3.0 == "3"); impl_test!(_5en1 : 0.5 == "0.5"); impl_test!(_25en2 : 0.25 == "0.25"); impl_test!(_1en1 : 0.1 == "0.1000000000000000055511151231257827021181583404541015625"); impl_test!(_1over3 : 0.333333333333333333333333333333 == "0.333333333333333314829616256247390992939472198486328125"); impl_test!(_pi : 3.141592653589793 == "3.141592653589793115997963468544185161590576171875"); impl_test!(_near_3 : 3.0000000000000004 == "3.000000000000000444089209850062616169452667236328125"); impl_test!(_8eneg306 : 8.544283616667655e-306 == "8.5442836166676545758745469881475846986178991076220674838778719735182619591847930738097459423424470941335996703553180065389909675214026779902482660710563190540056652827644969523715287333767167538014707594736533997824798692690142890189753467148541192574394234161821394612038920127719106177776787375705338074667624093006332620080979623387970617655687653904110103913103933178304212511707769987213793880764157458662751217010283883439888757033430556011326632895537144105152597427684695380215955244686097497705226475608085097617996058799189036784865947060736971859470127760066696392182317083388979882704968230500619384728741377732016919538675848783600526390429792978252568964346334556191024880163233082812954995600973750951114861484914086986464099027216434478759765625e-306"); impl_test!(_8e306 : 3e300 == "3000000000000000157514280765613260746113405743324477464747562346535407373966724587359114125241343592131113331498651634530827569706081291726934376554360120948545161602779727411213490701384364270178106859704912399835243357116902922640223958228340427483737776366460170528514347008416589160596378201620480"); impl_test!(_50 : 50. == "50"); impl_test!(_nanbits : bits:0b_0_11111111111_1000000000000000000000000000000000000000000000000001 => "269653970229347426076201969312749943170150807578117307259170330445749843759196293443300553362892619730839480672521111823337121537071529813188030913831084401350087805833926634314566788423582671529934053315387252306324360914392174188827078768228648633522131134987762597502339006422840407304422939101316534763520"); impl_test!(_3105036184601418e246 : bits:0b_0_11100000000_0000000000000000000000000000000000000000000000000000 => "3105036184601417870297958976925005110513772034233393222278104076052101905372753772661756817657292955900975461394262146412343160088229628782888574550082362278408909952041699811100530571263196889650525998387432937501785693707632115712"); impl_test!(_2_81341650018752E_308 : 2.81341650018752E-308 == "2.8134165001875198278759275525943498067505063001967969175506480744152639496835355462897889950138699429916690515722729976876607247658891051736045520063301219592298855232146428654590713004216312194773871772185068366206180596731958890086634117134422695105490626598276746331472433159429067991016548063113298957324839879447939977012897422163463450947345510093578791948321798481101505330952230105511530048812659083481787407026258844307461890753626327683153826358878159001221539330872743255707112001100520519610144879206546597846231715071742093092641158571855689231930930474890818690333095288369471228217443460522531282790309374378111440076317827545086535792316428407651758951233693496387904508572484340169054222573303301594335791590596740352481219815672375261783599853515625E-308"); #[test] fn case_f64_min() { let n = f64::MIN; let d = parse_from_f64(n); assert_eq!(d, "-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368".parse().unwrap()); } #[test] fn case_f64_max() { let n = f64::MAX; let d = parse_from_f64(n); assert_eq!(d, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368".parse().unwrap()); } #[test] fn case_f64_epsilon() { let n = f64::EPSILON; let d = parse_from_f64(n); assert_eq!(d, "2.220446049250313080847263336181640625e-16".parse().unwrap()); } #[test] fn case_f64_pi() { let n = f64::consts::PI; let d = parse_from_f64(n); assert_eq!(d, "3.141592653589793115997963468544185161590576171875".parse().unwrap()); } #[test] fn case_nan() { let n = f64::from_bits(0b0_11111111111_1000000000000000000000000000000000000000000000000000); assert!(n.is_nan()); let d = parse_from_f64(n); assert_eq!(d, "269653970229347386159395778618353710042696546841345985910145121736599013708251444699062715983611304031680170819807090036488184653221624933739271145959211186566651840137298227914453329401869141179179624428127508653257226023513694322210869665811240855745025766026879447359920868907719574457253034494436336205824".parse().unwrap()); } #[test] fn case_try_from_nan() { let n = f64::NAN; let d = try_parse_from_f64(n); assert!(d.is_err()); } #[test] fn case_try_from_infinity() { let n = f64::INFINITY; let d = try_parse_from_f64(n); assert!(d.is_err()); } bigdecimal-0.4.7/src/rounding.rs000064400000000000000000000312201046102023000146740ustar 00000000000000//! Rounding structures and subroutines #![allow(dead_code)] use crate::*; use crate::arithmetic::{add_carry, store_carry, extend_adding_with_carry}; use stdlib; // const DEFAULT_ROUNDING_MODE: RoundingMode = ${RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE} or HalfUp; include!(concat!(env!("OUT_DIR"), "/default_rounding_mode.rs")); /// Determines how to calculate the last digit of the number /// /// Default rounding mode is HalfUp /// #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum RoundingMode { /// Always round away from zero /// /// /// * 5.5 → 6.0 /// * 2.5 → 3.0 /// * 1.6 → 2.0 /// * 1.1 → 2.0 /// * -1.1 → -2.0 /// * -1.6 → -2.0 /// * -2.5 → -3.0 /// * -5.5 → -6.0 Up, /// Always round towards zero /// /// * 5.5 → 5.0 /// * 2.5 → 2.0 /// * 1.6 → 1.0 /// * 1.1 → 1.0 /// * -1.1 → -1.0 /// * -1.6 → -1.0 /// * -2.5 → -2.0 /// * -5.5 → -5.0 Down, /// Towards +∞ /// /// * 5.5 → 6.0 /// * 2.5 → 3.0 /// * 1.6 → 2.0 /// * 1.1 → 2.0 /// * -1.1 → -1.0 /// * -1.6 → -1.0 /// * -2.5 → -2.0 /// * -5.5 → -5.0 Ceiling, /// Towards -∞ /// /// * 5.5 → 5.0 /// * 2.5 → 2.0 /// * 1.6 → 1.0 /// * 1.1 → 1.0 /// * -1.1 → -2.0 /// * -1.6 → -2.0 /// * -2.5 → -3.0 /// * -5.5 → -6.0 Floor, /// Round to 'nearest neighbor', or up if ending decimal is 5 /// /// * 5.5 → 6.0 /// * 2.5 → 3.0 /// * 1.6 → 2.0 /// * 1.1 → 1.0 /// * -1.1 → -1.0 /// * -1.6 → -2.0 /// * -2.5 → -3.0 /// * -5.5 → -6.0 HalfUp, /// Round to 'nearest neighbor', or down if ending decimal is 5 /// /// * 5.5 → 5.0 /// * 2.5 → 2.0 /// * 1.6 → 2.0 /// * 1.1 → 1.0 /// * -1.1 → -1.0 /// * -1.6 → -2.0 /// * -2.5 → -2.0 /// * -5.5 → -5.0 HalfDown, /// Round to 'nearest neighbor', if equidistant, round towards /// nearest even digit /// /// * 5.5 → 6.0 /// * 2.5 → 2.0 /// * 1.6 → 2.0 /// * 1.1 → 1.0 /// * -1.1 → -1.0 /// * -1.6 → -2.0 /// * -2.5 → -2.0 /// * -5.5 → -6.0 /// HalfEven, } impl RoundingMode { /// Perform the rounding operation /// /// Parameters /// ---------- /// * sign (Sign) - Sign of the number to be rounded /// * pair (u8, u8) - The two digits in question to be rounded. /// i.e. to round 0.345 to two places, you would pass (4, 5). /// As decimal digits, they /// must be less than ten! /// * trailing_zeros (bool) - True if all digits after the pair are zero. /// This has an effect if the right hand digit is 0 or 5. /// /// Returns /// ------- /// Returns the first number of the pair, rounded. The sign is not preserved. /// /// Examples /// -------- /// - To round 2341, pass in `Plus, (4, 1), true` → get 4 or 5 depending on scheme /// - To round -0.1051, to two places: `Minus, (0, 5), false` → returns either 0 or 1 /// - To round -0.1, pass in `true, (0, 1)` → returns either 0 or 1 /// /// Calculation of pair of digits from full number, and the replacement of that number /// should be handled separately /// pub fn round_pair(&self, sign: Sign, pair: (u8, u8), trailing_zeros: bool) -> u8 { use self::RoundingMode::*; use stdlib::cmp::Ordering::*; let (lhs, rhs) = pair; // if all zero after digit, never round if rhs == 0 && trailing_zeros { return lhs; } let up = lhs + 1; let down = lhs; match (*self, rhs.cmp(&5)) { (Up, _) => up, (Down, _) => down, (Floor, _) => if sign == Sign::Minus { up } else { down }, (Ceiling, _) => if sign == Sign::Minus { down } else { up }, (_, Less) => down, (_, Greater) => up, (_, Equal) if !trailing_zeros => up, (HalfUp, Equal) => up, (HalfDown, Equal) => down, (HalfEven, Equal) => if lhs % 2 == 0 { down } else { up }, } } /// Round digits, and if rounded up to 10, store 1 in carry and return zero pub(crate) fn round_pair_with_carry( &self, sign: Sign, pair: (u8, u8), trailing_zeros: bool, carry: &mut u8, ) -> u8 { let r = self.round_pair(sign, pair, trailing_zeros); store_carry(r, carry) } /// Round value at particular digit, returning replacement digit /// /// Parameters /// ---------- /// * at_digit (NonZeroU8) - 0-based index of digit at which to round. /// 0 would be the first digit, and would /// /// * sign (Sign) - Sign of the number to be rounded /// * value (u32) - The number containing digits to be rounded. /// * trailing_zeros (bool) - True if all digits after the value are zero. /// /// Returns /// ------- /// Returns the first number of the pair, rounded. The sign is not preserved. /// /// Examples /// -------- /// - To round 823418, at digit-index 3: `3, Plus, 823418, true` → 823000 or 824000, depending on scheme /// - To round -100205, at digit-index 1: `1, Minus, 100205, true` → 100200 or 100210 /// /// Calculation of pair of digits from full number, and the replacement of that number /// should be handled separately /// pub fn round_u32( &self, at_digit: stdlib::num::NonZeroU8, sign: Sign, value: u32, trailing_zeros: bool, ) -> u32 { let shift = 10u32.pow(at_digit.get() as u32 - 1); let splitter = shift * 10; // split 'value' into high and low let (top, bottom) = num_integer::div_rem(value, splitter); let lhs = (top % 10) as u8; let (rhs, remainder) = num_integer::div_rem(bottom, shift); let pair = (lhs, rhs as u8); let rounded = self.round_pair(sign, pair, trailing_zeros && remainder == 0); // replace low digit with rounded value let full = top - lhs as u32 + rounded as u32; // shift rounded value back to position full * splitter } /// Hint used to skip calculating trailing_zeros if they don't matter fn needs_trailing_zeros(&self, insig_digit: u8) -> bool { use RoundingMode::*; // only need trailing zeros if the rounding digit is 0 or 5 if matches!(self, HalfUp | HalfDown | HalfEven) { insig_digit == 5 } else { insig_digit == 0 } } } /// Return compile-time constant default rounding mode /// /// Defined by RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE at compile time /// impl Default for RoundingMode { fn default() -> Self { DEFAULT_ROUNDING_MODE } } /// All non-digit information required to round digits /// /// Just the mode and the sign. /// #[derive(Debug,Clone,Copy)] pub(crate) struct NonDigitRoundingData { /// Rounding mode pub mode: RoundingMode, /// Sign of digits pub sign: Sign, } impl NonDigitRoundingData { /// Round pair of digits, storing overflow (10) in the carry pub fn round_pair(&self, pair: (u8, u8), trailing_zeros: bool) -> u8 { self.mode.round_pair(self.sign, pair, trailing_zeros) } /// round-pair with carry-digits pub fn round_pair_with_carry(&self, pair: (u8, u8), trailing_zeros: bool, carry: &mut u8) -> u8 { self.mode.round_pair_with_carry(self.sign, pair, trailing_zeros, carry) } /// Use sign and default rounding mode pub fn default_with_sign(sign: Sign) -> Self { NonDigitRoundingData { sign, mode: RoundingMode::default() } } } /// Relevant information about insignificant digits, used for rounding /// /// If rounding at indicated point: /// /// ```txt /// aaaaizzzzzzzz /// ^ /// ``` /// /// 'a' values are significant, 'i' is the insignificant digit, /// and trailing_zeros is true if all 'z' are 0. /// #[derive(Debug,Clone,Copy)] pub(crate) struct InsigData { /// highest insignificant digit pub digit: u8, /// true if all digits more insignificant than 'digit' is zero /// /// This is only useful if relevant for the rounding mode, it /// may be 'wrong' in these cases. pub trailing_zeros: bool, /// rounding-mode and sign pub rounding_data: NonDigitRoundingData } impl InsigData { /// Build from insig data and lazily calculated trailing-zeros callable pub fn from_digit_and_lazy_trailing_zeros( rounder: NonDigitRoundingData, insig_digit: u8, calc_trailing_zeros: impl FnOnce() -> bool ) -> Self { Self { digit: insig_digit, trailing_zeros: rounder.mode.needs_trailing_zeros(insig_digit) && calc_trailing_zeros(), rounding_data: rounder, } } /// Build from slice of insignificant little-endian digits pub fn from_digit_slice(rounder: NonDigitRoundingData, digits: &[u8]) -> Self { match digits.split_last() { Some((&d0, trailing)) => { Self::from_digit_and_lazy_trailing_zeros( rounder, d0, || trailing.iter().all(Zero::is_zero) ) } None => { Self { digit: 0, trailing_zeros: true, rounding_data: rounder, } } } } /// from sum of overlapping digits, (a is longer than b) pub fn from_overlapping_digits_backward_sum( rounder: NonDigitRoundingData, mut a_digits: stdlib::iter::Rev>, mut b_digits: stdlib::iter::Rev>, carry: &mut u8, ) -> Self { debug_assert!(a_digits.len() >= b_digits.len()); debug_assert_eq!(carry, &0); // most-significant insignificant digit let insig_digit; match (a_digits.next(), b_digits.next()) { (Some(a), Some(b)) => { // store 'full', initial sum, we will handle carry below insig_digit = a + b; } (Some(d), None) | (None, Some(d)) => { insig_digit = *d; } (None, None) => { // both digit slices were empty; all zeros return Self { digit: 0, trailing_zeros: true, rounding_data: rounder, }; } }; // find first non-nine value let mut sum = 9; while sum == 9 { let next_a = a_digits.next().unwrap_or(&0); let next_b = b_digits.next().unwrap_or(&0); sum = next_a + next_b; } // if previous sum was greater than ten, // the one would carry through all the 9s let sum = store_carry(sum, carry); // propagate carry to the highest insignificant digit let insig_digit = add_carry(insig_digit, carry); // if the last 'sum' value isn't zero, or if any remaining // digit is not zero, then it's not trailing zeros let trailing_zeros = sum == 0 && rounder.mode.needs_trailing_zeros(insig_digit) && a_digits.all(Zero::is_zero) && b_digits.all(Zero::is_zero); Self { digit: insig_digit, trailing_zeros: trailing_zeros, rounding_data: rounder, } } pub fn round_digit(&self, digit: u8) -> u8 { self.rounding_data.round_pair((digit, self.digit), self.trailing_zeros) } pub fn round_digit_with_carry(&self, digit: u8, carry: &mut u8) -> u8 { self.rounding_data.round_pair_with_carry((digit, self.digit), self.trailing_zeros, carry) } pub fn round_slice_into(&self, dest: &mut Vec, digits: &[u8]) { let (&d0, rest) = digits.split_first().unwrap_or((&0, &[])); let digits = rest.iter().copied(); let mut carry = 0; let r0 = self.round_digit_with_carry(d0, &mut carry); dest.push(r0); extend_adding_with_carry(dest, digits, &mut carry); if !carry.is_zero() { dest.push(carry); } } #[allow(dead_code)] pub fn round_slice_into_with_carry(&self, dest: &mut Vec, digits: &[u8], carry: &mut u8) { let (&d0, rest) = digits.split_first().unwrap_or((&0, &[])); let digits = rest.iter().copied(); let r0 = self.round_digit_with_carry(d0, carry); dest.push(r0); extend_adding_with_carry(dest, digits, carry); } } #[cfg(test)] include!("rounding.tests.rs"); bigdecimal-0.4.7/src/rounding.tests.rs000064400000000000000000000162201046102023000160400ustar 00000000000000 #[allow(non_snake_case)] mod test_round_pair { use paste::paste; use super::*; macro_rules! impl_test { ( $($mode:ident),+ => $expected:literal) => { $( paste! { #[test] fn [< mode_ $mode >]() { let (pair, sign, trailing_zeros) = test_input(); let mode = self::RoundingMode::$mode; let result = mode.round_pair(sign, pair, trailing_zeros); assert_eq!(result, $expected); } } )* } } macro_rules! define_test_input { ( - $lhs:literal . $rhs:literal $($t:tt)* ) => { define_test_input!(sign=Sign::Minus, pair=($lhs, $rhs), $($t)*); }; ( $lhs:literal . $rhs:literal $($t:tt)*) => { define_test_input!(sign=Sign::Plus, pair=($lhs, $rhs), $($t)*); }; ( sign=$sign:expr, pair=$pair:expr, ) => { define_test_input!(sign=$sign, pair=$pair, trailing_zeros=true); }; ( sign=$sign:expr, pair=$pair:expr, 000x ) => { define_test_input!(sign=$sign, pair=$pair, trailing_zeros=false); }; ( sign=$sign:expr, pair=$pair:expr, trailing_zeros=$trailing_zeros:literal ) => { fn test_input() -> ((u8, u8), Sign, bool) { ($pair, $sign, $trailing_zeros) } }; } mod case_0_1 { use super::*; define_test_input!(0 . 1); impl_test!(Up, Ceiling => 1); impl_test!(Down, Floor, HalfUp, HalfDown, HalfEven => 0); } mod case_neg_0_1 { use super::*; define_test_input!(-0 . 1); impl_test!(Up, Floor => 1); impl_test!(Down, Ceiling, HalfUp, HalfDown, HalfEven => 0); } mod case_0_5 { use super::*; define_test_input!( 0 . 5 ); impl_test!(Up, Ceiling, HalfUp => 1); impl_test!(Down, Floor, HalfDown, HalfEven => 0); } mod case_neg_0_5 { use super::*; define_test_input!(-0 . 5); impl_test!(Up, Floor, HalfUp => 1); impl_test!(Down, Ceiling, HalfDown, HalfEven => 0); } mod case_0_5_000x { use super::*; // ...000x indicates a non-zero trailing digit; affects behavior of rounding N.0 and N.5 define_test_input!(0 . 5 000x); impl_test!(Up, Ceiling, HalfUp, HalfDown, HalfEven => 1); impl_test!(Down, Floor => 0); } mod case_neg_0_5_000x { use super::*; define_test_input!(-0 . 5 000x); impl_test!(Up, Floor, HalfUp, HalfDown, HalfEven => 1); impl_test!(Down, Ceiling => 0); } mod case_0_7 { use super::*; define_test_input!(0 . 7); impl_test!(Up, Ceiling, HalfUp, HalfDown, HalfEven => 1); impl_test!(Down, Floor => 0); } mod case_neg_0_7 { use super::*; define_test_input!(-0 . 7); impl_test!(Up, Floor, HalfUp, HalfDown, HalfEven => 1); impl_test!(Down, Ceiling => 0); } mod case_neg_4_3_000x { use super::*; define_test_input!(-4 . 3 000x); impl_test!(Up, Floor => 5); impl_test!(Down, Ceiling, HalfUp, HalfDown, HalfEven => 4); } mod case_9_5_000x { use super::*; define_test_input!(9 . 5 000x); impl_test!(Up, Ceiling, HalfDown, HalfUp, HalfEven => 10); impl_test!(Down, Floor => 9); } mod case_9_5 { use super::*; define_test_input!(9 . 5); impl_test!(Up, Ceiling, HalfUp, HalfEven => 10); impl_test!(Down, Floor, HalfDown => 9); } mod case_8_5 { use super::*; define_test_input!(8 . 5); impl_test!(Up, Ceiling, HalfUp => 9); impl_test!(Down, Floor, HalfDown, HalfEven => 8); } mod case_neg_6_5 { use super::*; define_test_input!(-6 . 5); impl_test!(Up, Floor, HalfUp => 7); impl_test!(Down, Ceiling, HalfDown, HalfEven => 6); } mod case_neg_6_5_000x { use super::*; define_test_input!(-6 . 5 000x); impl_test!(Up, Floor, HalfUp, HalfDown, HalfEven => 7); impl_test!(Down, Ceiling => 6); } mod case_3_0 { use super::*; define_test_input!(3 . 0); impl_test!(Up, Down, Ceiling, Floor, HalfUp, HalfDown, HalfEven => 3); } mod case_3_0_000x { use super::*; define_test_input!(3 . 0 000x); impl_test!(Up, Ceiling => 4); impl_test!(Down, Floor, HalfUp, HalfDown, HalfEven => 3); } mod case_neg_2_0 { use super::*; define_test_input!(-2 . 0); impl_test!(Up, Down, Ceiling, Floor, HalfUp, HalfDown, HalfEven => 2); } mod case_neg_2_0_000x { use super::*; define_test_input!(-2 . 0 000x); impl_test!(Up, Floor => 3); impl_test!(Down, Ceiling, HalfUp, HalfDown, HalfEven => 2); } } #[cfg(test)] #[allow(non_snake_case)] mod test_round_u32 { use paste::paste; use super::*; macro_rules! impl_test { ( $pos:literal :: $($mode:ident),+ => $expected:literal) => { $( paste! { #[test] fn [< digit_ $pos _mode_ $mode >]() { let (value, sign, trailing_zeros) = test_input(); let mode = self::RoundingMode::$mode; let pos = stdlib::num::NonZeroU8::new($pos as u8).unwrap(); let result = mode.round_u32(pos, sign, value, trailing_zeros); assert_eq!(result, $expected); } } )* } } macro_rules! define_test_input { ( - $value:literal $($t:tt)* ) => { define_test_input!(sign=Sign::Minus, value=$value $($t)*); }; ( $value:literal $($t:tt)* ) => { define_test_input!(sign=Sign::Plus, value=$value $($t)*); }; ( sign=$sign:expr, value=$value:literal ...000x ) => { define_test_input!(sign=$sign, value=$value, trailing_zeros=false); }; ( sign=$sign:expr, value=$value:literal ) => { define_test_input!(sign=$sign, value=$value, trailing_zeros=true); }; ( sign=$sign:expr, value=$value:expr, trailing_zeros=$trailing_zeros:literal ) => { fn test_input() -> (u32, Sign, bool) { ($value, $sign, $trailing_zeros) } }; } mod case_13950000 { use super::*; define_test_input!(13950000); impl_test!(3 :: Up => 13950000); impl_test!(5 :: Up, Ceiling, HalfUp, HalfEven => 14000000); impl_test!(5 :: Down, HalfDown => 13900000); } mod case_neg_35488622_000x { use super::*; // ...000x indicates non-zero trailing digit define_test_input!(-35488622 ...000x); impl_test!(1 :: Up => 35488630); impl_test!(1 :: Down => 35488620); impl_test!(2 :: Up => 35488700); impl_test!(2 :: Down => 35488600); impl_test!(7 :: Up, Floor => 40000000); impl_test!(7 :: Down, Ceiling => 30000000); impl_test!(8 :: Up => 100000000); impl_test!(8 :: Down => 0); } } bigdecimal-0.4.7/src/with_std.rs000064400000000000000000000006531046102023000147020ustar 00000000000000 // Wrap std:: modules in namespace #[allow(unused_imports)] mod stdlib { pub use std::{ cmp, convert, default, fmt, hash, mem, num, ops, iter, slice, str, string, f32, f64, }; #[cfg(test)] pub use std::collections::hash_map::DefaultHasher; pub use std::vec::Vec; pub use std::borrow; } bigdecimal-0.4.7/src/without_std.rs000064400000000000000000000013521046102023000154270ustar 00000000000000#[allow(unused_imports)] #[macro_use] extern crate alloc; #[cfg(test)] extern crate siphasher; // Without this import we get the following error: // error[E0599]: no method naemed `powi` found for type `f64` in the current scope #[allow(unused_imports)] use num_traits::float::FloatCore; // Wrap core:: modules in namespace #[allow(unused_imports)] mod stdlib { pub use core::{ cmp, convert, default, fmt, hash, mem, num, ops, iter, slice, str, i8, f32, f64, }; #[cfg(test)] pub use siphasher::sip::SipHasher as DefaultHasher; pub use alloc::borrow; pub use alloc::string; pub use alloc::vec::Vec; }