rust_decimal-1.17.0/.cargo_vcs_info.json0000644000000001120000000000100135670ustar { "git": { "sha1": "f01dd0c7739ec8f5c53350bc899933f8b245d0bd" } } rust_decimal-1.17.0/.editorconfig000064400000000000000000000003300072674642500150660ustar 00000000000000# http://editorconfig.org root = true [*.rs] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = falserust_decimal-1.17.0/.github/workflows/main.yml000064400000000000000000000112510072674642500174610ustar 00000000000000on: pull_request: types: [opened, synchronize, reopened] push: branches: - master - "[1-9].x" name: Continuous integration jobs: ci: name: Build and test runs-on: ubuntu-latest strategy: matrix: rust: - stable - beta services: postgres: image: postgres:11.6 env: POSTGRES_PASSWORD: '' ports: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v2 - name: Cache cargo registry uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-${{ matrix.backend }}-cargo-${{ hashFiles('**/Cargo.toml') }} - name: Install dependencies run: | sudo apt-get update sudo apt-get -y install libpq-dev - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - uses: davidB/rust-cargo-make@v1 - name: Build rust-decimal uses: actions-rs/cargo@v1 with: command: build args: --workspace --all-features - name: Run no_std tests uses: actions-rs/cargo@v1 with: command: make args: test-no-std - name: Run default tests uses: actions-rs/cargo@v1 with: command: make args: test-default - name: Run legacy operation tests uses: actions-rs/cargo@v1 with: command: make args: test-legacy-ops - name: Run mathematical function tests uses: actions-rs/cargo@v1 with: command: make args: test-maths - name: Run miscellaneous tests uses: actions-rs/cargo@v1 with: command: make args: test-misc - name: Run database tests uses: actions-rs/cargo@v1 with: command: make args: test-db - name: Run serde tests uses: actions-rs/cargo@v1 with: command: make args: test-serde - name: Run macro tests uses: actions-rs/cargo@v1 with: command: make args: test-macros check_style: name: Check file formatting and style runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: clippy, rustfmt override: true - name: Cache cargo registry uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }} - name: Install dependencies run: | sudo apt-get update sudo apt-get -y install libpq-dev - name: Check file formatting uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - name: Run clippy uses: actions-rs/cargo@v1 with: command: clippy args: --workspace --all-features fuzz: name: Fuzz runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: override: true profile: minimal toolchain: nightly - uses: davidB/rust-cargo-make@v1 - uses: actions-rs/install@v0.1 with: crate: cargo-fuzz use-tool-cache: true - name: Run fuzz tests uses: actions-rs/cargo@v1 with: command: make args: fuzz minimum_rust_version: name: Check minimum rust version runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: 1.46.0 profile: minimal override: true - name: Cache cargo registry uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: minimal_rust_version-cargo-${{ hashFiles('**/Cargo.toml') }} - name: Install dependencies run: | sudo apt-get update sudo apt-get -y install libpq-dev - name: Use minimal dependencies run: | RUSTC_BOOTSTRAP=1 cargo update -Z minimal-versions - name: Check build uses: actions-rs/cargo@v1 with: command: check args: --workspace rust_decimal-1.17.0/.gitignore000064400000000000000000000006130072674642500144050ustar 00000000000000# Generated by Cargo # will have compiled files and executables target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock # Ignore benchmark results .criterion # Hidden macOS files .DS_Store # Fuzz output .fuzz/ # IDE support .idea/ # Fuzz artifacts corpus target rust_decimal-1.17.0/BUILD.md000064400000000000000000000110520072674642500135750ustar 00000000000000Developing Rust Decimal ======================= * [Setup](#setup) * [Building](#building) * [Library Versioning](#library-versioning) * [Formatting / Code Style](#formatting--code-style) * [Testing](#testing) * [Fuzzing](#fuzzing) * [Benchmarking](#benchmarking) * [Code Coverage](#code-coverage) ## Setup **Minimum Rust Version:** `1.46.0` Rust Decimal leverages [cargo make](https://github.com/sagiegurari/cargo-make) to ensure a consistent build/test/release approach. This also handles installing any additional dependencies in order to execute various commands, making getting set up and going relatively straight forward and easy. Installing this can be done using `cargo`: ```shell cargo install --force cargo-make ``` Once this has been installed, common tasks can be initiated with the `makers` command. ## Building **Useful Make Commands:** `makers build` Building this library doesn't require any special commands and can be performed using a standard `cargo build`. ### Building no-std Builds by default leverage `std`. To disable this functionality you can build Rust Decimal without any default features enabled: ```shell cargo build --no-default-features ``` ## Library Versioning **Useful Make Commands:** `makers outdated` The general rule of thumb with Rust Decimal is to ensure that downstream dependencies are kept up to date, so long as they align with the minimum requirements of the Rust Decimal build system. As a helpful measure, a `makers` command has been created to identify any outdated dependencies within the project. On a regular basis this command is run and results evaluated for potential updates to be applied. Please note: because this is a library, the `Cargo.lock` file is not committed into the repository. This means that `cargo update` should be run before running `makers outdated`. ## Formatting / Code Style **Useful Make Commands:** `makers format`, `makers clippy` To maintain consistent styling across the project, both `clippy` and `cargo fmt` are used. Github actions will check for consistent styling upon pull request however it is recommended that both of these commands are run before creating a PR. ## Testing **Useful Make Commands:** `makers test-all`, `makers test-no-std`, `makers test-maths`, `makers test-serde`, `makers test-db` Testing is a critical part of the Rust Decimal development lifecycle. It helps ensure that the library is behaving correctly under a mixture of different inputs and feature configurations. There are many different test configurations defined in `Makefile.toml` to help ensure that common test cases are easy to execute locally. For the full complete list, please review `Makefile.toml` directly for all `test-*` tasks. Some useful test tasks are listed below: * `test-all`: Tests all test configurations against all features * `test-default`: Test the default configuration * `test-no-std`: Test the library against a `no-std` build * `test-maths`: Test the maths feature of the library using both legacy and default ops. * `test-serde`: Test serialization and deserialization using a mixture of serde features * `test-db`: Test a variety of database logic changes, including `diesel` and `postgres` configurations. When adding new features, please make sure to generate new tasks within the `Makefile.toml` to ensure continued coverage of your feature going forward. ### Generated tests Rust Decimal includes a number of generated tests that have been generated outside the scope of this project. These are effectively CSV files that use randomized data for the `lo`, `mid` and `hi` portions of the Decimal number. Modification of these files is restricted and should not be committed. If you would like to add further generated tests then please create new files as you see fit. ## Fuzzing **Useful Make Commands:** `makers fuzz` Rust Decimal includes some fuzz testing support also to help capture a wider range of testing scenarios. These can be run by using `makers fuzz`. ## Benchmarking **Useful Make Commands:** `makers bench` In order to ensure that Rust Decimal runs quickly, a number of benchmarks have been created and can be executed using `makers bench`. When adding new benchmarking configurations please ensure that you add a corresponding `Makefile.toml` task to capture the updated configuration. ## Code Coverage **Useful Make Commands:** `makers coverage`, `makers codecov-open` Limited code coverage support has been added to Rust Decimal using `gcov`. A code coverage report can be generated by running `makers coverage`. Once generated, the report can be opened using `makers codecov-open`.rust_decimal-1.17.0/CODE_OF_CONDUCT.md000064400000000000000000000062150072674642500152200ustar 00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at paul@form1.co.nz. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ rust_decimal-1.17.0/CONTRIBUTING.md000064400000000000000000000030730072674642500146510ustar 00000000000000# Contributing to Rust Decimal Rust Decimal welcomes contributions from everyone. Here are the guidelines if you are thinking of helping us: ## Contributions Contributions to Rust Decimal or its dependencies should be made in the form of GitHub pull requests. Each pull request will be reviewed by a core contributor (someone with permission to land patches) and either landed in the main tree or given feedback for changes that would be required. All contributions should follow this format, even those from core contributors. Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on it. This is to prevent duplicated efforts from contributors on the same issue. ## Pull Request Checklist - Branch from the master branch and, if needed, rebase to the current master branch before submitting your pull request. If it doesn't merge cleanly with master you may be asked to rebase your changes. - If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or inside a comment. - Add tests relevant to the fixed bug or new feature. ## Conduct In all Rust Decimal related forums, we follow the [Rust Code of Conduct](https://www.rust-lang.org/conduct.html). For escalation or moderation of issues, please contact Paul (paul@form1.co.nz) instead of the Rust moderation team. ## Communication Opening tickets on the [paupino/rust-decimal](https://github.com/paupino/rust-decimal) project is the preferred method of communication. rust_decimal-1.17.0/Cargo.toml0000644000000054300000000000100115750ustar # 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 = "2018" name = "rust_decimal" version = "1.17.0" authors = ["Paul Mason "] exclude = ["tests/generated/*"] description = "A Decimal Implementation written in pure Rust suitable for financial calculations." documentation = "https://docs.rs/rust_decimal/" readme = "./README.md" keywords = ["decimal", "financial", "fixed", "precision"] categories = ["science", "data-structures"] license = "MIT" repository = "https://github.com/paupino/rust-decimal" [package.metadata.docs.rs] all-features = true [[bench]] name = "comparison" path = "benches/comparison.rs" harness = false [dependencies.arbitrary] version = "1.0" optional = true default-features = false [dependencies.arrayvec] version = "0.5" default-features = false [dependencies.byteorder] version = "1.3" optional = true default-features = false [dependencies.bytes] version = "1.0" optional = true default-features = false [dependencies.diesel] version = "1.4" features = ["postgres"] optional = true default-features = false [dependencies.num-traits] version = "0.2" features = ["i128"] default-features = false [dependencies.postgres] version = "0.19" optional = true default-features = false [dependencies.serde] version = "1.0" optional = true default-features = false [dependencies.serde_json] version = "1.0" optional = true default-features = false [dependencies.tokio-postgres] version = "0.7" optional = true default-features = false [dev-dependencies.bincode] version = "1.3" [dev-dependencies.bytes] version = "1.0" [dev-dependencies.criterion] version = "0.3" default-features = false [dev-dependencies.csv] version = "1" [dev-dependencies.futures] version = "0.3" [dev-dependencies.serde_derive] version = "1.0" [dev-dependencies.serde_json] version = "1.0" [dev-dependencies.tokio] version = "1.0" features = ["rt-multi-thread", "test-util", "macros"] [features] c-repr = [] db-diesel-postgres = ["diesel", "std"] db-postgres = ["byteorder", "bytes", "postgres", "std"] db-tokio-postgres = ["byteorder", "bytes", "postgres", "std", "tokio-postgres"] default = ["serde", "std"] legacy-ops = [] maths = [] maths-nopanic = ["maths"] rust-fuzz = ["arbitrary"] serde-arbitrary-precision = ["serde", "serde_json/arbitrary_precision"] serde-bincode = ["serde-str"] serde-float = ["serde"] serde-str = ["serde"] std = [] tokio-pg = ["db-tokio-postgres"] rust_decimal-1.17.0/Cargo.toml.orig000064400000000000000000000043230072674642500153060ustar 00000000000000[[bench]] harness = false name = "comparison" path = "benches/comparison.rs" [package] authors = ["Paul Mason "] categories = ["science","data-structures"] description = "A Decimal Implementation written in pure Rust suitable for financial calculations." documentation = "https://docs.rs/rust_decimal/" edition = "2018" exclude = [ "tests/generated/*" ] keywords = ["decimal","financial","fixed","precision"] license = "MIT" name = "rust_decimal" readme = "./README.md" repository = "https://github.com/paupino/rust-decimal" version = "1.17.0" [package.metadata.docs.rs] all-features = true [dependencies] arbitrary = { default-features = false, optional = true, version = "1.0" } arrayvec = { default-features = false, version = "0.5" } byteorder = { default-features = false, optional = true, version = "1.3" } bytes = { default-features = false, optional = true, version = "1.0" } diesel = { default-features = false, features = ["postgres"], optional = true, version = "1.4" } num-traits = { default-features = false, features = ["i128"], version = "0.2" } postgres = { default-features = false, optional = true, version = "0.19" } serde = { default-features = false, optional = true, version = "1.0" } serde_json = { default-features = false, optional = true, version = "1.0" } tokio-postgres = { default-features = false, optional = true, version = "0.7" } [dev-dependencies] bincode = "1.3" bytes = "1.0" criterion = { default-features = false, version = "0.3" } csv = "1" futures = "0.3" serde_derive = "1.0" serde_json = "1.0" tokio = { features = ["rt-multi-thread", "test-util", "macros"], version = "1.0" } [features] c-repr = [] # Force Decimal to be repr(C) db-diesel-postgres = ["diesel", "std"] db-postgres = ["byteorder", "bytes", "postgres", "std"] db-tokio-postgres = ["byteorder", "bytes", "postgres", "std", "tokio-postgres"] default = ["serde", "std"] legacy-ops = [] maths = [] maths-nopanic = ["maths"] rust-fuzz = ["arbitrary"] serde-arbitrary-precision = ["serde", "serde_json/arbitrary_precision"] serde-bincode = ["serde-str"] # Backwards compatability serde-float = ["serde"] serde-str = ["serde"] std = [] tokio-pg = ["db-tokio-postgres"] # Backwards compatability [workspace] members = [".", "./macros"] rust_decimal-1.17.0/LICENSE000064400000000000000000000020530072674642500134220ustar 00000000000000MIT License Copyright (c) 2016 Paul Mason 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. rust_decimal-1.17.0/Makefile.toml000064400000000000000000000125730072674642500150370ustar 00000000000000[config] default_to_workspace = false [env] FUZZ_RUNS = 1000 [tasks.build] command = "cargo" args = ["build"] [tasks.format] workspace = true install_crate = "rustfmt" command = "cargo" args = ["fmt", "--", "--emit=files"] [tasks.clippy] workspace = true install_crate = "clippy" command = "cargo" args = ["clippy"] [tasks.outdated] install_crate = "cargo-outdated" command = "cargo" args = ["outdated", "-R"] [tasks.bench] toolchain = "nightly" command = "cargo" args = ["bench", "${@}"] [tasks.bench-maths] toolchain = "nightly" command = "cargo" args = ["bench", "--features", "maths", "${@}"] [tasks.bench-legacy] toolchain = "nightly" command = "cargo" args = ["bench", "--no-default-features", "--features", "serde,std,legacy-ops", "${@}"] [tasks.benchcmp] dependencies = [ "benchcmp-legacy", "benchcmp-default" ] install_crate = "benchcmp" command = "cargo" args = ["benchcmp", "target/legacy.bench", "target/default.bench"] [tasks.benchcmp-default] script = "cargo +nightly bench > target/default.bench" [tasks.benchcmp-legacy] script = "cargo +nightly bench --no-default-features --features serde,std,legacy-ops > target/legacy.bench" [tasks.coverage] dependencies = ["codecov-clean"] env = { "CARGO_INCREMENTAL" = "0", "RUSTFLAGS" = "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort", "RUSTDOCFLAGS" = "-Cpanic=abort" } run_task = "codecov-grcov" [tasks.codecov-grcov] dependencies = ["codecov-build", "codecov-test"] command = "grcov" args = [".", "-s", ".", "--binary-path", "./target/debug/", "-t", "html", "--branch", "--ignore-not-existing", "-o", "./target/debug/coverage/"] [tasks.codecov-open] command = "open" args = [ "./target/debug/coverage/index.html" ] [tasks.codecov-clean] toolchain = "nightly" command = "cargo" args = [ "clean" ] [tasks.codecov-build] toolchain = "nightly" command = "cargo" args = [ "build", "-p", "rust_decimal", "--features=default" ] [tasks.codecov-test] toolchain = "nightly" command = "cargo" args = [ "test", "-p", "rust_decimal", "--features=default" ] # Always test no-std with std tests [tasks.test] dependencies = ["test-no-std"] command = "cargo" args = ["test"] [tasks.fuzz] dependencies = [ "fuzz-arithmetic", "fuzz-constructors" ] [tasks.fuzz-arithmetic] toolchain = "nightly" install_crate = "cargo-fuzz" command = "cargo" args = ["fuzz", "run", "arithmetic", "--", "-runs=${FUZZ_RUNS}"] [tasks.fuzz-constructors] toolchain = "nightly" install_crate = "cargo-fuzz" command = "cargo" args = ["fuzz", "run", "constructors", "--", "-runs=${FUZZ_RUNS}"] [tasks.test-all] dependencies = [ "test-no-std", "test-default", "test-legacy-ops", "test-maths", "test-misc", "test-db", "test-serde", "test-macros" ] [tasks.test-db] dependencies = [ "test-db-postgres", "test-db-tokio-postgres", "test-db-diesel-postgres" ] [tasks.test-serde] dependencies = [ "test-serde-float", "test-serde-str", "test-serde-str-float", "test-serde-arbitrary-precision", "test-serde-arbitrary-precision-float" ] [tasks.test-macros] workspace = true [tasks.test-no-std] command = "cargo" args = ["test", "--no-default-features"] [tasks.test-default] command = "cargo" args = ["test", "--workspace", "--features=default"] [tasks.test-legacy-ops] command = "cargo" args = ["test", "--workspace", "--features=legacy-ops"] [tasks.test-maths] dependencies = [ "test-maths-default", "test-maths-legacy", "test-maths-nopanic", ] [tasks.test-maths-default] command = "cargo" args = ["test", "--workspace", "--no-default-features", "--features=maths", "maths", "--", "--skip", "generated"] [tasks.test-maths-legacy] command = "cargo" args = ["test", "--workspace", "--no-default-features", "--features=maths,legacy-ops", "maths", "--", "--skip", "generated"] [tasks.test-maths-nopanic] command = "cargo" args = ["test", "--workspace", "--no-default-features", "--features=maths-nopanic", "maths", "--", "--skip", "generated"] [tasks.test-misc] command = "cargo" args = ["test", "--workspace", "--no-default-features", "--features=rust-fuzz", "rust_fuzz", "--", "--skip", "generated"] [tasks.test-db-postgres] command = "cargo" args = ["test", "--workspace", "--tests", "--features=db-postgres", "db", "--", "--skip", "generated"] [tasks.test-db-tokio-postgres] command = "cargo" args = ["test", "--workspace", "--tests", "--features=db-tokio-postgres", "db", "--", "--skip", "generated"] [tasks.test-db-diesel-postgres] command = "cargo" args = ["test", "--workspace", "--tests", "--features=db-diesel-postgres", "db", "--", "--skip", "generated"] [tasks.test-serde-float] command = "cargo" args = ["test", "--workspace", "--tests", "--features=serde-float", "serde", "--", "--skip", "generated"] [tasks.test-serde-str] command = "cargo" args = ["test", "--workspace", "--tests", "--features=serde-str", "serde", "--", "--skip", "generated"] [tasks.test-serde-str-float] command = "cargo" args = ["test", "--workspace", "--tests", "--features=serde-str,serde-float", "serde", "--", "--skip", "generated"] [tasks.test-serde-arbitrary-precision] command = "cargo" args = ["test", "--workspace", "--tests", "--features=serde-arbitrary-precision", "serde", "--", "--skip", "generated"] [tasks.test-serde-arbitrary-precision-float] command = "cargo" args = ["test", "--workspace", "--tests", "--features=serde-arbitrary-precision,serde-float", "serde", "--", "--skip", "generated"] rust_decimal-1.17.0/README.md000064400000000000000000000145550072674642500137060ustar 00000000000000# Decimal   [![Build Status]][actions] [![Latest Version]][crates.io] [![Docs Badge]][docs] [Build Status]: https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fpaupino%2Frust-decimal%2Fbadge&label=build&logo=none [actions]: https://actions-badge.atrox.dev/paupino/rust-decimal/goto [Latest Version]: https://img.shields.io/crates/v/rust-decimal.svg [crates.io]: https://crates.io/crates/rust-decimal [Docs Badge]: https://docs.rs/rust_decimal/badge.svg [docs]: https://docs.rs/rust_decimal A Decimal implementation written in pure Rust suitable for financial calculations that require significant integral and fractional digits with no round-off errors. The binary representation consists of a 96 bit integer number, a scaling factor used to specify the decimal fraction and a 1 bit sign. Because of this representation, trailing zeros are preserved and may be exposed when in string form. These can be truncated using the `normalize` or `round_dp` functions. ## Getting started To get started, add `rust_decimal` and optionally `rust_decimal_macros` to your `Cargo.toml`: ```toml [dependencies] rust_decimal = "1.17" rust_decimal_macros = "1.17" ``` ## Usage Decimal numbers can be created in a few distinct ways. The easiest and most optimal method of creating a Decimal is to use the procedural macro within the `rust_decimal_macros` crate: ```rust // Procedural macros need importing directly use rust_decimal_macros::dec; let number = dec!(-1.23); assert_eq!("-1.23", number.to_string()); ``` Alternatively you can also use one of the Decimal number convenience functions: ```rust // Using the prelude can help importing trait based functions (e.g. core::str::FromStr). use rust_decimal::prelude::*; // Using an integer followed by the decimal points let scaled = Decimal::new(202, 2); assert_eq!("2.02", scaled.to_string()); // From a string representation let from_string = Decimal::from_str("2.02").unwrap(); assert_eq!("2.02", from_string.to_string()); // From a string representation in a different base let from_string_base16 = Decimal::from_str_radix("ffff", 16).unwrap(); assert_eq!("65535", from_string_base16.to_string()); // Using the `Into` trait let my_int: Decimal = 3i32.into(); assert_eq!("3", my_int.to_string()); // Using the raw decimal representation let pi = Decimal::from_parts(1102470952, 185874565, 1703060790, false, 28); assert_eq!("3.1415926535897932384626433832", pi.to_string()); ``` Once you have instantiated your `Decimal` number you can perform calculations with it just like any other number: ```rust use rust_decimal::prelude::*; let amount = Decimal::from_str("25.12").unwrap(); let tax = Decimal::from_str("0.085").unwrap(); let total = amount + (amount * tax).round_dp(2); assert_eq!(total.to_string(), "27.26"); ``` ## Features * [c-repr](#c-repr) * [db-postgres](#db-postgres) * [db-tokio-postgres](#db-tokio-postgres) * [db-diesel-postgres](#db-diesel-postgres) * [legacy-ops](#legacy-ops) * [maths](#maths) * [rust-fuzz](#rust-fuzz) * [serde-float](#serde-float) * [serde-str](#serde-str) * [serde-arbitrary-precision](#serde-arbitrary-precision) * [std](#std) ### `c-repr` Forces `Decimal` to use `[repr(C)]`. The corresponding target layout is 128 bit aligned. ### `db-postgres` This feature enables a PostgreSQL communication module. It allows for reading and writing the `Decimal` type by transparently serializing/deserializing into the `NUMERIC` data type within PostgreSQL. ### `db-tokio-postgres` Enables the tokio postgres module allowing for async communication with PostgreSQL. ### `db-diesel-postgres` Enable `diesel` PostgreSQL support. ### `legacy-ops` As of `1.10` the algorithms used to perform basic operations have changed which has benefits of significant speed improvements. To maintain backwards compatibility this can be opted out of by enabling the `legacy-ops` feature. ### `maths` The `maths` feature enables additional complex mathematical functions such as `pow`, `ln`, `enf`, `exp` etc. Documentation detailing the additional functions can be found on the [`MathematicalOps`](https://docs.rs/rust_decimal/latest/rust_decimal/trait.MathematicalOps.html) trait. Please note that `ln` and `log10` will panic on invalid input with `checked_ln` and `checked_log10` the preferred functions to curb against this. When the `maths` feature was first developed the library would return `0` on invalid input. To re-enable this non-panicking behavior, please use the feature: `maths-nopanic`. ### `rust-fuzz` Enable `rust-fuzz` support by implementing the `Arbitrary` trait. ### `serde-float` Enable this so that JSON serialization of `Decimal` types are sent as a float instead of a string (default). e.g. with this turned on, JSON serialization would output: ```json { "value": 1.234 } ``` ### `serde-str` This is typically useful for `bincode` or `csv` like implementations. Since `bincode` does not specify type information, we need to ensure that a type hint is provided in order to correctly be able to deserialize. Enabling this feature on its own will force deserialization to use `deserialize_str` instead of `deserialize_any`. If, for some reason, you also have `serde-float` enabled then this will use `deserialize_f64` as a type hint. Because converting to `f64` _loses_ precision, it's highly recommended that you do NOT enable this feature when working with `bincode`. That being said, this will only use 8 bytes so is slightly more efficient in terms of storage size. ### `serde-arbitrary-precision` This is used primarily with `serde_json` and consequently adds it as a "weak dependency". This supports the `arbitrary_precision` feature inside `serde_json` when parsing decimals. This is recommended when parsing "float" looking data as it will prevent data loss. ### `std` Enable `std` library support. This is enabled by default, however in the future will be opt in. For now, to support `no_std` libraries, this crate can be compiled with `--no-default-features`. ## Building Please refer to the [Build document](BUILD.md) for more information on building and testing Rust Decimal. ## Minimum Rust Compiler Version This library maintains support for rust compiler versions at least one year old and only updates the minimum version if and when required. The current minimum compiler version is [`1.46.0`](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1460-2020-08-27) which was released on `2020-08-27`.rust_decimal-1.17.0/VERSION.md000064400000000000000000000654750072674642500141050ustar 00000000000000# Version History ## 1.17.0 * Fixes panic when display precision was specified greater than 30. [#428](https://github.com/paupino/rust-decimal/issues/428). * Binds `deserialize` so that invalid scale values cannot be set. This may be a breaking change for some users leveraging this in a `const` function. [#428](https://github.com/paupino/rust-decimal/issues/428). * Fixes an issue in `round_sf` whereby integral values that started with a 0 or 1 would output incorrect rounded numbers. [#430](https://github.com/paupino/rust-decimal/issues/430), [#437](https://github.com/paupino/rust-decimal/issues/437). * Adds `from_f32_retain` and `from_f64_retain` functions which allow parsing a floating point number and retaining any excess precision. [#438](https://github.com/paupino/rust-decimal/issues/438). ## 1.16.0 * Implement `sin`/`cos`/`tan` functions which are enabled within the `maths` feature. [#413](https://github.com/paupino/rust-decimal/pull/413). * Implement `round_sf` and `round_sf_with_strategy` to enable significant figure rounding. [#421](https://github.com/paupino/rust-decimal/pull/421) * Remove unnecessary `std` feature from `arrayvec` usage [#417](https://github.com/paupino/rust-decimal/pull/417). * Adhoc benchmarking and fuzz tooling improvements [#412](https://github.com/paupino/rust-decimal/pull/412), [#415](https://github.com/paupino/rust-decimal/pull/415), [#416](https://github.com/paupino/rust-decimal/pull/416), [#418](https://github.com/paupino/rust-decimal/pull/418). Thank you to [@c410-f3r](https://github.com/c410-f3r) for all your help with fuzz and benchmarking improvements! ## 1.15.0 A minor bug and feature release which adds a couple of new functions as well as cleans up some documentation: * Support for serializing to float without converting to float via the `serde-arbitrary-precision` feature. [#402](https://github.com/paupino/rust-decimal/issues/402). Thanks [@JamesHinshelwood](https://github.com/JamesHinshelwood)! for finding and fixing this! * Add `log10` support to the `maths` feature. Please note that `ln` and `log10` will now panic on invalid input since both functions have a `checked_*` equivalent. This is the preferred approach going forward, however if you would like to re-enable the previous behavior please use the `maths-nopanic` feature. [#397](https://github.com/paupino/rust-decimal/issues/397). * Added further constants to the `Decimal` library including `TWO`, `TEN`, `ONE_HUNDRED`, `ONE_THOUSAND`, and `NEGATIVE_ONE`. [#400](https://github.com/paupino/rust-decimal/issues/400). * Fixes serialization issue for `-0` whereby `-` would be output [#406](https://github.com/paupino/rust-decimal/pull/406). Thanks [@edwardycl](https://github.com/edwardycl)! * Fixes float rounding before return in `to_f64`. [#401](https://github.com/paupino/rust-decimal/issues/401). * Added `BUILD.md` file to help people get set up with Rust Decimal locally and cleaned up some documentation examples. ## 1.14.3 Fixes an issue [#398](https://github.com/paupino/rust-decimal/issues/398) where `Decimal::ZERO.ln()` would panic rather than returning `Decimal::ZERO`. This aligns the behavior with calling `ln` on negative decimals. Thank you to [@SebRollen](https://github.com/SebRollen) for finding and fixing this. ## 1.14.2 Fixes an overflow issue during division under some specific circumstances. ([#392](https://github.com/paupino/rust-decimal/issues/392)) ## 1.14.1 A bug fix release following on from `1.14.0`: * Fixes an issue whereby in some cases when subtracting a 64 bit `Decimal` a negating overflow would occur during underflow. [#384](https://github.com/paupino/rust-decimal/issues/384). Thank you to [@c410-f3r](https://github.com/c410-f3r) for finding this as part of fuzz testing. * Fixes an issue with `exp` whereby negative values lost accuracy due to inability to converge. [#378](https://github.com/paupino/rust-decimal/issues/378). Thank you to [@schungx](https://github.com/schungx) for finding this and proposing a fix. * Fixes some documentation issues. ## 1.14.0 * Added `checked_exp` and `checked_norm_pdf` functions [#375](https://github.com/paupino/rust-decimal/pull/375). * Fixes bug in division under certain circumstances whereby overflow would occur during rounding. [#377](https://github.com/paupino/rust-decimal/pull/377) * Documentation improvements Thank you to [@falsetru](https://github.com/falsetru), [@schungx](https://github.com/schungx) and [@blasrodri](https://github.com/blasrodri) for your help with this release! ## 1.13.0 This is a minor update to the library providing a few new features and one breaking change (I'm not using semver properly here sorry). * `#[must_use]` added to functions to provide additional compiler hints. * `try_from_i128_with_scale` function added to safely handle `i128` overflow errors. * New `c-repr` feature added which will ensure that `#[repr(C)]` is used on the `Decimal` type. Thanks [@jean-airoldie](https://github.com/jean-airoldie). * Small improvements to `from_scientific`. It now supports a wider range of values as well has slightly faster performance. * Support for negative and decimal `pow` functions. This is *breaking* since `powi(u64)` has been renamed to `powi(i64)`. If you want to continue using `u64` arguments then please use `powu(u64)`. The fractional functions should be considered experimental for the time being and may have subtle issues that still need ironing out. Functions are now: * `powi`, `checked_powi` - When the exponent is a signed integer. * `powu`, `checked_powu` - When the exponent is an unsigned integer. * `powf`, `checked_powf` - When the exponent is a floating point number. Please note, numbers with a fractional component will use an approximation function. * `powd`, `checked_powd` - When the exponent is a `Decimal`. Please note, numbers with a fractional component will use an approximation function. ## 1.12.4 Adds `num_traits::One` back to `rust_decimal::prelude` to prevent unnecessary downstream dependency breakages. Thanks [@spearman](https://github.com/spearman). ## 1.12.3 Fixes an issue [#361](https://github.com/paupino/rust-decimal/issues/361) when rounding a small number towards zero. ## 1.12.2 Fixes small regression whereby `0 - 0` was producing `-0`. Thank you [@KonishchevDmitry](https://github.com/KonishchevDmitry) for providing a swift fix ([#356](https://github.com/paupino/rust-decimal/pull/356)). ## 1.12.1 Added `num_traits::Zero` back to `rust_decimal::prelude` to prevent unnecessary downstream dependency breakages. ## 1.12.0 This version releases faster operation support for `add`, `sub`, `cmp`, `rem` and `mul` to match the renewed `div` strategy. It does this by leveraging 64 bit support when it makes sense, while attempting to still keep 32 bit optimizations in place. To ensure correct functionality, thousands more tests were included to cover a wide variety of different scenarios and bit combinations. Compared to previous operations, we get the following speed improvements: * `add` - up to 2.2x faster * `div` - up to 428x faster * `mul` - up to 1.8x faster * `rem` - up to 1.08x faster * `sub` - up to 2.5x faster Of course, if old functionality is desired, it can be re-enabled by using the `legacy-ops` feature. Other improvements include: * Remove unnecessary `String` allocation when parsing a scientific number format. Thanks [@thomcc](https://github.com/thomcc) for the fix [#350](https://github.com/paupino/rust-decimal/pull/350). * Fixes overflow bug with `sqrt` when using the smallest possible representable number. [#349](https://github.com/paupino/rust-decimal/pull/349). * Some minor optimizations in the `maths` feature. Future work will involve speeding up this feature by keeping operations in an internal format until required. * Added associated constants for `MIN`, `MAX` and `ZERO`. Deprecated `min_value()` and `max_value()` in favor of these new constants. * `-0` now gets corrected to `0`. During operation rewrite I needed to consider operations such as `-0 * 2` - in cases like this I opted towards `0` always being the right number and `-0` being superfluous (since `+0 == -0`). Consequently, parsing `-0` etc _in general_ will automatically be parsed as `0`. Of course, this _may_ be a breaking change so if this functionality is required then please create an issue with the use case described. * Small breaking change by renaming `is_negative` to `negative` in `UnpackedDecimal`. * Some internal housekeeping was made to help make way for version 2.0 improvements. ## 1.11.1 This is a documentation only release and has no new functionality included. Thank you [@c410-f3r](https://github.com/c410-f3r) for the documentation fix. ## 1.11.0 This release includes a number of bug fixes and ergonomic improvements. * Mathematical functionality is now behind a feature flag. This should help optimize library size when functions such as `log` and `pow` are not required (e.g. simple financial applications). Mathematical functionality is now behind the `maths` feature flag. [#321](https://github.com/paupino/rust-decimal/pull/321). * Numerous test coverage improvements to ensure broader coverage. [#322](https://github.com/paupino/rust-decimal/pull/322), [#323](https://github.com/paupino/rust-decimal/pull/323) * Various documentation improvements. [#324](https://github.com/paupino/rust-decimal/pull/324), [#342](https://github.com/paupino/rust-decimal/pull/342) * Fixes `u128` and `i128` parsing. [#332](https://github.com/paupino/rust-decimal/pull/332) * Implemented `Checked*` traits from `num_traits`. [#333](https://github.com/paupino/rust-decimal/pull/333). Thank you [@teoxoy](https://github.com/teoxoy) * Added `checked_powi` function to `maths` feature. [#336](https://github.com/paupino/rust-decimal/pull/336) * Updated `from_parts` to avoid accidental scale clobbering. [#337](https://github.com/paupino/rust-decimal/pull/337) * Added support for the `Arbitrary` trait for `rust-fuzz` support. This is behind the feature flag `rust-fuzz`. [#338](https://github.com/paupino/rust-decimal/pull/338) * Fixes `e^-1` returning an incorrect approximation. [#339](https://github.com/paupino/rust-decimal/pull/339) * Revamp of `RoundingStrategy` naming and documentation ([#340](https://github.com/paupino/rust-decimal/pull/340)). The old naming was ambiguous in interpretation - the new naming convention follows guidance from other libraries to ensure an easy to follow scheme. The `RoundingStrategy` enum now includes: * `MidpointNearestEven` (previously `BankersRounding`) * `MidpointAwayFromZero` (previously `RoundHalfUp`) * `MidpointTowardZero` (previously `RoundHalfDown`) * `ToZero` (previously `RoundDown`) * `AwayFromZero` (previously `RoundUp`) * `ToNegativeInfinity` - new rounding strategy * `ToPositiveInfinity` - new rounding strategy * Added function to access `mantissa` directly. [#341](https://github.com/paupino/rust-decimal/pull/341) * Added a feature to `rust_decimal_macros` to make re-exporting the macro from a downstream crate more approachable. Enabling the `reexportable` feature will ensure that the generated code doesn't require `rust_decimal` to be exposed at the root level. [#343](https://github.com/paupino/rust-decimal/pull/343) ## 1.10.3 * Fixes bug in bincode serialization where a negative symbol causes a buffer overflow (#317). ## 1.10.2 * Fixes a bug introduced in division whereby certain values when using a large remainder cause an incorrect results (#314). ## 1.10.1 * Fixes bug introduced in `neg` whereby sign would always be turned negative as opposed to being correctly negated. Thank you [KonishchevDmitry](https://github.com/KonishchevDmitry) for finding and fixing this. ## 1.10.0 * Upgrade `postgres` to `0.19` and `tokio-postgres` to `0.7`. * Faster `serde` serialization by preventing heap allocation. * Alternative division algorithm which provides significant speed improvements. The new algorithms are enabled by default, but can be disabled with the feature: `legacy-ops`. Further work to improve other operations will be made available in future versions. * Add `TryFrom` for `f32`/`f64` to/from Decimal Thank you for the the community help and support for making this release happen, in particular: [jean-airoldie](https://github.com/jean-airoldie), [gakonst](https://github.com/gakonst), [okaneco](https://github.com/okaneco) and [c410-f3r](https://github.com/c410-f3r). ## 1.9.0 * Added arbitrary precision support for `serde_json` deserialization (#283) * Add `u128` and `i128` `FromPrimitive` overrides to prevent default implementation kicking in. Also adds default `From` interceptors to avoid having to use trait directly. (#282) * Alias `serde-bincode` as `serde-str` to make usage clearer (#279) * Adds scientific notation to format strings via `UpperExp` and `LowerExp` traits. (#271) * Upgrade `tokio-postgres` and `postgres` libraries. * Add statistical function support for `powi`, `sqrt`, `exp`, `norm_cdf`, `norm_pdf`, `ln` & `erf` (#281, #287) * Allow `sum` across immutable references (#280) Thank you for all the community help and support with this release, in particular [xilec](https://github.com/xilec), [remkade](https://github.com/remkade) and [Anders429](https://github.com/Anders429). ## 1.8.1 Make `std` support the default to prevent breaking downstream library dependencies. To enable `no_std` support please set default features to false and opt-in to any required components. e.g. ``` rust_decimal = { default-features = false, version = "1.8.0" } ``` ## 1.8.0 * `no_std` support added to Rust Decimal by default. `std` isn't required to use Rust Decimal, however can be enabled by using the `std` feature. [#190](https://github.com/paupino/rust-decimal/issues/190) * Fixes issue with Decimal sometimes losing precision through `to_f64`. [#267](https://github.com/paupino/rust-decimal/issues/267). * Add `Clone`, `Copy`, `PartialEq` and `Eq` derives to `RoundingStrategy`. * Remove Proc Macro hack due to procedural macros as expressions being stabilized. * Minor optimizations Thank you to [@c410-f3r](https://github.com/c410-f3r), [@smessmer](https://github.com/smessmer) and [@KiChjang](https://github.com/KiChjang). ## 1.7.0 * Enables `bincode` support via the feature `serde-bincode`. This provides a long term fix for a regression that was introduced in version `0.6.5` (tests now cover this case!). [Issue 43](https://github.com/paupino/rust-decimal/issues/43). * Fixes issue where `rescale` on zero would not have an affect. This was due to an early exit condition which failed to set the new scale. [Issue 253](https://github.com/paupino/rust-decimal/issues/253). * Add `min` and `max` functions, similar to what `f32` and `f64` provide. Thank you [@michalsieron](https://github.com/michalsieron). * Updates documentation for `is_sign_positive` and `is_sign_negative` to specify that the sign bit is being checked. Please note: feature naming conventions have been modified, however backwards compatible aliases have been created where necessary. It's highly recommended that you move over to the new naming conventions as these aliases may be removed at a later date. ## 1.6.0 * Fixes issue with PostgreSQL conversions whereby certain inputs would cause unexpected outputs. [Issue 241](https://github.com/paupino/rust-decimal/issues/241). * Fixes issue with `from_str_radix` whereby rounding logic would kick in too early, especially with radix less than 10. [Issue 242](https://github.com/paupino/rust-decimal/issues/242). * Fixes issue whereby `from_str` (implicity `from_str_radix`) would panic when there was overflow and overflow significant digit was < 5. [Issue 246](https://github.com/paupino/rust-decimal/issues/246). * Make `bytes` and `byteorder` optional since they're only used in the `postgres` feature and tests. * Fix edge case in `from_i128_with_scale` when `i128::MIN` was provided. Thank you to [@serejkaaa512](https://github.com/serejkaaa512), [@AbsurdlySuspicious](https://github.com/AbsurdlySuspicious) and [@0e4ef622]((https://github.com/0e4ef622)) for your contributions! ## 1.5.0 * Added additional `RoundStrategy` abilities: `RoundUp` to always round up and `RoundDown` to always round down. * Updated prelude to include expected structs and traits by default. Special thank you to [@jean-airoldie](https://github.com/jean-airoldie) for adding the additional rounding strategies and to [@pfrenssen](https://github.com/pfrenssen) for fixing an issue in the README. ## 1.4.1 * Performance improvements for `to_f64` when using a scale > 0. Special thank you to [@hengchu](https://github.com/hengchu) who discovered and resolved the issue! ## 1.4.0 * Allow uppercase "E" in scientific notation. * Allow scientific notation in `dec!` macro. * Deprecate `set_sign` and replace with `set_sign_positive` and `set_sign_negative`. This is intended to improve the readability of the API. * Fixes precision issue when parsing `f64` values. The base 2 mantissa of the float was assuming guaranteed accuracy of 53 bit precision, however 52 bit precision is more accurate (`f64` only). * Removes deprecated usage of `Error::description`. ## 1.3.0 * Replace `num` dependency with `num_trait` - implemented `Signed` and `Num` traits. ## 1.2.1 * Fixes issue whereby overflow would occur reading from PostgreSQL with high precision. The library now handles this by rounding high precision numbers as they're read as opposed to crashing (similar to other underflow situations e.g. 1/3). ## 1.2.0 * Retain trailing zeros from PostgreSQL. This ensures that the scale is maintained when serialized into the Decimal type. * Fixes issue where -0 != 0 (these are now equivalent - thank you @hengchu for discovering). * Improve hashing function so that the following property is true: `k1 == k2 -> hash(k1) == hash(k2)` * Update normalize function so that -0 normalizes to 0. Special thanks to @hathawsh for their help in this release! ## 1.1.0 * Update to Postgres 0.17 and add postgres async/await support via `tokio-pg` * Added option for serializing decimals as float via `serde-float` Special thanks to @pimeys and @kaibyao! ## 1.0.3 Updates dependencies to prevent build issues. ## 1.0.2 Bug fix release: * Fixes issue where scaling logic produced incorrect results when one arm was a high precision zero. Thank you @KonishchevDmitry! ## 1.0.1 Bug fix release: * Fixes issue where `ToSql` was incorrectly calculating weight when whole portion = numeric portion. * Fixes issue where `Decimal::new` incorrectly handled `i64::max_value()` and `i64::min_value()`. * Fixes issue where `rem` operation incorrectly returned results when `scale` was required. ## 1.0.0 This release represents the start of semantic versioning and allows the library to start making fundamental improvements under the guise of V2.0. Leading up to that I expect to release 1.x versions which will include adding various mathematical functions such as `pow`, `ln`, `log10` etc. Version `1.0.0` does come with some new features: * Checked Operations! This implements `checked_add`, `checked_sub`, `checked_mul`, `checked_div` and `checked_rem`. * Fixes overflow from `max_value()` and `min_value()` for `i32` and `i64`. * Minor documentation improvements and test coverage. Special thanks to @0e4ef622 for their help with this release! ## 0.11.3 * Add prelude to help num trait inclusion (`use rust_decimal::prelude::*`) * Add `Default` trait to the library. This is equivalent to using `Decimal::zero()` * Added assignment operators for references. Special thanks to @jean-airoldie for his help with this release! ## 0.11.2 * Fall back to `from_scientific` when `from_str` fails during deserialization. Thanks @mattjbray! * Added basic `Sum` trait implementation ## 0.11.1 * Fixes a bug in `floor` and `ceil` where negative numbers were incorrectly handled. ## 0.11.0 * Macros are now supported on stable. This does use a [hack](https://github.com/dtolnay/proc-macro-hack) for the meantime so due diligence is required before usage. * Fixes issue when parsing strings where an underscore preceded a decimal point. * `const_fn` support via a feature flag. In the future this will be the default option however in order to support older compiler versions is behind a feature flag. ## 0.10.2 * Macros (nightly) now output structural data as opposed to serialized data. This is fully backwards compatible and results in some minor performance improvements. Also, removed feature gate so that it can be compiled in stable. * Fixes a string parsing bug when given highly significant numbers that require rounding. ## 0.10.1 * Bumped dependencies to remove some legacy serialization requirements. ## 0.10.0 Special thanks to @xilec, @snd and @AndrewSpeed for their help with this release. * New rounding strategies introduced via `round_dp_with_strategy`. Previously default rounding support used bankers rounding by default whereas now you can choose to round the half way point either up or down. * PostgreSQL write performance improved so that it is at least 3 times faster than the previous implementation. * `Debug` trait now outputs the actual decimal number by default to make it more useful within consuming libraries (e.g. `criterion.rs`). To get something similar to the previous functionality you can use the `unpack` argument - this is likely for core `rust-decimal` library maintainers. * Various other performance improvements for common operations such as `rescale`, `sub` and `div`. ## 0.9.1 * Performance optimization for `add`. ## 0.9.0 * Introduces the `Neg` trait to support the ability to use `-decimal_variable`. * Fixes bug with underflow on addition. ## 0.8.1 This release updates the published documentation only and is a no-op for functionality. ## 0.8.0 * Introduces `from_scientific` allowing parsing of scientific notation into the Decimal type. * Fixes a bug when formatting a number with a leading zero's. ## 0.7.2 * Fixes bug in `rescale` whereby scaling which invoked rounding incorrectly set the new scale for the left/right sides. ## 0.7.1 * Fixes bug in `cmp` whereby two negatives would return an incorrect result. * Further documentation examples * Small improvements in division logic * New `abs`, `floor` and `ceil` functions. ## 0.7.0 This is a minor version bump as we slowly build our way towards 1.0. Thank you for everyone's support and help as we get there! This has a few notable changes - also introducing a few new interfaces which is the reason for the version bump: * `from_parts` function to allow effective creation of `Decimal`'s without requiring binary serialization. An example of this benefit is with the lazy static group initializers for Postgres. * `normalize` function to allow stripping trailing zero's easily. * `trunc` function allows truncation of a number without any rounding. This effectively "truncates" the fractional part of the number. * `fract` function returns the fractional part of the number without the integral. * Minor improvements in some iterator logic, utilizing the compiler for further optimizations. * Fixes issue in string parsing logic whereby `_` would cause numbers to be incorrectly identified. * Many improvements to `mul`. Numbers utilizing the `lo` portion of the decimal only will now be shortcut and bigger numbers will now correctly overflow. True overflows will still panic, however large underflows will now be rounded as necessary as opposed to panicing. * `Hash` was implemented by convention in `0.6.5` however is reimplemented explicitly in `0.7.0` for effectiveness. * PostgreSQL read performance improved by pre-caching groups and leveraging `normalize` (i.e. avoiding strings). Further optimizations can be made in write however require some `div` optimizations first. * Added short circuit write improvement for zero in PostgreSQL writes. * Benchmarks are now recorded per build so we can start tracking where slow downs have occurred. This does mean there is a performance hit on Travis builds however hopefully the pay off will make it worthwhile. ## 0.6.5 Fixes issue with rescale sometimes causing a silent overflow which led to incorrect results during addition, subtraction and compare. Consequently Decimal now rounds the most significant number so that these operations work successfully. In addition, Decimal now derive's the `Hash` trait so that it can be used for indexing. ## 0.6.4 Fixes silent overflow errors when parsing highly significant strings. `from_str` will now round in these scenario's, similar to oleaut32 behavior. ## 0.6.3 Fixes a regression in ordering where by different scales would be rescaled towards losing precision instead of increasing precision. Have added numerous test suites to help cover more issues like this in the future. Also fixes an issue in parsing invalid strings whereby the precision exceeded our maximum precision. Previously, this would work with unintended results however this now returns an Error returned from `FromStr`. ## 0.6.2 Fixes an issue with division of rational numbers allowing results greater than `MAX_PRECISION`. This would ultimately cause issues for future operations on this number. In addition, in some cases transitive operations would not be equal due to overflow being lost. ## 0.6.1 This minor release is purely to expose `rust_decimal_macros` for use on the nightly channel. Documentation has been updated accordingly. ## 0.6.0 This release has a few major changes to the internal workings of the `Decimal` implementation and consequently comes with a number of performance improvements. * Floats can now be parsed into a `Decimal` type using `from_f32` and `from_f64`. * `add`, `sub`, `mul` run roughly 1500% faster than before. * `div` run's roughly 1000% faster than before with room for future improvement. * Also get significant speed improvements with `cmp`, `rescale`, `round_dp` and some string manipulations. * Implemented `*Assign` traits for simpler usage. * Removed `BigInt` and `BigUint` as being intermediary data types. ## 0.5.2 Minor bug fix to prevent a `panic` from overflow during comparison of high significant digit decimals. ## 0.5.1 Minor bux fix to prevent `panic` upon parsing an empty string. ## 0.5.0 * Removes postgres from default feature set. * `bincode` support for serde * Better support for format strings * Benchmarks added to tests ## 0.4.2 Fixes bug in `cmp` whereby negative's were not being compared correctly. ## 0.4.1 Minor bug fix to support creating negative numbers using the default constructor. ## 0.4.0 This release is a stylistic cleanup however does include some minor changes that may break existing builds. ### Changed * Serde is now optional. You can enable Serde support within `features` using the keyword `serde`. * Serde now returns errors on invalid input as opposed to `0`. * `f64` conversion support has been added. * Update Postgres dependency to use v0.15. ## 0.3.1 This is a documentation release that should help with discoverability and usage. ## 0.3.0 ### Changed * Removed trait `ToDecimal` and replaced with builtin [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) trait ([`#12`](https://github.com/paupino/rust-decimal/pull/12)) rust_decimal-1.17.0/benches/comparison.rs000064400000000000000000000033550072674642500165520ustar 00000000000000//! See how `rust-decimal` performs compared to native floating numbers. use criterion::{ black_box, criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, }; use rust_decimal::Decimal; const DECIMAL_2_01: Decimal = Decimal::from_parts(201, 0, 0, false, 2); const DECIMAL_13_7: Decimal = Decimal::from_parts(137, 0, 0, false, 1); const F64_2_01: f64 = 2.01; const F64_13_7: f64 = 13.7; macro_rules! add_benchmark_group { ($criterion:expr, $f:ident, $op:tt) => { fn $f(group: &mut BenchmarkGroup<'_, M>) where M: Measurement, { group.bench_with_input(BenchmarkId::new("f64 (diff)", N), &N, |ben, _| { ben.iter(|| black_box(F64_2_01 $op F64_13_7)) }); group.bench_with_input(BenchmarkId::new("f64 (equal)", N), &N, |ben, _| { ben.iter(|| black_box(F64_2_01 $op F64_2_01)) }); group.bench_with_input(BenchmarkId::new("rust-decimal (diff)", N), &N, |ben, _| { ben.iter(|| black_box(DECIMAL_2_01 $op DECIMAL_13_7)) }); group.bench_with_input(BenchmarkId::new("rust-decimal (equal)", N), &N, |ben, _| { ben.iter(|| black_box(DECIMAL_2_01 $op DECIMAL_2_01)) }); } let mut group = $criterion.benchmark_group(stringify!($f)); $f::<_, 100>(&mut group); group.finish(); }; } fn criterion_benchmark(c: &mut Criterion) { add_benchmark_group!(c, addition, +); add_benchmark_group!(c, division, /); add_benchmark_group!(c, multiplication, *); add_benchmark_group!(c, subtraction, -); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); rust_decimal-1.17.0/benches/lib_benches.rs000064400000000000000000000240420072674642500166310ustar 00000000000000#![feature(test)] extern crate test; use bincode::Options as _; use core::str::FromStr; use rust_decimal::Decimal; macro_rules! bench_decimal_op { ($name:ident, $op:tt, $x:expr, $y:expr) => { #[bench] fn $name(b: &mut ::test::Bencher) { let x = Decimal::from_str($x).unwrap(); let y = Decimal::from_str($y).unwrap(); b.iter(|| { let result = x $op y; ::test::black_box(result); }); } } } macro_rules! bench_fold_op { ($name:ident, $op:tt, $init:expr, $count:expr) => { #[bench] fn $name(b: &mut ::test::Bencher) { fn fold(values: &[Decimal]) -> Decimal { let mut acc: Decimal = $init.into(); for value in values { acc = acc $op value; } acc } let values: Vec = test::black_box((1..$count).map(|i| i.into()).collect()); b.iter(|| { let result = fold(&values); ::test::black_box(result); }); } } } /* Add */ bench_decimal_op!(add_self, +, "2.01", "2.01"); bench_decimal_op!(add_simple, +, "2", "1"); bench_decimal_op!(add_one, +, "2.01", "1"); bench_decimal_op!(add_two, +, "2.01", "2"); bench_decimal_op!(add_one_hundred, +, "2.01", "100"); bench_decimal_op!(add_point_zero_one, +, "2.01", "0.01"); bench_decimal_op!(add_negative_point_five, +, "2.01", "-0.5"); bench_decimal_op!(add_pi, +, "2.01", "3.1415926535897932384626433832"); bench_decimal_op!(add_negative_pi, +, "2.01", "-3.1415926535897932384626433832"); bench_fold_op!(add_10k, +, 0, 10_000); /* Sub */ bench_decimal_op!(sub_self, -, "2.01", "2.01"); bench_decimal_op!(sub_simple, -, "2", "1"); bench_decimal_op!(sub_one, -, "2.01", "1"); bench_decimal_op!(sub_two, -, "2.01", "2"); bench_decimal_op!(sub_one_hundred, -, "2.01", "100"); bench_decimal_op!(sub_point_zero_one, -, "2.01", "0.01"); bench_decimal_op!(sub_negative_point_five, -, "2.01", "-0.5"); bench_decimal_op!(sub_pi, -, "2.01", "3.1415926535897932384626433832"); bench_decimal_op!(sub_negative_pi, -, "2.01", "-3.1415926535897932384626433832"); bench_fold_op!(sub_10k, -, 5_000_000, 10_000); /* Mul */ bench_decimal_op!(mul_one, *, "2.01", "1"); bench_decimal_op!(mul_two, *, "2.01", "2"); bench_decimal_op!(mul_one_hundred, *, "2.01", "100"); bench_decimal_op!(mul_point_zero_one, *, "2.01", "0.01"); bench_decimal_op!(mul_negative_point_five, *, "2.01", "-0.5"); bench_decimal_op!(mul_pi, *, "2.01", "3.1415926535897932384626433832"); bench_decimal_op!(mul_negative_pi, *, "2.01", "-3.1415926535897932384626433832"); bench_fold_op!(mul_25, *, Decimal::from_str("1.1").unwrap(), 25); /* Div */ bench_decimal_op!(div_one, /, "2.01", "1"); bench_decimal_op!(div_two, /, "2.01", "2"); bench_decimal_op!(div_one_hundred, /, "2.01", "100"); bench_decimal_op!(div_point_zero_one, /, "2.01", "0.01"); bench_decimal_op!(div_negative_point_five, /, "2.01", "-0.5"); bench_decimal_op!(div_pi, /, "2.01", "3.1415926535897932384626433832"); bench_decimal_op!(div_negative_pi, /, "2.01", "-3.1415926535897932384626433832"); bench_decimal_op!(div_no_underflow, /, "1.02343545345", "0.35454343453"); bench_fold_op!(div_10k, /, Decimal::MAX, 10_000); bench_fold_op!(rem_10k, %, Decimal::MAX, 10_000); /* Iteration */ struct DecimalIterator { count: usize, } impl DecimalIterator { fn new() -> DecimalIterator { DecimalIterator { count: 0 } } } impl Iterator for DecimalIterator { type Item = Decimal; fn next(&mut self) -> Option { self.count += 1; if self.count < 6 { Some(Decimal::new(314, 2)) } else { None } } } #[bench] fn iterator_individual(b: &mut ::test::Bencher) { b.iter(|| { let mut result = Decimal::new(0, 0); let iterator = DecimalIterator::new(); for i in iterator { result += i; } ::test::black_box(result); }); } #[bench] fn iterator_sum(b: &mut ::test::Bencher) { b.iter(|| { let result: Decimal = DecimalIterator::new().sum(); ::test::black_box(result); }); } const SAMPLE_STRS: &[&str] = &[ "3950.123456", "3950", "0.1", "0.01", "0.001", "0.0001", "0.00001", "0.000001", "1", "-100", "-123.456", "119996.25", "1000000", "9999999.99999", "12340.56789", ]; #[bench] fn serialize_bincode(b: &mut test::Bencher) { let decimals: Vec = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect(); b.iter(|| { for d in &decimals { let bytes = bincode::options().serialize(d).unwrap(); test::black_box(bytes); } }) } #[cfg(feature = "serde-str")] #[bench] fn deserialize_bincode(b: &mut test::Bencher) { let payloads: Vec> = SAMPLE_STRS .iter() .map(|s| bincode::options().serialize(&Decimal::from_str(s).unwrap()).unwrap()) .collect(); b.iter(|| { for payload in &payloads { let decimal: Decimal = bincode::options().deserialize(payload).unwrap(); test::black_box(decimal); } }) } #[bench] fn decimal_from_str(b: &mut test::Bencher) { b.iter(|| { for s in SAMPLE_STRS { let result = Decimal::from_str(s).unwrap(); test::black_box(result); } }) } #[bench] fn decimal_to_string(b: &mut test::Bencher) { let decimals: Vec = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect(); b.iter(|| { for s in decimals.iter() { let string = s.to_string(); test::black_box(string); } }) } #[cfg(feature = "postgres")] #[bench] fn to_from_sql(b: &mut ::test::Bencher) { use bytes::BytesMut; use postgres::types::{FromSql, Kind, ToSql, Type}; let samples: Vec = test::black_box(SAMPLE_STRS.iter().map(|x| Decimal::from_str(x).unwrap()).collect()); let t = Type::new("".into(), 0, Kind::Simple, "".into()); let mut bytes: BytesMut = BytesMut::with_capacity(100).into(); b.iter(|| { for _ in 0..100 { for sample in &samples { bytes.clear(); sample.to_sql(&t, &mut bytes).unwrap(); let result = Decimal::from_sql(&t, &bytes).unwrap(); ::test::black_box(result); } } }); } #[cfg(feature = "maths")] mod maths { use rust_decimal::prelude::*; #[bench] fn powi(b: &mut ::test::Bencher) { // These exponents have to be fairly small because multiplcation overflows easily let samples = &[ (Decimal::from_str("36.7").unwrap(), 5), (Decimal::from_str("0.00000007").unwrap(), 5), (Decimal::from(2), 64), (Decimal::from_str("8819287.19276555").unwrap(), 3), (Decimal::from_str("-8819287.19276555").unwrap(), 3), ]; b.iter(|| { for sample in samples.iter() { let result = sample.0.powi(sample.1); ::test::black_box(result); } }); } #[bench] fn sqrt(b: &mut ::test::Bencher) { let samples = &[ Decimal::from_str("36.7").unwrap(), Decimal::from_str("0.00000007").unwrap(), Decimal::from(2), Decimal::from_str("8819287.19276555").unwrap(), Decimal::from_str("-8819287.19276555").unwrap(), ]; b.iter(|| { for sample in samples.iter() { let result = sample.sqrt(); ::test::black_box(result); } }); } #[bench] fn exp(b: &mut ::test::Bencher) { let samples = &[ Decimal::from_str("3.7").unwrap(), Decimal::from_str("0.07").unwrap(), Decimal::from(2), Decimal::from_str("8.19").unwrap(), Decimal::from_str("-8.19").unwrap(), ]; b.iter(|| { for sample in samples.iter() { let result = sample.exp(); ::test::black_box(result); } }); } #[bench] fn norm_cdf(b: &mut ::test::Bencher) { let samples = &[ Decimal::from_str("3.7").unwrap(), Decimal::from_str("0.007").unwrap(), Decimal::from(2), Decimal::from_str("1.19").unwrap(), Decimal::from_str("-1.19").unwrap(), ]; b.iter(|| { for sample in samples.iter() { let result = sample.norm_cdf(); ::test::black_box(result); } }); } #[bench] fn norm_pdf(b: &mut ::test::Bencher) { let samples = &[ Decimal::from_str("3.7").unwrap(), Decimal::from_str("0.007").unwrap(), Decimal::from(2), Decimal::from_str("1.19").unwrap(), Decimal::from_str("-1.19").unwrap(), ]; b.iter(|| { for sample in samples.iter() { let result = sample.norm_pdf(); ::test::black_box(result); } }); } #[bench] fn ln(b: &mut ::test::Bencher) { let samples = &[ Decimal::from_str("36.7").unwrap(), Decimal::from_str("0.00000007").unwrap(), Decimal::from(2), Decimal::from_str("8819287.19").unwrap(), Decimal::from_str("-8819287.19").unwrap(), ]; b.iter(|| { for sample in samples.iter() { let result = sample.ln(); ::test::black_box(result); } }); } #[bench] fn erf(b: &mut ::test::Bencher) { let samples = &[ Decimal::from(0), Decimal::from(1), Decimal::from_str("-0.98717").unwrap(), Decimal::from_str("0.07").unwrap(), Decimal::from_str("0.1111").unwrap(), Decimal::from_str("0.4").unwrap(), ]; b.iter(|| { for sample in samples.iter() { let result = sample.erf(); ::test::black_box(result); } }); } } rust_decimal-1.17.0/rustfmt.toml000064400000000000000000000000170072674642500150140ustar 00000000000000max_width = 120rust_decimal-1.17.0/src/constants.rs000064400000000000000000000043240072674642500155710ustar 00000000000000// Sign mask for the flags field. A value of zero in this bit indicates a // positive Decimal value, and a value of one in this bit indicates a // negative Decimal value. pub const SIGN_MASK: u32 = 0x8000_0000; pub const UNSIGN_MASK: u32 = 0x4FFF_FFFF; // Scale mask for the flags field. This byte in the flags field contains // the power of 10 to divide the Decimal value by. The scale byte must // contain a value between 0 and 28 inclusive. pub const SCALE_MASK: u32 = 0x00FF_0000; pub const U8_MASK: u32 = 0x0000_00FF; pub const U32_MASK: u64 = 0xFFFF_FFFF; // Number of bits scale is shifted by. pub const SCALE_SHIFT: u32 = 16; // Number of bits sign is shifted by. pub const SIGN_SHIFT: u32 = 31; // The maximum string buffer size used for serialization purposes. 31 is optimal, however we align // to the byte boundary for simplicity. pub const MAX_STR_BUFFER_SIZE: usize = 32; // The maximum supported precision pub const MAX_PRECISION: u8 = 28; #[cfg(not(feature = "legacy-ops"))] // u8 to i32 is infallible, therefore, this cast will never overflow pub const MAX_PRECISION_I32: i32 = MAX_PRECISION as _; // u8 to u32 is infallible, therefore, this cast will never overflow pub const MAX_PRECISION_U32: u32 = MAX_PRECISION as _; // 79,228,162,514,264,337,593,543,950,335 pub const MAX_I128_REPR: i128 = 0x0000_0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; // Fast access for 10^n where n is 0-9 pub const POWERS_10: [u32; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, ]; // Fast access for 10^n where n is 1-19 pub const BIG_POWERS_10: [u64; 19] = [ 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000, ]; #[cfg(not(feature = "legacy-ops"))] // The maximum power of 10 that a 32 bit integer can store pub const MAX_I32_SCALE: i32 = 9; #[cfg(not(feature = "legacy-ops"))] // The maximum power of 10 that a 64 bit integer can store pub const MAX_I64_SCALE: u32 = 19; #[cfg(not(feature = "legacy-ops"))] pub const U32_MAX: u64 = u32::MAX as u64; rust_decimal-1.17.0/src/db.rs000064400000000000000000000714100072674642500141420ustar 00000000000000use crate::constants::MAX_PRECISION_U32; use crate::{ ops::array::{div_by_u32, is_all_zero, mul_by_u32}, Decimal, }; use core::{convert::TryInto, fmt}; use std::error; #[derive(Debug, Clone)] pub struct InvalidDecimal { inner: Option, } impl fmt::Display for InvalidDecimal { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { if let Some(ref msg) = self.inner { fmt.write_fmt(format_args!("Invalid Decimal: {}", msg)) } else { fmt.write_str("Invalid Decimal") } } } impl error::Error for InvalidDecimal {} struct PostgresDecimal { neg: bool, weight: i16, scale: u16, digits: D, } impl Decimal { fn from_postgres>( PostgresDecimal { neg, scale, digits, weight, }: PostgresDecimal, ) -> Self { let mut digits = digits.into_iter().collect::>(); let fractionals_part_count = digits.len() as i32 + (-weight as i32) - 1; let integers_part_count = weight as i32 + 1; let mut result = Decimal::ZERO; // adding integer part if integers_part_count > 0 { let (start_integers, last) = if integers_part_count > digits.len() as i32 { (integers_part_count - digits.len() as i32, digits.len() as i32) } else { (0, integers_part_count) }; let integers: Vec<_> = digits.drain(..last as usize).collect(); for digit in integers { result *= Decimal::from_i128_with_scale(10i128.pow(4), 0); result += Decimal::new(digit as i64, 0); } result *= Decimal::from_i128_with_scale(10i128.pow(4 * start_integers as u32), 0); } // adding fractional part if fractionals_part_count > 0 { let start_fractionals = if weight < 0 { (-weight as u32) - 1 } else { 0 }; for (i, digit) in digits.into_iter().enumerate() { let fract_pow = 4 * (i as u32 + 1 + start_fractionals); if fract_pow <= MAX_PRECISION_U32 { result += Decimal::new(digit as i64, 0) / Decimal::from_i128_with_scale(10i128.pow(fract_pow), 0); } else if fract_pow == MAX_PRECISION_U32 + 4 { // rounding last digit if digit >= 5000 { result += Decimal::new(1_i64, 0) / Decimal::from_i128_with_scale(10i128.pow(MAX_PRECISION_U32), 0); } } } } result.set_sign_negative(neg); // Rescale to the postgres value, automatically rounding as needed. result.rescale(scale as u32); result } fn to_postgres(self) -> PostgresDecimal> { if self.is_zero() { return PostgresDecimal { neg: false, weight: 0, scale: 0, digits: vec![0], }; } let scale = self.scale() as u16; let groups_diff = scale & 0x3; // groups_diff = scale % 4 let mut mantissa = self.mantissa_array4(); if groups_diff > 0 { let remainder = 4 - groups_diff; let power = 10u32.pow(u32::from(remainder)); mul_by_u32(&mut mantissa, power); } // array to store max mantissa of Decimal in Postgres decimal format const MAX_GROUP_COUNT: usize = 8; let mut digits = Vec::with_capacity(MAX_GROUP_COUNT); while !is_all_zero(&mantissa) { let digit = div_by_u32(&mut mantissa, 10000) as u16; digits.push(digit.try_into().unwrap()); } digits.reverse(); let digits_after_decimal = (scale + 3) as u16 / 4; let weight = digits.len() as i16 - digits_after_decimal as i16 - 1; let unnecessary_zeroes = if weight >= 0 { let index_of_decimal = (weight + 1) as usize; digits .get(index_of_decimal..) .expect("enough digits exist") .iter() .rev() .take_while(|i| **i == 0) .count() } else { 0 }; let relevant_digits = digits.len() - unnecessary_zeroes; digits.truncate(relevant_digits); PostgresDecimal { neg: self.is_sign_negative(), digits, scale, weight, } } } #[cfg(feature = "diesel")] mod diesel { use super::*; use ::diesel::{ deserialize::{self, FromSql}, pg::data_types::PgNumeric, pg::Pg, serialize::{self, Output, ToSql}, sql_types::Numeric, }; use core::convert::{TryFrom, TryInto}; use std::io::Write; impl<'a> TryFrom<&'a PgNumeric> for Decimal { type Error = Box; fn try_from(numeric: &'a PgNumeric) -> deserialize::Result { let (neg, weight, scale, digits) = match *numeric { PgNumeric::Positive { weight, scale, ref digits, } => (false, weight, scale, digits), PgNumeric::Negative { weight, scale, ref digits, } => (true, weight, scale, digits), PgNumeric::NaN => return Err(Box::from("NaN is not supported in Decimal")), }; Ok(Self::from_postgres(PostgresDecimal { neg, weight, scale, digits: digits.iter().copied().map(|v| v.try_into().unwrap()), })) } } impl TryFrom for Decimal { type Error = Box; fn try_from(numeric: PgNumeric) -> deserialize::Result { (&numeric).try_into() } } impl<'a> From<&'a Decimal> for PgNumeric { fn from(decimal: &'a Decimal) -> Self { let PostgresDecimal { neg, weight, scale, digits, } = decimal.to_postgres(); if neg { PgNumeric::Negative { digits, scale, weight } } else { PgNumeric::Positive { digits, scale, weight } } } } impl From for PgNumeric { fn from(bigdecimal: Decimal) -> Self { (&bigdecimal).into() } } impl ToSql for Decimal { fn to_sql(&self, out: &mut Output) -> serialize::Result { let numeric = PgNumeric::from(self); ToSql::::to_sql(&numeric, out) } } impl FromSql for Decimal { fn from_sql(numeric: Option<&[u8]>) -> deserialize::Result { PgNumeric::from_sql(numeric)?.try_into() } } #[cfg(test)] mod pg_tests { use super::*; use core::str::FromStr; #[test] fn test_unnecessary_zeroes() { fn extract(value: &str) -> Decimal { Decimal::from_str(value).unwrap() } let tests = &[ ("0.000001660"), ("41.120255926293000"), ("0.5538973300"), ("08883.55986854293100"), ("0.0000_0000_0016_6000_00"), ("0.00000166650000"), ("1666500000000"), ("1666500000000.0000054500"), ("8944.000000000000"), ]; for &value in tests { let value = extract(value); let pg = PgNumeric::from(value); let dec = Decimal::try_from(pg).unwrap(); assert_eq!(dec, value); } } #[test] fn decimal_to_pgnumeric_converts_digits_to_base_10000() { let decimal = Decimal::from_str("1").unwrap(); let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![1], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("10").unwrap(); let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![10], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("10000").unwrap(); let expected = PgNumeric::Positive { weight: 1, scale: 0, digits: vec![1, 0], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("10001").unwrap(); let expected = PgNumeric::Positive { weight: 1, scale: 0, digits: vec![1, 1], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("100000000").unwrap(); let expected = PgNumeric::Positive { weight: 2, scale: 0, digits: vec![1, 0, 0], }; assert_eq!(expected, decimal.into()); } #[test] fn decimal_to_pg_numeric_properly_adjusts_scale() { let decimal = Decimal::from_str("1").unwrap(); let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![1], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("1.0").unwrap(); let expected = PgNumeric::Positive { weight: 0, scale: 1, digits: vec![1], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("1.1").unwrap(); let expected = PgNumeric::Positive { weight: 0, scale: 1, digits: vec![1, 1000], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("1.10").unwrap(); let expected = PgNumeric::Positive { weight: 0, scale: 2, digits: vec![1, 1000], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("100000000.0001").unwrap(); let expected = PgNumeric::Positive { weight: 2, scale: 4, digits: vec![1, 0, 0, 1], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("0.1").unwrap(); let expected = PgNumeric::Positive { weight: -1, scale: 1, digits: vec![1000], }; assert_eq!(expected, decimal.into()); } #[test] #[cfg(feature = "unstable")] fn decimal_to_pg_numeric_retains_sign() { let decimal = Decimal::from_str("123.456").unwrap(); let expected = PgNumeric::Positive { weight: 0, scale: 3, digits: vec![123, 4560], }; assert_eq!(expected, decimal.into()); let decimal = Decimal::from_str("-123.456").unwrap(); let expected = PgNumeric::Negative { weight: 0, scale: 3, digits: vec![123, 4560], }; assert_eq!(expected, decimal.into()); } #[test] fn pg_numeric_to_decimal_works() { let expected = Decimal::from_str("50").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![50], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res, expected); let expected = Decimal::from_str("123.456").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 3, digits: vec![123, 4560], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res, expected); let expected = Decimal::from_str("-56.78").unwrap(); let pg_numeric = PgNumeric::Negative { weight: 0, scale: 2, digits: vec![56, 7800], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res, expected); // Verify no trailing zeroes are lost. let expected = Decimal::from_str("1.100").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 3, digits: vec![1, 1000], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res.to_string(), expected.to_string()); // To represent 5.00, Postgres can return either [5, 0] as the list of digits. let expected = Decimal::from_str("5.00").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 2, digits: vec![5, 0], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res.to_string(), expected.to_string()); // To represent 5.00, Postgres can return [5] as the list of digits. let expected = Decimal::from_str("5.00").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 2, digits: vec![5], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res.to_string(), expected.to_string()); let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 30, digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res.to_string(), expected.to_string()); let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 34, digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res.to_string(), expected.to_string()); let expected = Decimal::from_str("1.2345678901234567890123456790").unwrap(); let pg_numeric = PgNumeric::Positive { weight: 0, scale: 34, digits: vec![1, 2345, 6789, 0123, 4567, 8901, 2345, 6789, 5000, 0], }; let res: Decimal = pg_numeric.try_into().unwrap(); assert_eq!(res.to_string(), expected.to_string()); } } } #[cfg(feature = "postgres")] mod postgres { use super::*; use ::postgres::types::{to_sql_checked, FromSql, IsNull, ToSql, Type}; use byteorder::{BigEndian, ReadBytesExt}; use bytes::{BufMut, BytesMut}; use std::io::Cursor; impl<'a> FromSql<'a> for Decimal { // Decimals are represented as follows: // Header: // u16 numGroups // i16 weightFirstGroup (10000^weight) // u16 sign (0x0000 = positive, 0x4000 = negative, 0xC000 = NaN) // i16 dscale. Number of digits (in base 10) to print after decimal separator // // Pseudo code : // const Decimals [ // 0.0000000000000000000000000001, // 0.000000000000000000000001, // 0.00000000000000000001, // 0.0000000000000001, // 0.000000000001, // 0.00000001, // 0.0001, // 1, // 10000, // 100000000, // 1000000000000, // 10000000000000000, // 100000000000000000000, // 1000000000000000000000000, // 10000000000000000000000000000 // ] // overflow = false // result = 0 // for i = 0, weight = weightFirstGroup + 7; i < numGroups; i++, weight-- // group = read.u16 // if weight < 0 or weight > MaxNum // overflow = true // else // result += Decimals[weight] * group // sign == 0x4000 ? -result : result // So if we were to take the number: 3950.123456 // // Stored on Disk: // 00 03 00 00 00 00 00 06 0F 6E 04 D2 15 E0 // // Number of groups: 00 03 // Weight of first group: 00 00 // Sign: 00 00 // DScale: 00 06 // // 0F 6E = 3950 // result = result + 3950 * 1; // 04 D2 = 1234 // result = result + 1234 * 0.0001; // 15 E0 = 5600 // result = result + 5600 * 0.00000001; // fn from_sql(_: &Type, raw: &[u8]) -> Result> { let mut raw = Cursor::new(raw); let num_groups = raw.read_u16::()?; let weight = raw.read_i16::()?; // 10000^weight // Sign: 0x0000 = positive, 0x4000 = negative, 0xC000 = NaN let sign = raw.read_u16::()?; // Number of digits (in base 10) to print after decimal separator let scale = raw.read_u16::()?; // Read all of the groups let mut groups = Vec::new(); for _ in 0..num_groups as usize { groups.push(raw.read_u16::()?); } Ok(Self::from_postgres(PostgresDecimal { neg: sign == 0x4000, weight, scale, digits: groups.into_iter(), })) } fn accepts(ty: &Type) -> bool { matches!(*ty, Type::NUMERIC) } } impl ToSql for Decimal { fn to_sql( &self, _: &Type, out: &mut BytesMut, ) -> Result> { let PostgresDecimal { neg, weight, scale, digits, } = self.to_postgres(); let num_digits = digits.len(); // Reserve bytes out.reserve(8 + num_digits * 2); // Number of groups out.put_u16(num_digits.try_into().unwrap()); // Weight of first group out.put_i16(weight); // Sign out.put_u16(if neg { 0x4000 } else { 0x0000 }); // DScale out.put_u16(scale); // Now process the number for digit in digits[0..num_digits].iter() { out.put_i16(*digit); } Ok(IsNull::No) } fn accepts(ty: &Type) -> bool { matches!(*ty, Type::NUMERIC) } to_sql_checked!(); } #[cfg(test)] mod test { use super::*; use ::postgres::{Client, NoTls}; use core::str::FromStr; /// Gets the URL for connecting to PostgreSQL for testing. Set the POSTGRES_URL /// environment variable to change from the default of "postgres://postgres@localhost". fn get_postgres_url() -> String { if let Ok(url) = std::env::var("POSTGRES_URL") { return url; } "postgres://postgres@localhost".to_string() } pub static TEST_DECIMALS: &[(u32, u32, &str, &str)] = &[ // precision, scale, sent, expected (35, 6, "3950.123456", "3950.123456"), (35, 2, "3950.123456", "3950.12"), (35, 2, "3950.1256", "3950.13"), (10, 2, "3950.123456", "3950.12"), (35, 6, "3950", "3950.000000"), (4, 0, "3950", "3950"), (35, 6, "0.1", "0.100000"), (35, 6, "0.01", "0.010000"), (35, 6, "0.001", "0.001000"), (35, 6, "0.0001", "0.000100"), (35, 6, "0.00001", "0.000010"), (35, 6, "0.000001", "0.000001"), (35, 6, "1", "1.000000"), (35, 6, "-100", "-100.000000"), (35, 6, "-123.456", "-123.456000"), (35, 6, "119996.25", "119996.250000"), (35, 6, "1000000", "1000000.000000"), (35, 6, "9999999.99999", "9999999.999990"), (35, 6, "12340.56789", "12340.567890"), // Scale is only 28 since that is the maximum we can represent. (65, 30, "1.2", "1.2000000000000000000000000000"), // Pi - rounded at scale 28 ( 65, 30, "3.141592653589793238462643383279", "3.1415926535897932384626433833", ), ( 65, 34, "3.1415926535897932384626433832795028", "3.1415926535897932384626433833", ), // Unrounded number ( 65, 34, "1.234567890123456789012345678950000", "1.2345678901234567890123456790", ), ( 65, 34, // No rounding due to 49999 after significant digits "1.234567890123456789012345678949999", "1.2345678901234567890123456789", ), // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF (96 bit) (35, 0, "79228162514264337593543950335", "79228162514264337593543950335"), // 0x0FFF_FFFF_FFFF_FFFF_FFFF_FFFF (95 bit) (35, 1, "4951760157141521099596496895", "4951760157141521099596496895.0"), // 0x1000_0000_0000_0000_0000_0000 (35, 1, "4951760157141521099596496896", "4951760157141521099596496896.0"), (35, 6, "18446744073709551615", "18446744073709551615.000000"), (35, 6, "-18446744073709551615", "-18446744073709551615.000000"), (35, 6, "0.10001", "0.100010"), (35, 6, "0.12345", "0.123450"), ]; #[test] fn test_null() { let mut client = match Client::connect(&get_postgres_url(), NoTls) { Ok(x) => x, Err(err) => panic!("{:#?}", err), }; // Test NULL let result: Option = match client.query("SELECT NULL::numeric", &[]) { Ok(x) => x.iter().next().unwrap().get(0), Err(err) => panic!("{:#?}", err), }; assert_eq!(None, result); } #[tokio::test] #[cfg(feature = "tokio-pg")] async fn async_test_null() { use futures::future::FutureExt; use tokio_postgres::connect; let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); let connection = connection.map(|e| e.unwrap()); tokio::spawn(connection); let statement = client.prepare(&"SELECT NULL::numeric").await.unwrap(); let rows = client.query(&statement, &[]).await.unwrap(); let result: Option = rows.iter().next().unwrap().get(0); assert_eq!(None, result); } #[test] fn read_numeric_type() { let mut client = match Client::connect(&get_postgres_url(), NoTls) { Ok(x) => x, Err(err) => panic!("{:#?}", err), }; for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { let result: Decimal = match client.query(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale), &[]) { Ok(x) => x.iter().next().unwrap().get(0), Err(err) => panic!("SELECT {}::NUMERIC({}, {}), error - {:#?}", sent, precision, scale, err), }; assert_eq!( expected, result.to_string(), "NUMERIC({}, {}) sent: {}", precision, scale, sent ); } } #[tokio::test] #[cfg(feature = "tokio-pg")] async fn async_read_numeric_type() { use futures::future::FutureExt; use tokio_postgres::connect; let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); let connection = connection.map(|e| e.unwrap()); tokio::spawn(connection); for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { let statement = client .prepare(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale)) .await .unwrap(); let rows = client.query(&statement, &[]).await.unwrap(); let result: Decimal = rows.iter().next().unwrap().get(0); assert_eq!(expected, result.to_string(), "NUMERIC({}, {})", precision, scale); } } #[test] fn write_numeric_type() { let mut client = match Client::connect(&get_postgres_url(), NoTls) { Ok(x) => x, Err(err) => panic!("{:#?}", err), }; for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { let number = Decimal::from_str(sent).unwrap(); let result: Decimal = match client.query(&*format!("SELECT $1::NUMERIC({}, {})", precision, scale), &[&number]) { Ok(x) => x.iter().next().unwrap().get(0), Err(err) => panic!("{:#?}", err), }; assert_eq!(expected, result.to_string(), "NUMERIC({}, {})", precision, scale); } } #[tokio::test] #[cfg(feature = "tokio-pg")] async fn async_write_numeric_type() { use futures::future::FutureExt; use tokio_postgres::connect; let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); let connection = connection.map(|e| e.unwrap()); tokio::spawn(connection); for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { let statement = client .prepare(&*format!("SELECT $1::NUMERIC({}, {})", precision, scale)) .await .unwrap(); let number = Decimal::from_str(sent).unwrap(); let rows = client.query(&statement, &[&number]).await.unwrap(); let result: Decimal = rows.iter().next().unwrap().get(0); assert_eq!(expected, result.to_string(), "NUMERIC({}, {})", precision, scale); } } #[test] fn numeric_overflow() { let tests = [(4, 4, "3950.1234")]; let mut client = match Client::connect(&get_postgres_url(), NoTls) { Ok(x) => x, Err(err) => panic!("{:#?}", err), }; for &(precision, scale, sent) in tests.iter() { match client.query(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale), &[]) { Ok(_) => panic!( "Expected numeric overflow for {}::NUMERIC({}, {})", sent, precision, scale ), Err(err) => { assert_eq!("22003", err.code().unwrap().code(), "Unexpected error code"); } }; } } #[tokio::test] #[cfg(feature = "tokio-pg")] async fn async_numeric_overflow() { use futures::future::FutureExt; use tokio_postgres::connect; let tests = [(4, 4, "3950.1234")]; let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); let connection = connection.map(|e| e.unwrap()); tokio::spawn(connection); for &(precision, scale, sent) in tests.iter() { let statement = client .prepare(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale)) .await .unwrap(); match client.query(&statement, &[]).await { Ok(_) => panic!( "Expected numeric overflow for {}::NUMERIC({}, {})", sent, precision, scale ), Err(err) => assert_eq!("22003", err.code().unwrap().code(), "Unexpected error code"), } } } } } rust_decimal-1.17.0/src/decimal.rs000064400000000000000000002331300072674642500151520ustar 00000000000000use crate::constants::{ MAX_I128_REPR, MAX_PRECISION_U32, POWERS_10, SCALE_MASK, SCALE_SHIFT, SIGN_MASK, SIGN_SHIFT, U32_MASK, U8_MASK, UNSIGN_MASK, }; use crate::ops; use crate::Error; use core::{ cmp::{Ordering::Equal, *}, fmt, hash::{Hash, Hasher}, iter::Sum, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, str::FromStr, }; #[cfg(feature = "diesel")] use diesel::sql_types::Numeric; #[allow(unused_imports)] // It's not actually dead code below, but the compiler thinks it is. #[cfg(not(feature = "std"))] use num_traits::float::FloatCore; use num_traits::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedRem, CheckedSub, FromPrimitive, Num, One, Signed, ToPrimitive, Zero, }; /// The smallest value that can be represented by this decimal type. const MIN: Decimal = Decimal { flags: 2_147_483_648, lo: 4_294_967_295, mid: 4_294_967_295, hi: 4_294_967_295, }; /// The largest value that can be represented by this decimal type. const MAX: Decimal = Decimal { flags: 0, lo: 4_294_967_295, mid: 4_294_967_295, hi: 4_294_967_295, }; const ZERO: Decimal = Decimal { flags: 0, lo: 0, mid: 0, hi: 0, }; const ONE: Decimal = Decimal { flags: 0, lo: 1, mid: 0, hi: 0, }; const TWO: Decimal = Decimal { flags: 0, lo: 2, mid: 0, hi: 0, }; const TEN: Decimal = Decimal { flags: 0, lo: 10, mid: 0, hi: 0, }; const ONE_HUNDRED: Decimal = Decimal { flags: 0, lo: 100, mid: 0, hi: 0, }; const ONE_THOUSAND: Decimal = Decimal { flags: 0, lo: 1000, mid: 0, hi: 0, }; const NEGATIVE_ONE: Decimal = Decimal { flags: 2147483648, lo: 1, mid: 0, hi: 0, }; /// `UnpackedDecimal` contains unpacked representation of `Decimal` where each component /// of decimal-format stored in it's own field #[derive(Clone, Copy, Debug)] pub struct UnpackedDecimal { pub negative: bool, pub scale: u32, pub hi: u32, pub mid: u32, pub lo: u32, } /// `Decimal` represents a 128 bit representation of a fixed-precision decimal number. /// The finite set of values of type `Decimal` are of the form m / 10e, /// where m is an integer such that -296 < m < 296, and e is an integer /// between 0 and 28 inclusive. #[derive(Clone, Copy)] #[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression), sql_type = "Numeric")] #[cfg_attr(feature = "c-repr", repr(C))] pub struct Decimal { // Bits 0-15: unused // Bits 16-23: Contains "e", a value between 0-28 that indicates the scale // Bits 24-30: unused // Bit 31: the sign of the Decimal value, 0 meaning positive and 1 meaning negative. flags: u32, // The lo, mid, hi, and flags fields contain the representation of the // Decimal value as a 96-bit integer. hi: u32, lo: u32, mid: u32, } /// `RoundingStrategy` represents the different rounding strategies that can be used by /// `round_dp_with_strategy`. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum RoundingStrategy { /// When a number is halfway between two others, it is rounded toward the nearest even number. /// Also known as "Bankers Rounding". /// e.g. /// 6.5 -> 6, 7.5 -> 8 MidpointNearestEven, /// When a number is halfway between two others, it is rounded toward the nearest number that /// is away from zero. e.g. 6.4 -> 6, 6.5 -> 7, -6.5 -> -7 MidpointAwayFromZero, /// When a number is halfway between two others, it is rounded toward the nearest number that /// is toward zero. e.g. 6.4 -> 6, 6.5 -> 7, -6.5 -> -6 MidpointTowardZero, /// The number is always rounded toward zero. e.g. -6.8 -> -6, 6.8 -> 6 ToZero, /// The number is always rounded away from zero. e.g. -6.8 -> -7, 6.8 -> 7 AwayFromZero, /// The number is always rounded towards negative infinity. e.g. 6.8 -> 6, -6.8 -> -7 ToNegativeInfinity, /// The number is always rounded towards positive infinity. e.g. 6.8 -> 7, -6.8 -> -6 ToPositiveInfinity, /// When a number is halfway between two others, it is rounded toward the nearest even number. /// e.g. /// 6.5 -> 6, 7.5 -> 8 #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::MidpointNearestEven instead")] BankersRounding, /// Rounds up if the value >= 5, otherwise rounds down, e.g. 6.5 -> 7 #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::MidpointAwayFromZero instead")] RoundHalfUp, /// Rounds down if the value =< 5, otherwise rounds up, e.g. 6.5 -> 6, 6.51 -> 7 1.4999999 -> 1 #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::MidpointTowardZero instead")] RoundHalfDown, /// Always round down. #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::ToZero instead")] RoundDown, /// Always round up. #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::AwayFromZero instead")] RoundUp, } #[allow(dead_code)] impl Decimal { /// The smallest value that can be represented by this decimal type. pub const MIN: Decimal = MIN; /// The largest value that can be represented by this decimal type. pub const MAX: Decimal = MAX; /// A constant representing 0. pub const ZERO: Decimal = ZERO; /// A constant representing 1. pub const ONE: Decimal = ONE; /// A constant representing -1. pub const NEGATIVE_ONE: Decimal = NEGATIVE_ONE; /// A constant representing 2. pub const TWO: Decimal = TWO; /// A constant representing 10. pub const TEN: Decimal = TEN; /// A constant representing 100. pub const ONE_HUNDRED: Decimal = ONE_HUNDRED; /// A constant representing 1000. pub const ONE_THOUSAND: Decimal = ONE_THOUSAND; /// A constant representing Ï€ as 3.1415926535897932384626433833 #[cfg(feature = "maths")] pub const PI: Decimal = Decimal { flags: 1835008, lo: 1102470953, mid: 185874565, hi: 1703060790, }; /// A constant representing Ï€/2 as 1.5707963267948966192313216916 #[cfg(feature = "maths")] pub const HALF_PI: Decimal = Decimal { flags: 1835008, lo: 2698719124, mid: 92937282, hi: 851530395, }; /// A constant representing Ï€/4 as 0.7853981633974483096156608458 #[cfg(feature = "maths")] pub const QUARTER_PI: Decimal = Decimal { flags: 1835008, lo: 1349359562, mid: 2193952289, hi: 425765197, }; /// A constant representing 2Ï€ as 6.2831853071795864769252867666 #[cfg(feature = "maths")] pub const TWO_PI: Decimal = Decimal { flags: 1835008, lo: 2204941906, mid: 371749130, hi: 3406121580, }; /// A constant representing Euler's number (e) as 2.7182818284590452353602874714 #[cfg(feature = "maths")] pub const E: Decimal = Decimal { flags: 1835008, lo: 2239425882, mid: 3958169141, hi: 1473583531, }; /// A constant representing the inverse of Euler's number (1/e) as 0.3678794411714423215955237702 #[cfg(feature = "maths")] pub const E_INVERSE: Decimal = Decimal { flags: 1835008, lo: 2384059206, mid: 2857938002, hi: 199427844, }; /// Returns a `Decimal` with a 64 bit `m` representation and corresponding `e` scale. /// /// # Arguments /// /// * `num` - An i64 that represents the `m` portion of the decimal number /// * `scale` - A u32 representing the `e` portion of the decimal number. /// /// # Panics /// /// This function panics if `scale` is > 28. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let pi = Decimal::new(3141, 3); /// assert_eq!(pi.to_string(), "3.141"); /// ``` #[must_use] pub fn new(num: i64, scale: u32) -> Decimal { match Self::try_new(num, scale) { Err(e) => panic!("{}", e), Ok(d) => d, } } /// Checked version of `Decimal::new`. Will return `Err` instead of panicking at run-time. /// /// # Example /// /// ```rust /// use rust_decimal::Decimal; /// /// let max = Decimal::try_new(i64::MAX, u32::MAX); /// assert!(max.is_err()); /// ``` pub const fn try_new(num: i64, scale: u32) -> crate::Result { if scale > MAX_PRECISION_U32 { return Err(Error::ScaleExceedsMaximumPrecision(scale)); } let flags: u32 = scale << SCALE_SHIFT; if num < 0 { let pos_num = num.wrapping_neg() as u64; return Ok(Decimal { flags: flags | SIGN_MASK, hi: 0, lo: (pos_num & U32_MASK) as u32, mid: ((pos_num >> 32) & U32_MASK) as u32, }); } Ok(Decimal { flags, hi: 0, lo: (num as u64 & U32_MASK) as u32, mid: ((num as u64 >> 32) & U32_MASK) as u32, }) } /// Creates a `Decimal` using a 128 bit signed `m` representation and corresponding `e` scale. /// /// # Arguments /// /// * `num` - An i128 that represents the `m` portion of the decimal number /// * `scale` - A u32 representing the `e` portion of the decimal number. /// /// # Panics /// /// This function panics if `scale` is > 28 or if `num` exceeds the maximum supported 96 bits. /// /// # Example /// /// ```rust /// use rust_decimal::Decimal; /// /// let pi = Decimal::from_i128_with_scale(3141i128, 3); /// assert_eq!(pi.to_string(), "3.141"); /// ``` #[must_use] pub fn from_i128_with_scale(num: i128, scale: u32) -> Decimal { match Self::try_from_i128_with_scale(num, scale) { Ok(d) => d, Err(e) => panic!("{}", e), } } /// Checked version of `Decimal::from_i128_with_scale`. Will return `Err` instead /// of panicking at run-time. /// /// # Example /// /// ```rust /// use rust_decimal::Decimal; /// /// let max = Decimal::try_from_i128_with_scale(i128::MAX, u32::MAX); /// assert!(max.is_err()); /// ``` pub const fn try_from_i128_with_scale(num: i128, scale: u32) -> crate::Result { if scale > MAX_PRECISION_U32 { return Err(Error::ScaleExceedsMaximumPrecision(scale)); } let mut neg = false; let mut wrapped = num; if num > MAX_I128_REPR { return Err(Error::ExceedsMaximumPossibleValue); } else if num < -MAX_I128_REPR { return Err(Error::LessThanMinimumPossibleValue); } else if num < 0 { neg = true; wrapped = -num; } let flags: u32 = flags(neg, scale); Ok(Decimal { flags, lo: (wrapped as u64 & U32_MASK) as u32, mid: ((wrapped as u64 >> 32) & U32_MASK) as u32, hi: ((wrapped as u128 >> 64) as u64 & U32_MASK) as u32, }) } /// Returns a `Decimal` using the instances constituent parts. /// /// # Arguments /// /// * `lo` - The low 32 bits of a 96-bit integer. /// * `mid` - The middle 32 bits of a 96-bit integer. /// * `hi` - The high 32 bits of a 96-bit integer. /// * `negative` - `true` to indicate a negative number. /// * `scale` - A power of 10 ranging from 0 to 28. /// /// # Caution: Undefined behavior /// /// While a scale greater than 28 can be passed in, it will be automatically capped by this /// function at the maximum precision. The library opts towards this functionality as opposed /// to a panic to ensure that the function can be treated as constant. This may lead to /// undefined behavior in downstream applications and should be treated with caution. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let pi = Decimal::from_parts(1102470952, 185874565, 1703060790, false, 28); /// assert_eq!(pi.to_string(), "3.1415926535897932384626433832"); /// ``` #[must_use] pub const fn from_parts(lo: u32, mid: u32, hi: u32, negative: bool, scale: u32) -> Decimal { Decimal { lo, mid, hi, flags: flags( if lo == 0 && mid == 0 && hi == 0 { false } else { negative }, scale % (MAX_PRECISION_U32 + 1), ), } } #[must_use] pub(crate) const fn from_parts_raw(lo: u32, mid: u32, hi: u32, flags: u32) -> Decimal { if lo == 0 && mid == 0 && hi == 0 { Decimal { lo, mid, hi, flags: flags & SCALE_MASK, } } else { Decimal { flags, hi, lo, mid } } } /// Returns a `Result` which if successful contains the `Decimal` constitution of /// the scientific notation provided by `value`. /// /// # Arguments /// /// * `value` - The scientific notation of the `Decimal`. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let value = Decimal::from_scientific("9.7e-7").unwrap(); /// assert_eq!(value.to_string(), "0.00000097"); /// ``` pub fn from_scientific(value: &str) -> Result { const ERROR_MESSAGE: &str = "Failed to parse"; let mut split = value.splitn(2, |c| c == 'e' || c == 'E'); let base = split.next().ok_or_else(|| Error::from(ERROR_MESSAGE))?; let exp = split.next().ok_or_else(|| Error::from(ERROR_MESSAGE))?; let mut ret = Decimal::from_str(base)?; let current_scale = ret.scale(); if let Some(stripped) = exp.strip_prefix('-') { let exp: u32 = stripped.parse().map_err(|_| Error::from(ERROR_MESSAGE))?; ret.set_scale(current_scale + exp)?; } else { let exp: u32 = exp.parse().map_err(|_| Error::from(ERROR_MESSAGE))?; if exp <= current_scale { ret.set_scale(current_scale - exp)?; } else if exp > 0 { use crate::constants::BIG_POWERS_10; // This is a case whereby the mantissa needs to be larger to be correctly // represented within the decimal type. A good example is 1.2E10. At this point, // we've parsed 1.2 as the base and 10 as the exponent. To represent this within a // Decimal type we effectively store the mantissa as 12,000,000,000 and scale as // zero. if exp > MAX_PRECISION_U32 { return Err(Error::ScaleExceedsMaximumPrecision(exp)); } let mut exp = exp as usize; // Max two iterations. If exp is 1 then it needs to index position 0 of the array. while exp > 0 { let pow; if exp >= BIG_POWERS_10.len() { pow = BIG_POWERS_10[BIG_POWERS_10.len() - 1]; exp -= BIG_POWERS_10.len(); } else { pow = BIG_POWERS_10[exp - 1]; exp = 0; } let pow = Decimal { flags: 0, lo: pow as u32, mid: (pow >> 32) as u32, hi: 0, }; match ret.checked_mul(pow) { Some(r) => ret = r, None => return Err(Error::ExceedsMaximumPossibleValue), }; } ret.normalize_assign(); } } Ok(ret) } /// Returns the scale of the decimal number, otherwise known as `e`. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let num = Decimal::new(1234, 3); /// assert_eq!(num.scale(), 3u32); /// ``` #[inline] #[must_use] pub const fn scale(&self) -> u32 { ((self.flags & SCALE_MASK) >> SCALE_SHIFT) as u32 } /// Returns the mantissa of the decimal number. /// /// # Example /// /// ``` /// use rust_decimal::prelude::*; /// /// let num = Decimal::from_str("-1.2345678").unwrap(); /// assert_eq!(num.mantissa(), -12345678i128); /// assert_eq!(num.scale(), 7); /// ``` #[must_use] pub const fn mantissa(&self) -> i128 { let raw = (self.lo as i128) | ((self.mid as i128) << 32) | ((self.hi as i128) << 64); if self.is_sign_negative() { -raw } else { raw } } /// Returns true if this Decimal number is equivalent to zero. /// /// # Example /// /// ``` /// use rust_decimal::prelude::*; /// /// let num = Decimal::ZERO; /// assert!(num.is_zero()); /// ``` #[must_use] pub const fn is_zero(&self) -> bool { self.lo == 0 && self.mid == 0 && self.hi == 0 } /// An optimized method for changing the sign of a decimal number. /// /// # Arguments /// /// * `positive`: true if the resulting decimal should be positive. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let mut one = Decimal::ONE; /// one.set_sign(false); /// assert_eq!(one.to_string(), "-1"); /// ``` #[deprecated(since = "1.4.0", note = "please use `set_sign_positive` instead")] pub fn set_sign(&mut self, positive: bool) { self.set_sign_positive(positive); } /// An optimized method for changing the sign of a decimal number. /// /// # Arguments /// /// * `positive`: true if the resulting decimal should be positive. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let mut one = Decimal::ONE; /// one.set_sign_positive(false); /// assert_eq!(one.to_string(), "-1"); /// ``` #[inline(always)] pub fn set_sign_positive(&mut self, positive: bool) { if positive { self.flags &= UNSIGN_MASK; } else { self.flags |= SIGN_MASK; } } /// An optimized method for changing the sign of a decimal number. /// /// # Arguments /// /// * `negative`: true if the resulting decimal should be negative. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let mut one = Decimal::ONE; /// one.set_sign_negative(true); /// assert_eq!(one.to_string(), "-1"); /// ``` #[inline(always)] pub fn set_sign_negative(&mut self, negative: bool) { self.set_sign_positive(!negative); } /// An optimized method for changing the scale of a decimal number. /// /// # Arguments /// /// * `scale`: the new scale of the number /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let mut one = Decimal::ONE; /// one.set_scale(5).unwrap(); /// assert_eq!(one.to_string(), "0.00001"); /// ``` pub fn set_scale(&mut self, scale: u32) -> Result<(), Error> { if scale > MAX_PRECISION_U32 { return Err(Error::ScaleExceedsMaximumPrecision(scale)); } self.flags = (scale << SCALE_SHIFT) | (self.flags & SIGN_MASK); Ok(()) } /// Modifies the `Decimal` to the given scale, attempting to do so without changing the /// underlying number itself. /// /// Note that setting the scale to something less then the current `Decimal`s scale will /// cause the newly created `Decimal` to have some rounding. /// Scales greater than the maximum precision supported by `Decimal` will be automatically /// rounded to `Decimal::MAX_PRECISION`. /// Rounding leverages the half up strategy. /// /// # Arguments /// * `scale`: The scale to use for the new `Decimal` number. /// /// # Example /// /// ``` /// use rust_decimal::prelude::*; /// /// // Rescaling to a higher scale preserves the value /// let mut number = Decimal::from_str("1.123").unwrap(); /// assert_eq!(number.scale(), 3); /// number.rescale(6); /// assert_eq!(number.to_string(), "1.123000"); /// assert_eq!(number.scale(), 6); /// /// // Rescaling to a lower scale forces the number to be rounded /// let mut number = Decimal::from_str("1.45").unwrap(); /// assert_eq!(number.scale(), 2); /// number.rescale(1); /// assert_eq!(number.to_string(), "1.5"); /// assert_eq!(number.scale(), 1); /// ``` pub fn rescale(&mut self, scale: u32) { let mut array = [self.lo, self.mid, self.hi]; let mut value_scale = self.scale(); ops::array::rescale_internal(&mut array, &mut value_scale, scale); self.lo = array[0]; self.mid = array[1]; self.hi = array[2]; self.flags = flags(self.is_sign_negative(), value_scale); } /// Returns a serialized version of the decimal number. /// The resulting byte array will have the following representation: /// /// * Bytes 1-4: flags /// * Bytes 5-8: lo portion of `m` /// * Bytes 9-12: mid portion of `m` /// * Bytes 13-16: high portion of `m` #[must_use] pub const fn serialize(&self) -> [u8; 16] { [ (self.flags & U8_MASK) as u8, ((self.flags >> 8) & U8_MASK) as u8, ((self.flags >> 16) & U8_MASK) as u8, ((self.flags >> 24) & U8_MASK) as u8, (self.lo & U8_MASK) as u8, ((self.lo >> 8) & U8_MASK) as u8, ((self.lo >> 16) & U8_MASK) as u8, ((self.lo >> 24) & U8_MASK) as u8, (self.mid & U8_MASK) as u8, ((self.mid >> 8) & U8_MASK) as u8, ((self.mid >> 16) & U8_MASK) as u8, ((self.mid >> 24) & U8_MASK) as u8, (self.hi & U8_MASK) as u8, ((self.hi >> 8) & U8_MASK) as u8, ((self.hi >> 16) & U8_MASK) as u8, ((self.hi >> 24) & U8_MASK) as u8, ] } /// Deserializes the given bytes into a decimal number. /// The deserialized byte representation must be 16 bytes and adhere to the following convention: /// /// * Bytes 1-4: flags /// * Bytes 5-8: lo portion of `m` /// * Bytes 9-12: mid portion of `m` /// * Bytes 13-16: high portion of `m` #[must_use] pub fn deserialize(bytes: [u8; 16]) -> Decimal { // We can bound flags by a bitwise mask to correspond to: // Bits 0-15: unused // Bits 16-23: Contains "e", a value between 0-28 that indicates the scale // Bits 24-30: unused // Bit 31: the sign of the Decimal value, 0 meaning positive and 1 meaning negative. let mut raw = Decimal { flags: ((bytes[0] as u32) | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24) & 0x801F_0000, lo: (bytes[4] as u32) | (bytes[5] as u32) << 8 | (bytes[6] as u32) << 16 | (bytes[7] as u32) << 24, mid: (bytes[8] as u32) | (bytes[9] as u32) << 8 | (bytes[10] as u32) << 16 | (bytes[11] as u32) << 24, hi: (bytes[12] as u32) | (bytes[13] as u32) << 8 | (bytes[14] as u32) << 16 | (bytes[15] as u32) << 24, }; // Scale must be bound to maximum precision. Only two values can be greater than this if raw.scale() > MAX_PRECISION_U32 { let mut bits = raw.mantissa_array3(); let remainder = match raw.scale() { 29 => crate::ops::array::div_by_1x(&mut bits, 1), 30 => crate::ops::array::div_by_1x(&mut bits, 2), 31 => crate::ops::array::div_by_1x(&mut bits, 3), _ => 0, }; if remainder >= 5 { ops::array::add_one_internal(&mut bits); } raw.lo = bits[0]; raw.mid = bits[1]; raw.hi = bits[2]; raw.flags = flags(raw.is_sign_negative(), MAX_PRECISION_U32); } raw } /// Returns `true` if the decimal is negative. #[deprecated(since = "0.6.3", note = "please use `is_sign_negative` instead")] #[must_use] pub fn is_negative(&self) -> bool { self.is_sign_negative() } /// Returns `true` if the decimal is positive. #[deprecated(since = "0.6.3", note = "please use `is_sign_positive` instead")] #[must_use] pub fn is_positive(&self) -> bool { self.is_sign_positive() } /// Returns `true` if the sign bit of the decimal is negative. #[inline(always)] pub const fn is_sign_negative(&self) -> bool { self.flags & SIGN_MASK > 0 } /// Returns `true` if the sign bit of the decimal is positive. #[inline(always)] #[must_use] pub const fn is_sign_positive(&self) -> bool { self.flags & SIGN_MASK == 0 } /// Returns the minimum possible number that `Decimal` can represent. #[deprecated(since = "1.12.0", note = "Use the associated constant Decimal::MIN")] #[must_use] pub const fn min_value() -> Decimal { MIN } /// Returns the maximum possible number that `Decimal` can represent. #[deprecated(since = "1.12.0", note = "Use the associated constant Decimal::MAX")] #[must_use] pub const fn max_value() -> Decimal { MAX } /// Returns a new `Decimal` integral with no fractional portion. /// This is a true truncation whereby no rounding is performed. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let pi = Decimal::new(3141, 3); /// let trunc = Decimal::new(3, 0); /// // note that it returns a decimal /// assert_eq!(pi.trunc(), trunc); /// ``` #[must_use] pub fn trunc(&self) -> Decimal { let mut scale = self.scale(); if scale == 0 { // Nothing to do return *self; } let mut working = [self.lo, self.mid, self.hi]; while scale > 0 { // We're removing precision, so we don't care about overflow if scale < 10 { ops::array::div_by_u32(&mut working, POWERS_10[scale as usize]); break; } else { ops::array::div_by_u32(&mut working, POWERS_10[9]); // Only 9 as this array starts with 1 scale -= 9; } } Decimal { lo: working[0], mid: working[1], hi: working[2], flags: flags(self.is_sign_negative(), 0), } } /// Returns a new `Decimal` representing the fractional portion of the number. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let pi = Decimal::new(3141, 3); /// let fract = Decimal::new(141, 3); /// // note that it returns a decimal /// assert_eq!(pi.fract(), fract); /// ``` #[must_use] pub fn fract(&self) -> Decimal { // This is essentially the original number minus the integral. // Could possibly be optimized in the future *self - self.trunc() } /// Computes the absolute value of `self`. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let num = Decimal::new(-3141, 3); /// assert_eq!(num.abs().to_string(), "3.141"); /// ``` #[must_use] pub fn abs(&self) -> Decimal { let mut me = *self; me.set_sign_positive(true); me } /// Returns the largest integer less than or equal to a number. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let num = Decimal::new(3641, 3); /// assert_eq!(num.floor().to_string(), "3"); /// ``` #[must_use] pub fn floor(&self) -> Decimal { let scale = self.scale(); if scale == 0 { // Nothing to do return *self; } // Opportunity for optimization here let floored = self.trunc(); if self.is_sign_negative() && !self.fract().is_zero() { floored - ONE } else { floored } } /// Returns the smallest integer greater than or equal to a number. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// let num = Decimal::new(3141, 3); /// assert_eq!(num.ceil().to_string(), "4"); /// let num = Decimal::new(3, 0); /// assert_eq!(num.ceil().to_string(), "3"); /// ``` #[must_use] pub fn ceil(&self) -> Decimal { let scale = self.scale(); if scale == 0 { // Nothing to do return *self; } // Opportunity for optimization here if self.is_sign_positive() && !self.fract().is_zero() { self.trunc() + ONE } else { self.trunc() } } /// Returns the maximum of the two numbers. /// /// ``` /// use rust_decimal::Decimal; /// /// let x = Decimal::new(1, 0); /// let y = Decimal::new(2, 0); /// assert_eq!(y, x.max(y)); /// ``` #[must_use] pub fn max(self, other: Decimal) -> Decimal { if self < other { other } else { self } } /// Returns the minimum of the two numbers. /// /// ``` /// use rust_decimal::Decimal; /// /// let x = Decimal::new(1, 0); /// let y = Decimal::new(2, 0); /// assert_eq!(x, x.min(y)); /// ``` #[must_use] pub fn min(self, other: Decimal) -> Decimal { if self > other { other } else { self } } /// Strips any trailing zero's from a `Decimal` and converts -0 to 0. /// /// # Example /// /// ``` /// use rust_decimal::prelude::*; /// /// let number = Decimal::from_str("3.100").unwrap(); /// assert_eq!(number.normalize().to_string(), "3.1"); /// ``` #[must_use] pub fn normalize(&self) -> Decimal { let mut result = *self; result.normalize_assign(); result } /// An in place version of `normalize`. Strips any trailing zero's from a `Decimal` and converts -0 to 0. /// /// # Example /// /// ``` /// use rust_decimal::prelude::*; /// /// let mut number = Decimal::from_str("3.100").unwrap(); /// assert_eq!(number.to_string(), "3.100"); /// number.normalize_assign(); /// assert_eq!(number.to_string(), "3.1"); /// ``` pub fn normalize_assign(&mut self) { if self.is_zero() { self.flags = 0; return; } let mut scale = self.scale(); if scale == 0 { return; } let mut result = self.mantissa_array3(); let mut working = self.mantissa_array3(); while scale > 0 { if ops::array::div_by_u32(&mut working, 10) > 0 { break; } scale -= 1; result.copy_from_slice(&working); } self.lo = result[0]; self.mid = result[1]; self.hi = result[2]; self.flags = flags(self.is_sign_negative(), scale); } /// Returns a new `Decimal` number with no fractional portion (i.e. an integer). /// Rounding currently follows "Bankers Rounding" rules. e.g. 6.5 -> 6, 7.5 -> 8 /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// /// // Demonstrating bankers rounding... /// let number_down = Decimal::new(65, 1); /// let number_up = Decimal::new(75, 1); /// assert_eq!(number_down.round().to_string(), "6"); /// assert_eq!(number_up.round().to_string(), "8"); /// ``` #[must_use] pub fn round(&self) -> Decimal { self.round_dp(0) } /// Returns a new `Decimal` number with the specified number of decimal points for fractional /// portion. /// Rounding is performed using the provided [`RoundingStrategy`] /// /// # Arguments /// * `dp`: the number of decimal points to round to. /// * `strategy`: the [`RoundingStrategy`] to use. /// /// # Example /// /// ``` /// use rust_decimal::{Decimal, RoundingStrategy}; /// use core::str::FromStr; /// /// let tax = Decimal::from_str("3.4395").unwrap(); /// assert_eq!(tax.round_dp_with_strategy(2, RoundingStrategy::MidpointAwayFromZero).to_string(), "3.44"); /// ``` #[must_use] pub fn round_dp_with_strategy(&self, dp: u32, strategy: RoundingStrategy) -> Decimal { // Short circuit for zero if self.is_zero() { return Decimal { lo: 0, mid: 0, hi: 0, flags: flags(self.is_sign_negative(), dp), }; } let old_scale = self.scale(); // return early if decimal has a smaller number of fractional places than dp // e.g. 2.51 rounded to 3 decimal places is 2.51 if old_scale <= dp { return *self; } let mut value = [self.lo, self.mid, self.hi]; let mut value_scale = self.scale(); let negative = self.is_sign_negative(); value_scale -= dp; // Rescale to zero so it's easier to work with while value_scale > 0 { if value_scale < 10 { ops::array::div_by_u32(&mut value, POWERS_10[value_scale as usize]); value_scale = 0; } else { ops::array::div_by_u32(&mut value, POWERS_10[9]); value_scale -= 9; } } // Do some midpoint rounding checks // We're actually doing two things here. // 1. Figuring out midpoint rounding when we're right on the boundary. e.g. 2.50000 // 2. Figuring out whether to add one or not e.g. 2.51 // For this, we need to figure out the fractional portion that is additional to // the rounded number. e.g. for 0.12345 rounding to 2dp we'd want 345. // We're doing the equivalent of losing precision (e.g. to get 0.12) // then increasing the precision back up to 0.12000 let mut offset = [self.lo, self.mid, self.hi]; let mut diff = old_scale - dp; while diff > 0 { if diff < 10 { ops::array::div_by_u32(&mut offset, POWERS_10[diff as usize]); break; } else { ops::array::div_by_u32(&mut offset, POWERS_10[9]); // Only 9 as this array starts with 1 diff -= 9; } } let mut diff = old_scale - dp; while diff > 0 { if diff < 10 { ops::array::mul_by_u32(&mut offset, POWERS_10[diff as usize]); break; } else { ops::array::mul_by_u32(&mut offset, POWERS_10[9]); // Only 9 as this array starts with 1 diff -= 9; } } let mut decimal_portion = [self.lo, self.mid, self.hi]; ops::array::sub_by_internal(&mut decimal_portion, &offset); // If the decimal_portion is zero then we round based on the other data let mut cap = [5, 0, 0]; for _ in 0..(old_scale - dp - 1) { ops::array::mul_by_u32(&mut cap, 10); } let order = ops::array::cmp_internal(&decimal_portion, &cap); #[allow(deprecated)] match strategy { RoundingStrategy::BankersRounding | RoundingStrategy::MidpointNearestEven => { match order { Ordering::Equal => { if (value[0] & 1) == 1 { ops::array::add_one_internal(&mut value); } } Ordering::Greater => { // Doesn't matter about the decimal portion ops::array::add_one_internal(&mut value); } _ => {} } } RoundingStrategy::RoundHalfDown | RoundingStrategy::MidpointTowardZero => { if let Ordering::Greater = order { ops::array::add_one_internal(&mut value); } } RoundingStrategy::RoundHalfUp | RoundingStrategy::MidpointAwayFromZero => { // when Ordering::Equal, decimal_portion is 0.5 exactly // when Ordering::Greater, decimal_portion is > 0.5 match order { Ordering::Equal => { ops::array::add_one_internal(&mut value); } Ordering::Greater => { // Doesn't matter about the decimal portion ops::array::add_one_internal(&mut value); } _ => {} } } RoundingStrategy::RoundUp | RoundingStrategy::AwayFromZero => { if !ops::array::is_all_zero(&decimal_portion) { ops::array::add_one_internal(&mut value); } } RoundingStrategy::ToPositiveInfinity => { if !negative && !ops::array::is_all_zero(&decimal_portion) { ops::array::add_one_internal(&mut value); } } RoundingStrategy::ToNegativeInfinity => { if negative && !ops::array::is_all_zero(&decimal_portion) { ops::array::add_one_internal(&mut value); } } RoundingStrategy::RoundDown | RoundingStrategy::ToZero => (), } Decimal::from_parts(value[0], value[1], value[2], negative, dp) } /// Returns a new `Decimal` number with the specified number of decimal points for fractional portion. /// Rounding currently follows "Bankers Rounding" rules. e.g. 6.5 -> 6, 7.5 -> 8 /// /// # Arguments /// * `dp`: the number of decimal points to round to. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// use core::str::FromStr; /// /// let pi = Decimal::from_str("3.1415926535897932384626433832").unwrap(); /// assert_eq!(pi.round_dp(2).to_string(), "3.14"); /// ``` #[must_use] pub fn round_dp(&self, dp: u32) -> Decimal { self.round_dp_with_strategy(dp, RoundingStrategy::MidpointNearestEven) } /// Returns `Some(Decimal)` number rounded to the specified number of significant digits. If /// the resulting number is unable to be represented by the `Decimal` number then `None` will /// be returned. /// When the number of significant figures of the `Decimal` being rounded is greater than the requested /// number of significant digits then rounding will be performed using `MidpointNearestEven` strategy. /// /// # Arguments /// * `digits`: the number of significant digits to round to. /// /// # Remarks /// A significant figure is determined using the following rules: /// 1. Non-zero digits are always significant. /// 2. Zeros between non-zero digits are always significant. /// 3. Leading zeros are never significant. /// 4. Trailing zeros are only significant if the number contains a decimal point. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// use core::str::FromStr; /// /// let value = Decimal::from_str("305.459").unwrap(); /// assert_eq!(value.round_sf(0), Some(Decimal::from_str("0").unwrap())); /// assert_eq!(value.round_sf(1), Some(Decimal::from_str("300").unwrap())); /// assert_eq!(value.round_sf(2), Some(Decimal::from_str("310").unwrap())); /// assert_eq!(value.round_sf(3), Some(Decimal::from_str("305").unwrap())); /// assert_eq!(value.round_sf(4), Some(Decimal::from_str("305.5").unwrap())); /// assert_eq!(value.round_sf(5), Some(Decimal::from_str("305.46").unwrap())); /// assert_eq!(value.round_sf(6), Some(Decimal::from_str("305.459").unwrap())); /// assert_eq!(value.round_sf(7), Some(Decimal::from_str("305.4590").unwrap())); /// assert_eq!(Decimal::MAX.round_sf(1), None); /// /// let value = Decimal::from_str("0.012301").unwrap(); /// assert_eq!(value.round_sf(3), Some(Decimal::from_str("0.0123").unwrap())); /// ``` #[must_use] pub fn round_sf(&self, digits: u32) -> Option { self.round_sf_with_strategy(digits, RoundingStrategy::MidpointNearestEven) } /// Returns `Some(Decimal)` number rounded to the specified number of significant digits. If /// the resulting number is unable to be represented by the `Decimal` number then `None` will /// be returned. /// When the number of significant figures of the `Decimal` being rounded is greater than the requested /// number of significant digits then rounding will be performed using the provided [RoundingStrategy]. /// /// # Arguments /// * `digits`: the number of significant digits to round to. /// * `strategy`: if required, the rounding strategy to use. /// /// # Remarks /// A significant figure is determined using the following rules: /// 1. Non-zero digits are always significant. /// 2. Zeros between non-zero digits are always significant. /// 3. Leading zeros are never significant. /// 4. Trailing zeros are only significant if the number contains a decimal point. /// /// # Example /// /// ``` /// use rust_decimal::{Decimal, RoundingStrategy}; /// use core::str::FromStr; /// /// let value = Decimal::from_str("305.459").unwrap(); /// assert_eq!(value.round_sf_with_strategy(0, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("0").unwrap()); /// assert_eq!(value.round_sf_with_strategy(1, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("300").unwrap()); /// assert_eq!(value.round_sf_with_strategy(2, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("300").unwrap()); /// assert_eq!(value.round_sf_with_strategy(3, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("305").unwrap()); /// assert_eq!(value.round_sf_with_strategy(4, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("305.4").unwrap()); /// assert_eq!(value.round_sf_with_strategy(5, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("305.45").unwrap()); /// assert_eq!(value.round_sf_with_strategy(6, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("305.459").unwrap()); /// assert_eq!(value.round_sf_with_strategy(7, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("305.4590").unwrap()); /// assert_eq!(Decimal::MAX.round_sf_with_strategy(1, RoundingStrategy::ToZero).unwrap(), Decimal::from_str("70000000000000000000000000000").unwrap()); /// /// let value = Decimal::from_str("0.012301").unwrap(); /// assert_eq!(value.round_sf_with_strategy(3, RoundingStrategy::AwayFromZero), Some(Decimal::from_str("0.0124").unwrap())); /// ``` #[must_use] pub fn round_sf_with_strategy(&self, digits: u32, strategy: RoundingStrategy) -> Option { if self.is_zero() || digits == 0 { return Some(Decimal::ZERO); } // We start by grabbing the mantissa and figuring out how many significant figures it is // made up of. We do this by just dividing by 10 and checking remainders - effectively // we're performing a naive log10. let mut working = self.mantissa_array3(); let mut mantissa_sf = 0; while !ops::array::is_all_zero(&working) { let _remainder = ops::array::div_by_u32(&mut working, 10u32); mantissa_sf += 1; if working[2] == 0 && working[1] == 0 && working[0] == 1 { mantissa_sf += 1; break; } } let scale = self.scale(); match digits.cmp(&mantissa_sf) { Ordering::Greater => { // If we're requesting a higher number of significant figures, we rescale let mut array = [self.lo, self.mid, self.hi]; let mut value_scale = scale; ops::array::rescale_internal(&mut array, &mut value_scale, scale + digits - mantissa_sf); Some(Decimal { lo: array[0], mid: array[1], hi: array[2], flags: flags(self.is_sign_negative(), value_scale), }) } Ordering::Less => { // We're requesting a lower number of significant digits. let diff = mantissa_sf - digits; // If the diff is greater than the scale we're focused on the integral. Otherwise, we can // just round. if diff > scale { use crate::constants::BIG_POWERS_10; // We need to adjust the integral portion. This also should be rounded, consequently // we reduce the number down, round it, and then scale back up. // E.g. If we have 305.459 scaling to a sf of 2 - we first reduce the number // down to 30.5459, round it to 31 and then scale it back up to 310. // Likewise, if we have 12301 scaling to a sf of 3 - we first reduce the number // down to 123.01, round it to 123 and then scale it back up to 12300. let mut num = *self; let mut exp = (diff - scale) as usize; while exp > 0 { let pow; if exp >= BIG_POWERS_10.len() { pow = Decimal::from(BIG_POWERS_10[BIG_POWERS_10.len() - 1]); exp -= BIG_POWERS_10.len(); } else { pow = Decimal::from(BIG_POWERS_10[exp - 1]); exp = 0; } num = num.checked_div(pow)?; } let mut num = num.round_dp_with_strategy(0, strategy).trunc(); let mut exp = (mantissa_sf - digits - scale) as usize; while exp > 0 { let pow; if exp >= BIG_POWERS_10.len() { pow = Decimal::from(BIG_POWERS_10[BIG_POWERS_10.len() - 1]); exp -= BIG_POWERS_10.len(); } else { pow = Decimal::from(BIG_POWERS_10[exp - 1]); exp = 0; } num = num.checked_mul(pow)?; } Some(num) } else { Some(self.round_dp_with_strategy(scale - diff, strategy)) } } Ordering::Equal => { // Case where significant figures = requested significant digits. Some(*self) } } } /// Convert `Decimal` to an internal representation of the underlying struct. This is useful /// for debugging the internal state of the object. /// /// # Important Disclaimer /// This is primarily intended for library maintainers. The internal representation of a /// `Decimal` is considered "unstable" for public use. /// /// # Example /// /// ``` /// use rust_decimal::Decimal; /// use core::str::FromStr; /// /// let pi = Decimal::from_str("3.1415926535897932384626433832").unwrap(); /// assert_eq!(format!("{:?}", pi), "3.1415926535897932384626433832"); /// assert_eq!(format!("{:?}", pi.unpack()), "UnpackedDecimal { \ /// negative: false, scale: 28, hi: 1703060790, mid: 185874565, lo: 1102470952 \ /// }"); /// ``` pub const fn unpack(&self) -> UnpackedDecimal { UnpackedDecimal { negative: self.is_sign_negative(), scale: self.scale(), hi: self.hi, lo: self.lo, mid: self.mid, } } #[inline(always)] pub(crate) const fn lo(&self) -> u32 { self.lo } #[inline(always)] pub(crate) const fn mid(&self) -> u32 { self.mid } #[inline(always)] pub(crate) const fn hi(&self) -> u32 { self.hi } #[inline(always)] pub(crate) const fn flags(&self) -> u32 { self.flags } #[inline(always)] pub(crate) const fn mantissa_array3(&self) -> [u32; 3] { [self.lo, self.mid, self.hi] } #[inline(always)] pub(crate) const fn mantissa_array4(&self) -> [u32; 4] { [self.lo, self.mid, self.hi, 0] } /// Parses a 32-bit float into a Decimal number whilst retaining any non-guaranteed precision. /// /// Typically when a float is parsed in Rust Decimal, any excess bits (after ~7.22 decimal points for /// f32 as per IEEE-754) are removed due to any digits following this are considered an approximation /// at best. This function bypasses this additional step and retains these excess bits. /// /// # Example /// /// ``` /// use rust_decimal::prelude::*; /// /// // Usually floats are parsed leveraging float guarantees. i.e. 0.1_f32 => 0.1 /// assert_eq!("0.1", Decimal::from_f32(0.1_f32).unwrap().to_string()); /// /// // Sometimes, we may want to represent the approximation exactly. /// assert_eq!("0.100000001490116119384765625", Decimal::from_f32_retain(0.1_f32).unwrap().to_string()); /// ``` pub fn from_f32_retain(n: f32) -> Option { from_f32(n, false) } /// Parses a 64-bit float into a Decimal number whilst retaining any non-guaranteed precision. /// /// Typically when a float is parsed in Rust Decimal, any excess bits (after ~15.95 decimal points for /// f64 as per IEEE-754) are removed due to any digits following this are considered an approximation /// at best. This function bypasses this additional step and retains these excess bits. /// /// # Example /// /// ``` /// use rust_decimal::prelude::*; /// /// // Usually floats are parsed leveraging float guarantees. i.e. 0.1_f64 => 0.1 /// assert_eq!("0.1", Decimal::from_f64(0.1_f64).unwrap().to_string()); /// /// // Sometimes, we may want to represent the approximation exactly. /// assert_eq!("0.1000000000000000055511151231", Decimal::from_f64_retain(0.1_f64).unwrap().to_string()); /// ``` pub fn from_f64_retain(n: f64) -> Option { from_f64(n, false) } /// Checked addition. Computes `self + other`, returning `None` if overflow occurred. #[inline(always)] #[must_use] pub fn checked_add(self, other: Decimal) -> Option { match ops::add_impl(&self, &other) { CalculationResult::Ok(result) => Some(result), CalculationResult::Overflow => None, _ => None, } } /// Checked subtraction. Computes `self - other`, returning `None` if overflow occurred. #[inline(always)] #[must_use] pub fn checked_sub(self, other: Decimal) -> Option { match ops::sub_impl(&self, &other) { CalculationResult::Ok(result) => Some(result), CalculationResult::Overflow => None, _ => None, } } /// Checked multiplication. Computes `self * other`, returning `None` if overflow occurred. #[inline] #[must_use] pub fn checked_mul(self, other: Decimal) -> Option { match ops::mul_impl(&self, &other) { CalculationResult::Ok(result) => Some(result), CalculationResult::Overflow => None, _ => None, } } /// Checked division. Computes `self / other`, returning `None` if `other == 0.0` or the /// division results in overflow. #[inline] #[must_use] pub fn checked_div(self, other: Decimal) -> Option { match ops::div_impl(&self, &other) { CalculationResult::Ok(quot) => Some(quot), CalculationResult::Overflow => None, CalculationResult::DivByZero => None, } } /// Checked remainder. Computes `self % other`, returning `None` if `other == 0.0`. #[inline] #[must_use] pub fn checked_rem(self, other: Decimal) -> Option { match ops::rem_impl(&self, &other) { CalculationResult::Ok(quot) => Some(quot), CalculationResult::Overflow => None, CalculationResult::DivByZero => None, } } pub fn from_str_radix(str: &str, radix: u32) -> Result { if radix == 10 { crate::str::parse_str_radix_10(str) } else { crate::str::parse_str_radix_n(str, radix) } } } impl Default for Decimal { fn default() -> Self { ZERO } } pub(crate) enum CalculationResult { Ok(Decimal), Overflow, DivByZero, } #[inline] const fn flags(neg: bool, scale: u32) -> u32 { (scale << SCALE_SHIFT) | ((neg as u32) << SIGN_SHIFT) } macro_rules! impl_from { ($T:ty, $from_ty:path) => { impl core::convert::From<$T> for Decimal { #[inline] fn from(t: $T) -> Self { $from_ty(t).unwrap() } } }; } impl_from!(isize, FromPrimitive::from_isize); impl_from!(i8, FromPrimitive::from_i8); impl_from!(i16, FromPrimitive::from_i16); impl_from!(i32, FromPrimitive::from_i32); impl_from!(i64, FromPrimitive::from_i64); impl_from!(usize, FromPrimitive::from_usize); impl_from!(u8, FromPrimitive::from_u8); impl_from!(u16, FromPrimitive::from_u16); impl_from!(u32, FromPrimitive::from_u32); impl_from!(u64, FromPrimitive::from_u64); impl_from!(i128, FromPrimitive::from_i128); impl_from!(u128, FromPrimitive::from_u128); 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 { (&self).$method(&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 { self.$method(&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 { (&self).$method(other) } } }; } macro_rules! forward_all_binop { (impl $imp:ident for $res:ty, $method:ident) => { forward_val_val_binop!(impl $imp for $res, $method); forward_ref_val_binop!(impl $imp for $res, $method); forward_val_ref_binop!(impl $imp for $res, $method); }; } impl Zero for Decimal { fn zero() -> Decimal { ZERO } fn is_zero(&self) -> bool { self.is_zero() } } impl One for Decimal { fn one() -> Decimal { ONE } } impl Signed for Decimal { fn abs(&self) -> Self { self.abs() } fn abs_sub(&self, other: &Self) -> Self { if self <= other { ZERO } else { self.abs() } } fn signum(&self) -> Self { if self.is_zero() { ZERO } else { let mut value = ONE; if self.is_sign_negative() { value.set_sign_negative(true); } value } } fn is_positive(&self) -> bool { self.is_sign_positive() } fn is_negative(&self) -> bool { self.is_sign_negative() } } impl CheckedAdd for Decimal { #[inline] fn checked_add(&self, v: &Decimal) -> Option { Decimal::checked_add(*self, *v) } } impl CheckedSub for Decimal { #[inline] fn checked_sub(&self, v: &Decimal) -> Option { Decimal::checked_sub(*self, *v) } } impl CheckedMul for Decimal { #[inline] fn checked_mul(&self, v: &Decimal) -> Option { Decimal::checked_mul(*self, *v) } } impl CheckedDiv for Decimal { #[inline] fn checked_div(&self, v: &Decimal) -> Option { Decimal::checked_div(*self, *v) } } impl CheckedRem for Decimal { #[inline] fn checked_rem(&self, v: &Decimal) -> Option { Decimal::checked_rem(*self, *v) } } impl Num for Decimal { type FromStrRadixErr = Error; fn from_str_radix(str: &str, radix: u32) -> Result { Decimal::from_str_radix(str, radix) } } impl FromStr for Decimal { type Err = Error; fn from_str(value: &str) -> Result { crate::str::parse_str_radix_10(value) } } impl FromPrimitive for Decimal { fn from_i32(n: i32) -> Option { let flags: u32; let value_copy: i64; if n >= 0 { flags = 0; value_copy = n as i64; } else { flags = SIGN_MASK; value_copy = -(n as i64); } Some(Decimal { flags, lo: value_copy as u32, mid: 0, hi: 0, }) } fn from_i64(n: i64) -> Option { let flags: u32; let value_copy: i128; if n >= 0 { flags = 0; value_copy = n as i128; } else { flags = SIGN_MASK; value_copy = -(n as i128); } Some(Decimal { flags, lo: value_copy as u32, mid: (value_copy >> 32) as u32, hi: 0, }) } fn from_i128(n: i128) -> Option { let flags; let unsigned; if n >= 0 { unsigned = n as u128; flags = 0; } else { unsigned = -n as u128; flags = SIGN_MASK; }; // Check if we overflow if unsigned >> 96 != 0 { return None; } Some(Decimal { flags, lo: unsigned as u32, mid: (unsigned >> 32) as u32, hi: (unsigned >> 64) as u32, }) } fn from_u32(n: u32) -> Option { Some(Decimal { flags: 0, lo: n, mid: 0, hi: 0, }) } fn from_u64(n: u64) -> Option { Some(Decimal { flags: 0, lo: n as u32, mid: (n >> 32) as u32, hi: 0, }) } fn from_u128(n: u128) -> Option { // Check if we overflow if n >> 96 != 0 { return None; } Some(Decimal { flags: 0, lo: n as u32, mid: (n >> 32) as u32, hi: (n >> 64) as u32, }) } fn from_f32(n: f32) -> Option { // By default, we remove excess bits. This allows 0.1_f64 == dec!(0.1). from_f32(n, true) } fn from_f64(n: f64) -> Option { // By default, we remove excess bits. This allows 0.1_f64 == dec!(0.1). from_f64(n, true) } } #[inline] fn from_f64(n: f64, remove_excess_bits: bool) -> Option { // Handle the case if it is NaN, Infinity or -Infinity if !n.is_finite() { return None; } // It's a shame we can't use a union for this due to it being broken up by bits // i.e. 1/11/52 (sign, exponent, mantissa) // See https://en.wikipedia.org/wiki/IEEE_754-1985 // n = (sign*-1) * 2^exp * mantissa // Decimal of course stores this differently... 10^-exp * significand let raw = n.to_bits(); let positive = (raw >> 63) == 0; let biased_exponent = ((raw >> 52) & 0x7FF) as i32; let mantissa = raw & 0x000F_FFFF_FFFF_FFFF; // Handle the special zero case if biased_exponent == 0 && mantissa == 0 { let mut zero = ZERO; if !positive { zero.set_sign_negative(true); } return Some(zero); } // Get the bits and exponent2 let mut exponent2 = biased_exponent - 1023; let mut bits = [ (mantissa & 0xFFFF_FFFF) as u32, ((mantissa >> 32) & 0xFFFF_FFFF) as u32, 0u32, ]; if biased_exponent == 0 { // Denormalized number - correct the exponent exponent2 += 1; } else { // Add extra hidden bit to mantissa bits[1] |= 0x0010_0000; } // The act of copying a mantissa as integer bits is equivalent to shifting // left the mantissa 52 bits. The exponent is reduced to compensate. exponent2 -= 52; // Convert to decimal base2_to_decimal(&mut bits, exponent2, positive, true, remove_excess_bits) } #[inline] fn from_f32(n: f32, remove_excess_bits: bool) -> Option { // Handle the case if it is NaN, Infinity or -Infinity if !n.is_finite() { return None; } // It's a shame we can't use a union for this due to it being broken up by bits // i.e. 1/8/23 (sign, exponent, mantissa) // See https://en.wikipedia.org/wiki/IEEE_754-1985 // n = (sign*-1) * 2^exp * mantissa // Decimal of course stores this differently... 10^-exp * significand let raw = n.to_bits(); let positive = (raw >> 31) == 0; let biased_exponent = ((raw >> 23) & 0xFF) as i32; let mantissa = raw & 0x007F_FFFF; // Handle the special zero case if biased_exponent == 0 && mantissa == 0 { let mut zero = ZERO; if !positive { zero.set_sign_negative(true); } return Some(zero); } // Get the bits and exponent2 let mut exponent2 = biased_exponent - 127; let mut bits = [mantissa, 0u32, 0u32]; if biased_exponent == 0 { // Denormalized number - correct the exponent exponent2 += 1; } else { // Add extra hidden bit to mantissa bits[0] |= 0x0080_0000; } // The act of copying a mantissa as integer bits is equivalent to shifting // left the mantissa 23 bits. The exponent is reduced to compensate. exponent2 -= 23; // Convert to decimal base2_to_decimal(&mut bits, exponent2, positive, false, remove_excess_bits) } fn base2_to_decimal( bits: &mut [u32; 3], exponent2: i32, positive: bool, is64: bool, remove_excess_bits: bool, ) -> Option { // 2^exponent2 = (10^exponent2)/(5^exponent2) // = (5^-exponent2)*(10^exponent2) let mut exponent5 = -exponent2; let mut exponent10 = exponent2; // Ultimately, we want this for the scale while exponent5 > 0 { // Check to see if the mantissa is divisible by 2 if bits[0] & 0x1 == 0 { exponent10 += 1; exponent5 -= 1; // We can divide by 2 without losing precision let hi_carry = bits[2] & 0x1 == 1; bits[2] >>= 1; let mid_carry = bits[1] & 0x1 == 1; bits[1] = (bits[1] >> 1) | if hi_carry { SIGN_MASK } else { 0 }; bits[0] = (bits[0] >> 1) | if mid_carry { SIGN_MASK } else { 0 }; } else { // The mantissa is NOT divisible by 2. Therefore the mantissa should // be multiplied by 5, unless the multiplication overflows. exponent5 -= 1; let mut temp = [bits[0], bits[1], bits[2]]; if ops::array::mul_by_u32(&mut temp, 5) == 0 { // Multiplication succeeded without overflow, so copy result back bits[0] = temp[0]; bits[1] = temp[1]; bits[2] = temp[2]; } else { // Multiplication by 5 overflows. The mantissa should be divided // by 2, and therefore will lose significant digits. exponent10 += 1; // Shift right let hi_carry = bits[2] & 0x1 == 1; bits[2] >>= 1; let mid_carry = bits[1] & 0x1 == 1; bits[1] = (bits[1] >> 1) | if hi_carry { SIGN_MASK } else { 0 }; bits[0] = (bits[0] >> 1) | if mid_carry { SIGN_MASK } else { 0 }; } } } // In order to divide the value by 5, it is best to multiply by 2/10. // Therefore, exponent10 is decremented, and the mantissa should be multiplied by 2 while exponent5 < 0 { if bits[2] & SIGN_MASK == 0 { // No far left bit, the mantissa can withstand a shift-left without overflowing exponent10 -= 1; exponent5 += 1; ops::array::shl1_internal(bits, 0); } else { // The mantissa would overflow if shifted. Therefore it should be // directly divided by 5. This will lose significant digits, unless // by chance the mantissa happens to be divisible by 5. exponent5 += 1; ops::array::div_by_u32(bits, 5); } } // At this point, the mantissa has assimilated the exponent5, but // exponent10 might not be suitable for assignment. exponent10 must be // in the range [-MAX_PRECISION..0], so the mantissa must be scaled up or // down appropriately. while exponent10 > 0 { // In order to bring exponent10 down to 0, the mantissa should be // multiplied by 10 to compensate. If the exponent10 is too big, this // will cause the mantissa to overflow. if ops::array::mul_by_u32(bits, 10) == 0 { exponent10 -= 1; } else { // Overflowed - return? return None; } } // In order to bring exponent up to -MAX_PRECISION, the mantissa should // be divided by 10 to compensate. If the exponent10 is too small, this // will cause the mantissa to underflow and become 0. while exponent10 < -(MAX_PRECISION_U32 as i32) { let rem10 = ops::array::div_by_u32(bits, 10); exponent10 += 1; if ops::array::is_all_zero(bits) { // Underflow, unable to keep dividing exponent10 = 0; } else if rem10 >= 5 { ops::array::add_one_internal(bits); } } if remove_excess_bits { // This step is required in order to remove excess bits of precision from the // end of the bit representation, down to the precision guaranteed by the // floating point number (see IEEE-754). if is64 { // Guaranteed to approx 15/16 dp while exponent10 < 0 && (bits[2] != 0 || (bits[1] & 0xFFF0_0000) != 0) { let rem10 = ops::array::div_by_u32(bits, 10); exponent10 += 1; if rem10 >= 5 { ops::array::add_one_internal(bits); } } } else { // Guaranteed to about 7/8 dp while exponent10 < 0 && ((bits[0] & 0xFF00_0000) != 0 || bits[1] != 0 || bits[2] != 0) { let rem10 = ops::array::div_by_u32(bits, 10); exponent10 += 1; if rem10 >= 5 { ops::array::add_one_internal(bits); } } } // Remove multiples of 10 from the representation while exponent10 < 0 { let mut temp = [bits[0], bits[1], bits[2]]; let remainder = ops::array::div_by_u32(&mut temp, 10); if remainder == 0 { exponent10 += 1; bits[0] = temp[0]; bits[1] = temp[1]; bits[2] = temp[2]; } else { break; } } } Some(Decimal { lo: bits[0], mid: bits[1], hi: bits[2], flags: flags(!positive, -exponent10 as u32), }) } impl ToPrimitive for Decimal { fn to_i64(&self) -> Option { let d = self.trunc(); // Quick overflow check if d.hi != 0 || (d.mid & 0x8000_0000) > 0 { // Overflow return None; } let raw: i64 = (i64::from(d.mid) << 32) | i64::from(d.lo); if self.is_sign_negative() { Some(-raw) } else { Some(raw) } } fn to_i128(&self) -> Option { let d = self.trunc(); let raw: i128 = ((i128::from(d.hi) << 64) | i128::from(d.mid) << 32) | i128::from(d.lo); if self.is_sign_negative() { Some(-raw) } else { Some(raw) } } fn to_u64(&self) -> Option { if self.is_sign_negative() { return None; } let d = self.trunc(); if d.hi != 0 { // Overflow return None; } Some((u64::from(d.mid) << 32) | u64::from(d.lo)) } fn to_u128(&self) -> Option { if self.is_sign_negative() { return None; } let d = self.trunc(); Some((u128::from(d.hi) << 64) | (u128::from(d.mid) << 32) | u128::from(d.lo)) } fn to_f64(&self) -> Option { if self.scale() == 0 { let integer = self.to_i64(); integer.map(|i| i as f64) } else { let sign: f64 = if self.is_sign_negative() { -1.0 } else { 1.0 }; let mut mantissa: u128 = self.lo.into(); mantissa |= (self.mid as u128) << 32; mantissa |= (self.hi as u128) << 64; // scale is at most 28, so this fits comfortably into a u128. let scale = self.scale(); let precision: u128 = 10_u128.pow(scale); let integral_part = mantissa / precision; let frac_part = mantissa % precision; let frac_f64 = (frac_part as f64) / (precision as f64); let value = sign * ((integral_part as f64) + frac_f64); let round_to = 10f64.powi(self.scale() as i32); Some((value * round_to).round() / round_to) } } } impl core::convert::TryFrom for Decimal { type Error = crate::Error; fn try_from(value: f32) -> Result { Self::from_f32(value).ok_or_else(|| Error::from("Failed to convert to Decimal")) } } impl core::convert::TryFrom for Decimal { type Error = crate::Error; fn try_from(value: f64) -> Result { Self::from_f64(value).ok_or_else(|| Error::from("Failed to convert to Decimal")) } } impl core::convert::TryFrom for f32 { type Error = crate::Error; fn try_from(value: Decimal) -> Result { Decimal::to_f32(&value).ok_or_else(|| Error::from("Failed to convert to f32")) } } impl core::convert::TryFrom for f64 { type Error = crate::Error; fn try_from(value: Decimal) -> Result { Decimal::to_f64(&value).ok_or_else(|| Error::from("Failed to convert to f64")) } } impl fmt::Display for Decimal { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { let (rep, additional) = crate::str::to_str_internal(self, false, f.precision()); if let Some(additional) = additional { let value = [rep.as_str(), "0".repeat(additional).as_str()].concat(); f.pad_integral(self.is_sign_positive(), "", value.as_str()) } else { f.pad_integral(self.is_sign_positive(), "", rep.as_str()) } } } impl fmt::Debug for Decimal { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } } impl fmt::LowerExp for Decimal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { crate::str::fmt_scientific_notation(self, "e", f) } } impl fmt::UpperExp for Decimal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { crate::str::fmt_scientific_notation(self, "E", f) } } impl Neg for Decimal { type Output = Decimal; fn neg(self) -> Decimal { let mut copy = self; copy.set_sign_negative(self.is_sign_positive()); copy } } impl<'a> Neg for &'a Decimal { type Output = Decimal; fn neg(self) -> Decimal { Decimal { flags: flags(!self.is_sign_negative(), self.scale()), hi: self.hi, lo: self.lo, mid: self.mid, } } } forward_all_binop!(impl Add for Decimal, add); impl<'a, 'b> Add<&'b Decimal> for &'a Decimal { type Output = Decimal; #[inline(always)] fn add(self, other: &Decimal) -> Decimal { match ops::add_impl(self, other) { CalculationResult::Ok(sum) => sum, _ => panic!("Addition overflowed"), } } } impl AddAssign for Decimal { fn add_assign(&mut self, other: Decimal) { let result = self.add(other); self.lo = result.lo; self.mid = result.mid; self.hi = result.hi; self.flags = result.flags; } } impl<'a> AddAssign<&'a Decimal> for Decimal { fn add_assign(&mut self, other: &'a Decimal) { Decimal::add_assign(self, *other) } } impl<'a> AddAssign for &'a mut Decimal { fn add_assign(&mut self, other: Decimal) { Decimal::add_assign(*self, other) } } impl<'a> AddAssign<&'a Decimal> for &'a mut Decimal { fn add_assign(&mut self, other: &'a Decimal) { Decimal::add_assign(*self, *other) } } forward_all_binop!(impl Sub for Decimal, sub); impl<'a, 'b> Sub<&'b Decimal> for &'a Decimal { type Output = Decimal; #[inline(always)] fn sub(self, other: &Decimal) -> Decimal { match ops::sub_impl(self, other) { CalculationResult::Ok(sum) => sum, _ => panic!("Subtraction overflowed"), } } } impl SubAssign for Decimal { fn sub_assign(&mut self, other: Decimal) { let result = self.sub(other); self.lo = result.lo; self.mid = result.mid; self.hi = result.hi; self.flags = result.flags; } } impl<'a> SubAssign<&'a Decimal> for Decimal { fn sub_assign(&mut self, other: &'a Decimal) { Decimal::sub_assign(self, *other) } } impl<'a> SubAssign for &'a mut Decimal { fn sub_assign(&mut self, other: Decimal) { Decimal::sub_assign(*self, other) } } impl<'a> SubAssign<&'a Decimal> for &'a mut Decimal { fn sub_assign(&mut self, other: &'a Decimal) { Decimal::sub_assign(*self, *other) } } forward_all_binop!(impl Mul for Decimal, mul); impl<'a, 'b> Mul<&'b Decimal> for &'a Decimal { type Output = Decimal; #[inline] fn mul(self, other: &Decimal) -> Decimal { match ops::mul_impl(self, other) { CalculationResult::Ok(prod) => prod, _ => panic!("Multiplication overflowed"), } } } impl MulAssign for Decimal { fn mul_assign(&mut self, other: Decimal) { let result = self.mul(other); self.lo = result.lo; self.mid = result.mid; self.hi = result.hi; self.flags = result.flags; } } impl<'a> MulAssign<&'a Decimal> for Decimal { fn mul_assign(&mut self, other: &'a Decimal) { Decimal::mul_assign(self, *other) } } impl<'a> MulAssign for &'a mut Decimal { fn mul_assign(&mut self, other: Decimal) { Decimal::mul_assign(*self, other) } } impl<'a> MulAssign<&'a Decimal> for &'a mut Decimal { fn mul_assign(&mut self, other: &'a Decimal) { Decimal::mul_assign(*self, *other) } } forward_all_binop!(impl Div for Decimal, div); impl<'a, 'b> Div<&'b Decimal> for &'a Decimal { type Output = Decimal; fn div(self, other: &Decimal) -> Decimal { match ops::div_impl(self, other) { CalculationResult::Ok(quot) => quot, CalculationResult::Overflow => panic!("Division overflowed"), CalculationResult::DivByZero => panic!("Division by zero"), } } } impl DivAssign for Decimal { fn div_assign(&mut self, other: Decimal) { let result = self.div(other); self.lo = result.lo; self.mid = result.mid; self.hi = result.hi; self.flags = result.flags; } } impl<'a> DivAssign<&'a Decimal> for Decimal { fn div_assign(&mut self, other: &'a Decimal) { Decimal::div_assign(self, *other) } } impl<'a> DivAssign for &'a mut Decimal { fn div_assign(&mut self, other: Decimal) { Decimal::div_assign(*self, other) } } impl<'a> DivAssign<&'a Decimal> for &'a mut Decimal { fn div_assign(&mut self, other: &'a Decimal) { Decimal::div_assign(*self, *other) } } forward_all_binop!(impl Rem for Decimal, rem); impl<'a, 'b> Rem<&'b Decimal> for &'a Decimal { type Output = Decimal; #[inline] fn rem(self, other: &Decimal) -> Decimal { match ops::rem_impl(self, other) { CalculationResult::Ok(rem) => rem, CalculationResult::Overflow => panic!("Division overflowed"), CalculationResult::DivByZero => panic!("Division by zero"), } } } impl RemAssign for Decimal { fn rem_assign(&mut self, other: Decimal) { let result = self.rem(other); self.lo = result.lo; self.mid = result.mid; self.hi = result.hi; self.flags = result.flags; } } impl<'a> RemAssign<&'a Decimal> for Decimal { fn rem_assign(&mut self, other: &'a Decimal) { Decimal::rem_assign(self, *other) } } impl<'a> RemAssign for &'a mut Decimal { fn rem_assign(&mut self, other: Decimal) { Decimal::rem_assign(*self, other) } } impl<'a> RemAssign<&'a Decimal> for &'a mut Decimal { fn rem_assign(&mut self, other: &'a Decimal) { Decimal::rem_assign(*self, *other) } } impl PartialEq for Decimal { #[inline] fn eq(&self, other: &Decimal) -> bool { self.cmp(other) == Equal } } impl Eq for Decimal {} impl Hash for Decimal { fn hash(&self, state: &mut H) { let n = self.normalize(); n.lo.hash(state); n.mid.hash(state); n.hi.hash(state); n.flags.hash(state); } } impl PartialOrd for Decimal { #[inline] fn partial_cmp(&self, other: &Decimal) -> Option { Some(self.cmp(other)) } } impl Ord for Decimal { fn cmp(&self, other: &Decimal) -> Ordering { ops::cmp_impl(self, other) } } impl Sum for Decimal { fn sum>(iter: I) -> Self { let mut sum = ZERO; for i in iter { sum += i; } sum } } impl<'a> Sum<&'a Decimal> for Decimal { fn sum>(iter: I) -> Self { let mut sum = ZERO; for i in iter { sum += i; } sum } } rust_decimal-1.17.0/src/error.rs000064400000000000000000000023540072674642500147070ustar 00000000000000use crate::constants::MAX_PRECISION_U32; use alloc::string::String; use core::fmt; /// Error type for the library. #[derive(Clone, Debug, PartialEq)] pub enum Error { ErrorString(String), ExceedsMaximumPossibleValue, LessThanMinimumPossibleValue, ScaleExceedsMaximumPrecision(u32), } impl From for Error where S: Into, { #[inline] fn from(from: S) -> Self { Self::ErrorString(from.into()) } } #[cfg(feature = "std")] impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Self::ErrorString(ref err) => f.pad(err), Self::ExceedsMaximumPossibleValue => { write!(f, "Number exceeds maximum value that can be represented.") } Self::LessThanMinimumPossibleValue => { write!(f, "Number less than minimum value that can be represented.") } Self::ScaleExceedsMaximumPrecision(ref scale) => { write!( f, "Scale exceeds the maximum precision allowed: {} > {}", scale, MAX_PRECISION_U32 ) } } } } rust_decimal-1.17.0/src/fuzz.rs000064400000000000000000000007230072674642500145520ustar 00000000000000use crate::Decimal; use arbitrary::{Arbitrary, Result as ArbitraryResult, Unstructured}; impl Arbitrary<'_> for crate::Decimal { fn arbitrary(u: &mut Unstructured<'_>) -> ArbitraryResult { let lo = u32::arbitrary(u)?; let mid = u32::arbitrary(u)?; let hi = u32::arbitrary(u)?; let negative = bool::arbitrary(u)?; let scale = u32::arbitrary(u)?; Ok(Decimal::from_parts(lo, mid, hi, negative, scale)) } } rust_decimal-1.17.0/src/lib.rs000064400000000000000000000161230072674642500143230ustar 00000000000000//! //! A Decimal implementation written in pure Rust suitable //! for financial calculations that require significant integral //! and fractional digits with no round-off errors. //! //! The binary representation consists of a 96 bit integer number, //! a scaling factor used to specify the decimal fraction and a 1 //! bit sign. Because of this representation, trailing zeros are //! preserved and may be exposed when in string form. These can be //! truncated using the `normalize` or `round_dp` functions. //! //! ## Getting started //! //! To get started, add `rust_decimal` and optionally `rust_decimal_macros` to your `Cargo.toml`: //! //! ```toml //! [dependencies] //! rust_decimal = "1.17" //! rust_decimal_macros = "1.17" //! ``` //! //! ## Usage //! //! Decimal numbers can be created in a few distinct ways. The easiest and most optimal //! method of creating a Decimal is to use the procedural macro within the //! `rust_decimal_macros` crate: //! //! ```ignore //! // Procedural macros need importing directly //! use rust_decimal_macros::dec; //! //! let number = dec!(-1.23); //! assert_eq!("-1.23", number.to_string()); //! ``` //! //! Alternatively you can also use one of the Decimal number convenience functions: //! //! ```rust //! // Using the prelude can help importing trait based functions (e.g. core::str::FromStr). //! use rust_decimal::prelude::*; //! //! // Using an integer followed by the decimal points //! let scaled = Decimal::new(202, 2); //! assert_eq!("2.02", scaled.to_string()); //! //! // From a string representation //! let from_string = Decimal::from_str("2.02").unwrap(); //! assert_eq!("2.02", from_string.to_string()); //! //! // From a string representation in a different base //! let from_string_base16 = Decimal::from_str_radix("ffff", 16).unwrap(); //! assert_eq!("65535", from_string_base16.to_string()); //! //! // Using the `Into` trait //! let my_int: Decimal = 3i32.into(); //! assert_eq!("3", my_int.to_string()); //! //! // Using the raw decimal representation //! let pi = Decimal::from_parts(1102470952, 185874565, 1703060790, false, 28); //! assert_eq!("3.1415926535897932384626433832", pi.to_string()); //! ``` //! //! Once you have instantiated your `Decimal` number you can perform calculations with it just like any other number: //! //! ```rust //! use rust_decimal::prelude::*; //! //! let amount = Decimal::from_str("25.12").unwrap(); //! let tax = Decimal::from_str("0.085").unwrap(); //! let total = amount + (amount * tax).round_dp(2); //! assert_eq!(total.to_string(), "27.26"); //! ``` //! //! ## Features //! //! * [c-repr](#c-repr) //! * [db-postgres](#db-postgres) //! * [db-tokio-postgres](#db-tokio-postgres) //! * [db-diesel-postgres](#db-diesel-postgres) //! * [legacy-ops](#legacy-ops) //! * [maths](#maths) //! * [rust-fuzz](#rust-fuzz) //! * [serde-float](#serde-float) //! * [serde-str](#serde-str) //! * [serde-arbitrary-precision](#serde-arbitrary-precision) //! * [std](#std) //! //! ### `c-repr` //! //! Forces `Decimal` to use `[repr(C)]`. The corresponding target layout is 128 bit aligned. //! //! ### `db-postgres` //! //! This feature enables a PostgreSQL communication module. It allows for reading and writing the `Decimal` //! type by transparently serializing/deserializing into the `NUMERIC` data type within PostgreSQL. //! //! ### `db-tokio-postgres` //! //! Enables the tokio postgres module allowing for async communication with PostgreSQL. //! //! ### `db-diesel-postgres` //! //! Enable `diesel` PostgreSQL support. //! //! ### `legacy-ops` //! //! As of `1.10` the algorithms used to perform basic operations have changed which has benefits of significant speed improvements. //! To maintain backwards compatibility this can be opted out of by enabling the `legacy-ops` feature. //! //! ### `maths` //! //! The `maths` feature enables additional complex mathematical functions such as `pow`, `ln`, `enf`, `exp` etc. //! Documentation detailing the additional functions can be found on the //! [`MathematicalOps`](https://docs.rs/rust_decimal/latest/rust_decimal/trait.MathematicalOps.html) trait. //! //! Please note that `ln` and `log10` will panic on invalid input with `checked_ln` and `checked_log10` the preferred functions //! to curb against this. When the `maths` feature was first developed the library would return `0` on invalid input. To re-enable this //! non-panicing behavior, please use the feature: `maths-nopanic`. //! //! ### `rust-fuzz` //! //! Enable `rust-fuzz` support by implementing the `Arbitrary` trait. //! //! ### `serde-float` //! //! Enable this so that JSON serialization of `Decimal` types are sent as a float instead of a string (default). //! //! e.g. with this turned on, JSON serialization would output: //! ```json //! { //! "value": 1.234 //! } //! ``` //! //! ### `serde-str` //! //! This is typically useful for `bincode` or `csv` like implementations. //! //! Since `bincode` does not specify type information, we need to ensure that a type hint is provided in order to //! correctly be able to deserialize. Enabling this feature on its own will force deserialization to use `deserialize_str` //! instead of `deserialize_any`. //! //! If, for some reason, you also have `serde-float` enabled then this will use `deserialize_f64` as a type hint. Because //! converting to `f64` _loses_ precision, it's highly recommended that you do NOT enable this feature when working with //! `bincode`. That being said, this will only use 8 bytes so is slightly more efficient in terms of storage size. //! //! ### `serde-arbitrary-precision` //! //! This is used primarily with `serde_json` and consequently adds it as a "weak dependency". This supports the //! `arbitrary_precision` feature inside `serde_json` when parsing decimals. //! //! This is recommended when parsing "float" looking data as it will prevent data loss. //! //! ### `std` //! //! Enable `std` library support. This is enabled by default, however in the future will be opt in. For now, to support `no_std` //! libraries, this crate can be compiled with `--no-default-features`. //! //! ## Building //! //! Please refer to the [Build document](BUILD.md) for more information on building and testing Rust Decimal. //! #![forbid(unsafe_code)] #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; mod constants; mod decimal; mod error; mod ops; mod str; #[cfg(any(feature = "postgres", feature = "diesel"))] mod db; #[cfg(feature = "rust-fuzz")] mod fuzz; #[cfg(feature = "maths")] mod maths; #[cfg(feature = "serde")] mod serde; pub use decimal::{Decimal, RoundingStrategy}; pub use error::Error; #[cfg(feature = "maths")] pub use maths::MathematicalOps; /// A convenience module appropriate for glob imports (`use rust_decimal::prelude::*;`). pub mod prelude { #[cfg(feature = "maths")] pub use crate::maths::MathematicalOps; pub use crate::{Decimal, RoundingStrategy}; pub use core::str::FromStr; pub use num_traits::{FromPrimitive, One, ToPrimitive, Zero}; } #[cfg(feature = "diesel")] #[macro_use] extern crate diesel; /// Shortcut for `core::result::Result`. Useful to distinguish /// between `rust_decimal` and `std` types. pub type Result = core::result::Result; rust_decimal-1.17.0/src/maths.rs000064400000000000000000000665400072674642500147010ustar 00000000000000use crate::prelude::*; use num_traits::pow::Pow; // Tolerance for inaccuracies when calculating exp const EXP_TOLERANCE: Decimal = Decimal::from_parts(2, 0, 0, false, 7); // Approximation of 1/ln(10) = 0.4342944819032518276511289189 const LN10_INVERSE: Decimal = Decimal::from_parts_raw(1763037029, 1670682625, 235431510, 1835008); // Total iterations of taylor series for Trig. const TRIG_SERIES_UPPER_BOUND: usize = 6; // PI / 8 const EIGHTH_PI: Decimal = Decimal::from_parts_raw(2822163429, 3244459792, 212882598, 1835008); // Table representing {index}! const FACTORIAL: [Decimal; 28] = [ Decimal::from_parts(1, 0, 0, false, 0), Decimal::from_parts(1, 0, 0, false, 0), Decimal::from_parts(2, 0, 0, false, 0), Decimal::from_parts(6, 0, 0, false, 0), Decimal::from_parts(24, 0, 0, false, 0), // 5! Decimal::from_parts(120, 0, 0, false, 0), Decimal::from_parts(720, 0, 0, false, 0), Decimal::from_parts(5040, 0, 0, false, 0), Decimal::from_parts(40320, 0, 0, false, 0), Decimal::from_parts(362880, 0, 0, false, 0), // 10! Decimal::from_parts(3628800, 0, 0, false, 0), Decimal::from_parts(39916800, 0, 0, false, 0), Decimal::from_parts(479001600, 0, 0, false, 0), Decimal::from_parts(1932053504, 1, 0, false, 0), Decimal::from_parts(1278945280, 20, 0, false, 0), // 15! Decimal::from_parts(2004310016, 304, 0, false, 0), Decimal::from_parts(2004189184, 4871, 0, false, 0), Decimal::from_parts(4006445056, 82814, 0, false, 0), Decimal::from_parts(3396534272, 1490668, 0, false, 0), Decimal::from_parts(109641728, 28322707, 0, false, 0), // 20! Decimal::from_parts(2192834560, 566454140, 0, false, 0), Decimal::from_parts(3099852800, 3305602358, 2, false, 0), Decimal::from_parts(3772252160, 4003775155, 60, false, 0), Decimal::from_parts(862453760, 1892515369, 1401, false, 0), Decimal::from_parts(3519021056, 2470695900, 33634, false, 0), // 25! Decimal::from_parts(2076180480, 1637855376, 840864, false, 0), Decimal::from_parts(2441084928, 3929534124, 21862473, false, 0), Decimal::from_parts(1484783616, 3018206259, 590286795, false, 0), ]; /// Trait exposing various mathematical operations that can be applied using a Decimal. This is only /// present when the `maths` feature has been enabled. pub trait MathematicalOps { /// The estimated exponential function, ex. Stops calculating when it is within /// tolerance of roughly `0.0000002`. fn exp(&self) -> Decimal; /// The estimated exponential function, ex. Stops calculating when it is within /// tolerance of roughly `0.0000002`. Returns `None` on overflow. fn checked_exp(&self) -> Option; /// The estimated exponential function, ex using the `tolerance` provided as a hint /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating /// sooner at the potential cost of a slightly less accurate result. fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal; /// The estimated exponential function, ex using the `tolerance` provided as a hint /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating /// sooner at the potential cost of a slightly less accurate result. /// Returns `None` on overflow. fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option; /// Raise self to the given integer exponent: xy fn powi(&self, exp: i64) -> Decimal; /// Raise self to the given integer exponent xy returning `None` on overflow. fn checked_powi(&self, exp: i64) -> Option; /// Raise self to the given unsigned integer exponent: xy fn powu(&self, exp: u64) -> Decimal; /// Raise self to the given unsigned integer exponent xy returning `None` on overflow. fn checked_powu(&self, exp: u64) -> Option; /// Raise self to the given floating point exponent: xy fn powf(&self, exp: f64) -> Decimal; /// Raise self to the given floating point exponent xy returning `None` on overflow. fn checked_powf(&self, exp: f64) -> Option; /// Raise self to the given Decimal exponent: xy. If `exp` is not whole then the approximation /// ey*ln(x) is used. fn powd(&self, exp: Decimal) -> Decimal; /// Raise self to the given Decimal exponent xy returning `None` on overflow. /// If `exp` is not whole then the approximation ey*ln(x) is used. fn checked_powd(&self, exp: Decimal) -> Option; /// The square root of a Decimal. Uses a standard Babylonian method. fn sqrt(&self) -> Option; /// Calculates the natural logarithm for a Decimal calculated using Taylor's series. fn ln(&self) -> Decimal; /// Calculates the checked natural logarithm for a Decimal calculated using Taylor's series. /// Returns `None` for negative numbers or zero. fn checked_ln(&self) -> Option; /// Calculates the base 10 logarithm of a specified Decimal number. fn log10(&self) -> Decimal; /// Calculates the checked base 10 logarithm of a specified Decimal number. /// Returns `None` for negative numbers or zero. fn checked_log10(&self) -> Option; /// Abramowitz Approximation of Error Function from [wikipedia](https://en.wikipedia.org/wiki/Error_function#Numerical_approximations) fn erf(&self) -> Decimal; /// The Cumulative distribution function for a Normal distribution fn norm_cdf(&self) -> Decimal; /// The Probability density function for a Normal distribution. fn norm_pdf(&self) -> Decimal; /// The Probability density function for a Normal distribution returning `None` on overflow. fn checked_norm_pdf(&self) -> Option; /// Computes the sine of a number (in radians). /// Panics upon overflow. fn sin(&self) -> Decimal; /// Computes the checked sine of a number (in radians). fn checked_sin(&self) -> Option; /// Computes the cosine of a number (in radians). /// Panics upon overflow. fn cos(&self) -> Decimal; /// Computes the checked cosine of a number (in radians). fn checked_cos(&self) -> Option; /// Computes the tangent of a number (in radians). /// Panics upon overflow or upon approaching a limit. fn tan(&self) -> Decimal; /// Computes the checked tangent of a number (in radians). /// Returns None on limit. fn checked_tan(&self) -> Option; } impl MathematicalOps for Decimal { fn exp(&self) -> Decimal { self.exp_with_tolerance(EXP_TOLERANCE) } fn checked_exp(&self) -> Option { self.checked_exp_with_tolerance(EXP_TOLERANCE) } fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal { match self.checked_exp_with_tolerance(tolerance) { Some(d) => d, None => { if self.is_sign_negative() { panic!("Exp underflowed") } else { panic!("Exp overflowed") } } } } fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option { if self.is_zero() { return Some(Decimal::ONE); } if self.is_sign_negative() { let mut flipped = *self; flipped.set_sign_positive(true); let exp = flipped.checked_exp_with_tolerance(tolerance)?; return Decimal::ONE.checked_div(exp); } let mut term = *self; let mut result = self.checked_add(Decimal::ONE)?; for factorial in FACTORIAL.iter().skip(2) { term = self.checked_mul(term)?; let next = result + (term / factorial); let diff = (next - result).abs(); result = next; if diff <= tolerance { break; } } Some(result) } fn powi(&self, exp: i64) -> Decimal { match self.checked_powi(exp) { Some(result) => result, None => panic!("Pow overflowed"), } } fn checked_powi(&self, exp: i64) -> Option { // For negative exponents we change x^-y into 1 / x^y. // Otherwise, we calculate a standard unsigned exponent if exp >= 0 { return self.checked_powu(exp as u64); } // Get the unsigned exponent let exp = exp.unsigned_abs(); let pow = match self.checked_powu(exp) { Some(v) => v, None => return None, }; Decimal::ONE.checked_div(pow) } fn powu(&self, exp: u64) -> Decimal { match self.checked_powu(exp) { Some(result) => result, None => panic!("Pow overflowed"), } } fn checked_powu(&self, exp: u64) -> Option { match exp { 0 => Some(Decimal::ONE), 1 => Some(*self), 2 => self.checked_mul(*self), _ => { // Get the squared value let squared = match self.checked_mul(*self) { Some(s) => s, None => return None, }; // Square self once and make an infinite sized iterator of the square. let iter = core::iter::repeat(squared); // We then take half of the exponent to create a finite iterator and then multiply those together. let mut product = Decimal::ONE; for x in iter.take((exp >> 1) as usize) { match product.checked_mul(x) { Some(r) => product = r, None => return None, }; } // If the exponent is odd we still need to multiply once more if exp & 0x1 > 0 { match self.checked_mul(product) { Some(p) => product = p, None => return None, } } product.normalize_assign(); Some(product) } } } fn powf(&self, exp: f64) -> Decimal { match self.checked_powf(exp) { Some(result) => result, None => panic!("Pow overflowed"), } } fn checked_powf(&self, exp: f64) -> Option { let exp = match Decimal::from_f64(exp) { Some(f) => f, None => return None, }; self.checked_powd(exp) } fn powd(&self, exp: Decimal) -> Decimal { match self.checked_powd(exp) { Some(result) => result, None => panic!("Pow overflowed"), } } fn checked_powd(&self, exp: Decimal) -> Option { if exp.is_zero() { return Some(Decimal::ONE); } if self.is_zero() { return Some(Decimal::ZERO); } if self.is_one() { return Some(Decimal::ONE); } if exp.is_one() { return Some(*self); } // If the scale is 0 then it's a trivial calculation let exp = exp.normalize(); if exp.scale() == 0 { if exp.mid() != 0 || exp.hi() != 0 { // Exponent way too big return None; } return if exp.is_sign_negative() { self.checked_powi(-(exp.lo() as i64)) } else { self.checked_powu(exp.lo() as u64) }; } // We do some approximations since we've got a decimal exponent. // For positive bases: a^b = exp(b*ln(a)) let negative = self.is_sign_negative(); let e = match self.abs().ln().checked_mul(exp) { Some(e) => e, None => return None, }; let mut result = e.checked_exp()?; result.set_sign_negative(negative); Some(result) } fn sqrt(&self) -> Option { if self.is_sign_negative() { return None; } if self.is_zero() { return Some(Decimal::ZERO); } // Start with an arbitrary number as the first guess let mut result = self / Decimal::TWO; // Too small to represent, so we start with self // Future iterations could actually avoid using a decimal altogether and use a buffered // vector, only combining back into a decimal on return if result.is_zero() { result = *self; } let mut last = result + Decimal::ONE; // Keep going while the difference is larger than the tolerance let mut circuit_breaker = 0; while last != result { circuit_breaker += 1; assert!(circuit_breaker < 1000, "geo mean circuit breaker"); last = result; result = (result + self / result) / Decimal::TWO; } Some(result) } #[cfg(feature = "maths-nopanic")] fn ln(&self) -> Decimal { match self.checked_ln() { Some(result) => result, None => Decimal::ZERO, } } #[cfg(not(feature = "maths-nopanic"))] fn ln(&self) -> Decimal { match self.checked_ln() { Some(result) => result, None => { if self.is_sign_negative() { panic!("Unable to calculate ln for negative numbers") } else if self.is_zero() { panic!("Unable to calculate ln for zero") } else { panic!("Calculation of ln failed for unknown reasons") } } } } fn checked_ln(&self) -> Option { if self.is_sign_negative() || self.is_zero() { return None; } if self.is_one() { return Some(Decimal::ZERO); } // Approximate using Taylor Series let mut x = *self; let mut count = 0; while x >= Decimal::ONE { x *= Decimal::E_INVERSE; count += 1; } while x <= Decimal::E_INVERSE { x *= Decimal::E; count -= 1; } x -= Decimal::ONE; if x.is_zero() { return Some(Decimal::new(count, 0)); } let mut result = Decimal::ZERO; let mut iteration = 0; let mut y = Decimal::ONE; let mut last = Decimal::ONE; while last != result && iteration < 100 { iteration += 1; last = result; y *= -x; result += y / Decimal::new(iteration, 0); } Some(Decimal::new(count, 0) - result) } #[cfg(feature = "maths-nopanic")] fn log10(&self) -> Decimal { match self.checked_log10() { Some(result) => result, None => Decimal::ZERO, } } #[cfg(not(feature = "maths-nopanic"))] fn log10(&self) -> Decimal { match self.checked_log10() { Some(result) => result, None => { if self.is_sign_negative() { panic!("Unable to calculate log10 for negative numbers") } else if self.is_zero() { panic!("Unable to calculate log10 for zero") } else { panic!("Calculation of log10 failed for unknown reasons") } } } } fn checked_log10(&self) -> Option { use crate::ops::array::{div_by_u32, is_all_zero}; // Early exits if self.is_sign_negative() || self.is_zero() { return None; } if self.is_one() { return Some(Decimal::ZERO); } // This uses a very basic method for calculating log10. We know the following is true: // log10(n) = ln(n) / ln(10) // From this we can perform some small optimizations: // 1. ln(10) is a constant // 2. Multiplication is faster than division, so we can pre-calculate the constant 1/ln(10) // This allows us to then simplify log10(n) to: // log10(n) = C * ln(n) // Before doing all of this however, we see if there are simple calculations to be made. let mut working = self.mantissa_array3(); let mut result = 0; let mut invalid_early_exit = false; while !is_all_zero(&working) { let remainder = div_by_u32(&mut working, 10u32); if remainder != 0 { invalid_early_exit = true; break; } result += 1; if working[2] == 0 && working[1] == 0 && working[0] == 1 { break; } } if !invalid_early_exit { return Some((result - self.scale()).into()); } self.checked_ln().map(|result| LN10_INVERSE * result) } fn erf(&self) -> Decimal { if self.is_sign_positive() { let one = &Decimal::ONE; let xa1 = self * Decimal::from_parts(705230784, 0, 0, false, 10); let xa2 = self.powi(2) * Decimal::from_parts(422820123, 0, 0, false, 10); let xa3 = self.powi(3) * Decimal::from_parts(92705272, 0, 0, false, 10); let xa4 = self.powi(4) * Decimal::from_parts(1520143, 0, 0, false, 10); let xa5 = self.powi(5) * Decimal::from_parts(2765672, 0, 0, false, 10); let xa6 = self.powi(6) * Decimal::from_parts(430638, 0, 0, false, 10); let sum = one + xa1 + xa2 + xa3 + xa4 + xa5 + xa6; one - (one / sum.powi(16)) } else { -self.abs().erf() } } fn norm_cdf(&self) -> Decimal { (Decimal::ONE + (self / Decimal::from_parts(2318911239, 3292722, 0, false, 16)).erf()) / Decimal::TWO } fn norm_pdf(&self) -> Decimal { match self.checked_norm_pdf() { Some(d) => d, None => panic!("Norm Pdf overflowed"), } } fn checked_norm_pdf(&self) -> Option { let sqrt2pi = Decimal::from_parts_raw(2133383024, 2079885984, 1358845910, 1835008); let factor = -self.checked_powi(2)?; let factor = factor.checked_div(Decimal::TWO)?; factor.checked_exp()?.checked_div(sqrt2pi) } fn sin(&self) -> Decimal { match self.checked_sin() { Some(x) => x, None => panic!("Sin overflowed"), } } fn checked_sin(&self) -> Option { if self.is_zero() { return Some(Decimal::ZERO); } if self.is_sign_negative() { // -Sin(-x) return (-self).checked_sin().map(|x| -x); } if self >= &Decimal::TWO_PI { // Reduce large numbers early - we can do this using rem to constrain to a range let adjusted = self.checked_rem(Decimal::TWO_PI)?; return adjusted.checked_sin(); } if self >= &Decimal::PI { // -Sin(x-Ï€) return (self - Decimal::PI).checked_sin().map(|x| -x); } if self >= &Decimal::QUARTER_PI { // Cos(Ï€2-x) return (Decimal::HALF_PI - self).checked_cos(); } // Taylor series: // ∑(n=0 to ∞) : ((−1)^n / (2n + 1)!) * x^(2n + 1) , x∈R // First few expansions: // x^1/1! - x^3/3! + x^5/5! - x^7/7! + x^9/9! let mut result = Decimal::ZERO; for n in 0..TRIG_SERIES_UPPER_BOUND { let x = 2 * n + 1; let element = self.checked_powi(x as i64)?.checked_div(FACTORIAL[x])?; if n & 0x1 == 0 { result += element; } else { result -= element; } } Some(result) } fn cos(&self) -> Decimal { match self.checked_cos() { Some(x) => x, None => panic!("Cos overflowed"), } } fn checked_cos(&self) -> Option { if self.is_zero() { return Some(Decimal::ONE); } if self.is_sign_negative() { // Cos(-x) return (-self).checked_cos(); } if self >= &Decimal::TWO_PI { // Reduce large numbers early - we can do this using rem to constrain to a range let adjusted = self.checked_rem(Decimal::TWO_PI)?; return adjusted.checked_cos(); } if self >= &Decimal::PI { // -Cos(x-Ï€) return (self - Decimal::PI).checked_cos().map(|x| -x); } if self >= &Decimal::QUARTER_PI { // Sin(Ï€2-x) return (Decimal::HALF_PI - self).checked_sin(); } // Taylor series: // ∑(n=0 to ∞) : ((−1)^n / (2n)!) * x^(2n) , x∈R // First few expansions: // x^0/0! - x^2/2! + x^4/4! - x^6/6! + x^8/8! let mut result = Decimal::ZERO; for n in 0..TRIG_SERIES_UPPER_BOUND { let x = 2 * n; let element = self.checked_powi(x as i64)?.checked_div(FACTORIAL[x])?; if n & 0x1 == 0 { result += element; } else { result -= element; } } Some(result) } fn tan(&self) -> Decimal { match self.checked_tan() { Some(x) => x, None => panic!("Tan overflowed"), } } fn checked_tan(&self) -> Option { if self.is_zero() { return Some(Decimal::ZERO); } if self.is_sign_negative() { // -Tan(-x) return (-self).checked_tan().map(|x| -x); } if self >= &Decimal::TWO_PI { // Reduce large numbers early - we can do this using rem to constrain to a range let adjusted = self.checked_rem(Decimal::TWO_PI)?; return adjusted.checked_tan(); } // Reduce to 0 <= x <= PI if self >= &Decimal::PI { // Tan(x-Ï€) return (self - Decimal::PI).checked_tan(); } // Reduce to 0 <= x <= PI/2 if self > &Decimal::HALF_PI { // We can use the symmetrical function inside the first quadrant // e.g. tan(x) = -tan((PI/2 - x) + PI/2) return ((Decimal::HALF_PI - self) + Decimal::HALF_PI).checked_tan().map(|x| -x); } // It has now been reduced to 0 <= x <= PI/2. If it is >= PI/4 we can make it even smaller // by calculating tan(PI/2 - x) and taking the reciprocal if self > &Decimal::QUARTER_PI { return match (Decimal::HALF_PI - self).checked_tan() { Some(x) => Decimal::ONE.checked_div(x), None => None, }; } // Due the way that tan(x) sharply tends towards infinity, we try to optimize // the resulting accuracy by using Trigonometric identity when > PI/8. We do this by // replacing the angle with one that is half as big. if self > &EIGHTH_PI { // Work out tan(x/2) let tan_half = (self / Decimal::TWO).checked_tan()?; // Work out the dividend i.e. 2tan(x/2) let dividend = Decimal::TWO.checked_mul(tan_half)?; // Work out the divisor i.e. 1 - tan^2(x/2) let squared = tan_half.checked_mul(tan_half)?; let divisor = Decimal::ONE - squared; // Treat this as infinity if divisor.is_zero() { return None; } return dividend.checked_div(divisor); } // Do a polynomial approximation based upon the Maclaurin series. // This can be simplified to something like: // // ∑(n=1,3,5,7,9)(f(n)(0)/n!)x^n // // First few expansions (which we leverage): // (f'(0)/1!)x^1 + (f'''(0)/3!)x^3 + (f'''''(0)/5!)x^5 + (f'''''''/7!)x^7 // // x + (1/3)x^3 + (2/15)x^5 + (17/315)x^7 + (62/2835)x^9 + (1382/155925)x^11 // // (Generated by https://www.wolframalpha.com/widgets/view.jsp?id=fe1ad8d4f5dbb3cb866d0c89beb527a6) // The more terms, the better the accuracy. This generates accuracy within approx 10^-8 for angles // less than PI/8. const SERIES: [(Decimal, u64); 6] = [ // 1 / 3 (Decimal::from_parts_raw(89478485, 347537611, 180700362, 1835008), 3), // 2 / 15 (Decimal::from_parts_raw(894784853, 3574988881, 72280144, 1835008), 5), // 17 / 315 (Decimal::from_parts_raw(905437054, 3907911371, 2925624, 1769472), 7), // 62 / 2835 (Decimal::from_parts_raw(3191872741, 2108928381, 11855473, 1835008), 9), // 1382 / 155925 (Decimal::from_parts_raw(3482645539, 2612995122, 4804769, 1835008), 11), // 21844 / 6081075 (Decimal::from_parts_raw(4189029078, 2192791200, 1947296, 1835008), 13), ]; let mut result = *self; for (fraction, pow) in SERIES { result += fraction * self.powu(pow); } Some(result) } } impl Pow for Decimal { type Output = Decimal; fn pow(self, rhs: Decimal) -> Self::Output { MathematicalOps::powd(&self, rhs) } } impl Pow for Decimal { type Output = Decimal; fn pow(self, rhs: u64) -> Self::Output { MathematicalOps::powu(&self, rhs) } } impl Pow for Decimal { type Output = Decimal; fn pow(self, rhs: i64) -> Self::Output { MathematicalOps::powi(&self, rhs) } } impl Pow for Decimal { type Output = Decimal; fn pow(self, rhs: f64) -> Self::Output { MathematicalOps::powf(&self, rhs) } } #[cfg(test)] mod test { use super::*; #[test] fn test_factorials() { assert_eq!("1", FACTORIAL[0].to_string(), "0!"); assert_eq!("1", FACTORIAL[1].to_string(), "1!"); assert_eq!("2", FACTORIAL[2].to_string(), "2!"); assert_eq!("6", FACTORIAL[3].to_string(), "3!"); assert_eq!("24", FACTORIAL[4].to_string(), "4!"); assert_eq!("120", FACTORIAL[5].to_string(), "5!"); assert_eq!("720", FACTORIAL[6].to_string(), "6!"); assert_eq!("5040", FACTORIAL[7].to_string(), "7!"); assert_eq!("40320", FACTORIAL[8].to_string(), "8!"); assert_eq!("362880", FACTORIAL[9].to_string(), "9!"); assert_eq!("3628800", FACTORIAL[10].to_string(), "10!"); assert_eq!("39916800", FACTORIAL[11].to_string(), "11!"); assert_eq!("479001600", FACTORIAL[12].to_string(), "12!"); assert_eq!("6227020800", FACTORIAL[13].to_string(), "13!"); assert_eq!("87178291200", FACTORIAL[14].to_string(), "14!"); assert_eq!("1307674368000", FACTORIAL[15].to_string(), "15!"); assert_eq!("20922789888000", FACTORIAL[16].to_string(), "16!"); assert_eq!("355687428096000", FACTORIAL[17].to_string(), "17!"); assert_eq!("6402373705728000", FACTORIAL[18].to_string(), "18!"); assert_eq!("121645100408832000", FACTORIAL[19].to_string(), "19!"); assert_eq!("2432902008176640000", FACTORIAL[20].to_string(), "20!"); assert_eq!("51090942171709440000", FACTORIAL[21].to_string(), "21!"); assert_eq!("1124000727777607680000", FACTORIAL[22].to_string(), "22!"); assert_eq!("25852016738884976640000", FACTORIAL[23].to_string(), "23!"); assert_eq!("620448401733239439360000", FACTORIAL[24].to_string(), "24!"); assert_eq!("15511210043330985984000000", FACTORIAL[25].to_string(), "25!"); assert_eq!("403291461126605635584000000", FACTORIAL[26].to_string(), "26!"); assert_eq!("10888869450418352160768000000", FACTORIAL[27].to_string(), "27!"); } } rust_decimal-1.17.0/src/ops/add.rs000064400000000000000000000300330072674642500151020ustar 00000000000000use crate::constants::{MAX_I32_SCALE, POWERS_10, SCALE_MASK, SCALE_SHIFT, SIGN_MASK, U32_MASK, U32_MAX}; use crate::decimal::{CalculationResult, Decimal}; use crate::ops::common::{Buf24, Dec64}; pub(crate) fn add_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { add_sub_internal(d1, d2, false) } pub(crate) fn sub_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { add_sub_internal(d1, d2, true) } #[inline] fn add_sub_internal(d1: &Decimal, d2: &Decimal, subtract: bool) -> CalculationResult { if d1.is_zero() { // 0 - x or 0 + x let mut result = *d2; if subtract && !d2.is_zero() { result.set_sign_negative(d2.is_sign_positive()); } return CalculationResult::Ok(result); } if d2.is_zero() { // x - 0 or x + 0 return CalculationResult::Ok(*d1); } // Work out whether we need to rescale and/or if it's a subtract still given the signs of the // numbers. let flags = d1.flags() ^ d2.flags(); let subtract = subtract ^ ((flags & SIGN_MASK) != 0); let rescale = (flags & SCALE_MASK) > 0; // We optimize towards using 32 bit logic as much as possible. It's noticeably faster at // scale, even on 64 bit machines if d1.mid() | d1.hi() == 0 && d2.mid() | d2.hi() == 0 { // We'll try to rescale, however we may end up with 64 bit (or more) numbers // If we do, we'll choose a different flow than fast_add if rescale { // This is less optimized if we scale to a 64 bit integer. We can add some further logic // here later on. let rescale_factor = ((d2.flags() & SCALE_MASK) as i32 - (d1.flags() & SCALE_MASK) as i32) >> SCALE_SHIFT; if rescale_factor < 0 { // We try to rescale the rhs if let Some(rescaled) = rescale32(d2.lo(), -rescale_factor) { return fast_add(d1.lo(), rescaled, d1.flags(), subtract); } } else { // We try to rescale the lhs if let Some(rescaled) = rescale32(d1.lo(), rescale_factor) { return fast_add( rescaled, d2.lo(), (d2.flags() & SCALE_MASK) | (d1.flags() & SIGN_MASK), subtract, ); } } } else { return fast_add(d1.lo(), d2.lo(), d1.flags(), subtract); } } // Continue on with the slower 64 bit method let d1 = Dec64::new(d1); let d2 = Dec64::new(d2); // If we're not the same scale then make sure we're there first before starting addition if rescale { let rescale_factor = d2.scale as i32 - d1.scale as i32; if rescale_factor < 0 { let negative = subtract ^ d1.negative; let scale = d1.scale; unaligned_add(d2, d1, negative, scale, -rescale_factor, subtract) } else { let negative = d1.negative; let scale = d2.scale; unaligned_add(d1, d2, negative, scale, rescale_factor, subtract) } } else { let neg = d1.negative; let scale = d1.scale; aligned_add(d1, d2, neg, scale, subtract) } } #[inline(always)] fn rescale32(num: u32, rescale_factor: i32) -> Option { if rescale_factor > MAX_I32_SCALE { return None; } num.checked_mul(POWERS_10[rescale_factor as usize]) } fn fast_add(lo1: u32, lo2: u32, flags: u32, subtract: bool) -> CalculationResult { if subtract { // Sub can't overflow because we're ensuring the bigger number always subtracts the smaller number if lo1 < lo2 { return CalculationResult::Ok(Decimal::from_parts_raw(lo2 - lo1, 0, 0, flags ^ SIGN_MASK)); } return CalculationResult::Ok(Decimal::from_parts_raw(lo1 - lo2, 0, 0, flags)); } // Add can overflow however, so we check for that explicitly let lo = lo1.wrapping_add(lo2); let mid = if lo < lo1 { 1 } else { 0 }; CalculationResult::Ok(Decimal::from_parts_raw(lo, mid, 0, flags)) } fn aligned_add(lhs: Dec64, rhs: Dec64, negative: bool, scale: u32, subtract: bool) -> CalculationResult { if subtract { // Signs differ, so subtract let mut result = Dec64 { negative, scale, low64: lhs.low64.wrapping_sub(rhs.low64), hi: lhs.hi.wrapping_sub(rhs.hi), }; // Check for carry if result.low64 > lhs.low64 { result.hi = result.hi.wrapping_sub(1); if result.hi >= lhs.hi { flip_sign(&mut result); } } else if result.hi > lhs.hi { flip_sign(&mut result); } CalculationResult::Ok(result.to_decimal()) } else { // Signs are the same, so add let mut result = Dec64 { negative, scale, low64: lhs.low64.wrapping_add(rhs.low64), hi: lhs.hi.wrapping_add(rhs.hi), }; // Check for carry if result.low64 < lhs.low64 { result.hi = result.hi.wrapping_add(1); if result.hi <= lhs.hi { if result.scale == 0 { return CalculationResult::Overflow; } reduce_scale(&mut result); } } else if result.hi < lhs.hi { if result.scale == 0 { return CalculationResult::Overflow; } reduce_scale(&mut result); } CalculationResult::Ok(result.to_decimal()) } } fn flip_sign(result: &mut Dec64) { // Bitwise not the high portion result.hi = !result.hi; let low64 = ((result.low64 as i64).wrapping_neg()) as u64; if low64 == 0 { result.hi += 1; } result.low64 = low64; result.negative = !result.negative; } fn reduce_scale(result: &mut Dec64) { let mut low64 = result.low64; let mut hi = result.hi; let mut num = (hi as u64) + (1u64 << 32); hi = (num / 10u64) as u32; num = ((num - (hi as u64) * 10u64) << 32) + (low64 >> 32); let mut div = (num / 10) as u32; num = ((num - (div as u64) * 10u64) << 32) + (low64 & U32_MASK); low64 = (div as u64) << 32; div = (num / 10u64) as u32; low64 = low64.wrapping_add(div as u64); let remainder = (num as u32).wrapping_sub(div.wrapping_mul(10)); // Finally, round. This is optimizing slightly toward non-rounded numbers if remainder >= 5 && (remainder > 5 || (low64 & 1) > 0) { low64 = low64.wrapping_add(1); if low64 == 0 { hi += 1; } } result.low64 = low64; result.hi = hi; result.scale -= 1; } // Assumption going into this function is that the LHS is the larger number and will "absorb" the // smaller number. fn unaligned_add( lhs: Dec64, rhs: Dec64, negative: bool, scale: u32, rescale_factor: i32, subtract: bool, ) -> CalculationResult { let mut lhs = lhs; let mut low64 = lhs.low64; let mut high = lhs.hi; let mut rescale_factor = rescale_factor; // First off, we see if we can get away with scaling small amounts (or none at all) if high == 0 { if low64 <= U32_MAX { // We know it's not zero, so we start scaling. // Start with reducing the scale down for the low portion while low64 <= U32_MAX { if rescale_factor <= MAX_I32_SCALE { low64 *= POWERS_10[rescale_factor as usize] as u64; lhs.low64 = low64; return aligned_add(lhs, rhs, negative, scale, subtract); } rescale_factor -= MAX_I32_SCALE; low64 *= POWERS_10[9] as u64; } } // Reduce the scale for the high portion while high == 0 { let power = if rescale_factor <= MAX_I32_SCALE { POWERS_10[rescale_factor as usize] as u64 } else { POWERS_10[9] as u64 }; let tmp_low = (low64 & U32_MASK) * power; let tmp_hi = (low64 >> 32) * power + (tmp_low >> 32); low64 = (tmp_low & U32_MASK) + (tmp_hi << 32); high = (tmp_hi >> 32) as u32; rescale_factor -= MAX_I32_SCALE; if rescale_factor <= 0 { lhs.low64 = low64; lhs.hi = high; return aligned_add(lhs, rhs, negative, scale, subtract); } } } // See if we can get away with keeping it in the 96 bits. Otherwise, we need a buffer let mut tmp64: u64; loop { let power = if rescale_factor <= MAX_I32_SCALE { POWERS_10[rescale_factor as usize] as u64 } else { POWERS_10[9] as u64 }; let tmp_low = (low64 & U32_MASK) * power; tmp64 = (low64 >> 32) * power + (tmp_low >> 32); low64 = (tmp_low & U32_MASK) + (tmp64 << 32); tmp64 >>= 32; tmp64 += (high as u64) * power; rescale_factor -= MAX_I32_SCALE; if tmp64 > U32_MAX { break; } else { high = tmp64 as u32; if rescale_factor <= 0 { lhs.low64 = low64; lhs.hi = high; return aligned_add(lhs, rhs, negative, scale, subtract); } } } let mut buffer = Buf24::zero(); buffer.set_low64(low64); buffer.set_mid64(tmp64); let mut upper_word = buffer.upper_word(); while rescale_factor > 0 { let power = if rescale_factor <= MAX_I32_SCALE { POWERS_10[rescale_factor as usize] as u64 } else { POWERS_10[9] as u64 }; tmp64 = 0; for (index, part) in buffer.data.iter_mut().enumerate() { tmp64 = tmp64.wrapping_add((*part as u64) * power); *part = tmp64 as u32; tmp64 >>= 32; if index + 1 > upper_word { break; } } if tmp64 & U32_MASK > 0 { // Extend the result upper_word += 1; buffer.data[upper_word] = tmp64 as u32; } rescale_factor -= MAX_I32_SCALE; } // Do the add tmp64 = buffer.low64(); low64 = rhs.low64; let tmp_hi = buffer.data[2]; high = rhs.hi; if subtract { low64 = tmp64.wrapping_sub(low64); high = tmp_hi.wrapping_sub(high); // Check for carry let carry = if low64 > tmp64 { high = high.wrapping_sub(1); high >= tmp_hi } else { high > tmp_hi }; if carry { for part in buffer.data.iter_mut().skip(3) { *part = part.wrapping_sub(1); if *part > 0 { break; } } if buffer.data[upper_word] == 0 && upper_word < 3 { return CalculationResult::Ok(Decimal::from_parts( low64 as u32, (low64 >> 32) as u32, high, negative, scale, )); } } } else { low64 = low64.wrapping_add(tmp64); high = high.wrapping_add(tmp_hi); // Check for carry let carry = if low64 < tmp64 { high = high.wrapping_add(1); high <= tmp_hi } else { high < tmp_hi }; if carry { for (index, part) in buffer.data.iter_mut().enumerate().skip(3) { if upper_word < index { *part = 1; upper_word = index; break; } *part = part.wrapping_add(1); if *part > 0 { break; } } } } buffer.set_low64(low64); buffer.data[2] = high; if let Some(scale) = buffer.rescale(upper_word, scale) { CalculationResult::Ok(Decimal::from_parts( buffer.data[0], buffer.data[1], buffer.data[2], negative, scale, )) } else { CalculationResult::Overflow } } rust_decimal-1.17.0/src/ops/array.rs000064400000000000000000000247350072674642500155040ustar 00000000000000use crate::constants::{POWERS_10, U32_MASK}; /// Rescales the given decimal to new scale. /// e.g. with 1.23 and new scale 3 rescale the value to 1.230 #[inline(always)] pub(crate) fn rescale_internal(value: &mut [u32; 3], value_scale: &mut u32, new_scale: u32) { if *value_scale == new_scale { // Nothing to do return; } if is_all_zero(value) { *value_scale = new_scale; return; } if *value_scale > new_scale { let mut diff = *value_scale - new_scale; // Scaling further isn't possible since we got an overflow // In this case we need to reduce the accuracy of the "side to keep" // Now do the necessary rounding let mut remainder = 0; while diff > 0 { if is_all_zero(value) { *value_scale = new_scale; return; } diff -= 1; // Any remainder is discarded if diff > 0 still (i.e. lost precision) remainder = div_by_u32(value, 10); } if remainder >= 5 { for part in value.iter_mut() { let digit = u64::from(*part) + 1u64; remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 }; *part = (digit & 0xFFFF_FFFF) as u32; if remainder == 0 { break; } } } *value_scale = new_scale; } else { let mut diff = new_scale - *value_scale; let mut working = [value[0], value[1], value[2]]; while diff > 0 && mul_by_10(&mut working) == 0 { value.copy_from_slice(&working); diff -= 1; } *value_scale = new_scale - diff; } } pub(crate) fn add_by_internal(value: &mut [u32], by: &[u32]) -> u32 { let mut carry: u64 = 0; let vl = value.len(); let bl = by.len(); if vl >= bl { let mut sum: u64; for i in 0..bl { sum = u64::from(value[i]) + u64::from(by[i]) + carry; value[i] = (sum & U32_MASK) as u32; carry = sum >> 32; } if vl > bl && carry > 0 { for i in value.iter_mut().skip(bl) { sum = u64::from(*i) + carry; *i = (sum & U32_MASK) as u32; carry = sum >> 32; if carry == 0 { break; } } } } else if vl + 1 == bl { // Overflow, by default, is anything in the high portion of by let mut sum: u64; for i in 0..vl { sum = u64::from(value[i]) + u64::from(by[i]) + carry; value[i] = (sum & U32_MASK) as u32; carry = sum >> 32; } if by[vl] > 0 { carry += u64::from(by[vl]); } } else { panic!("Internal error: add using incompatible length arrays. {} <- {}", vl, bl); } carry as u32 } #[inline] pub(crate) fn add_one_internal(value: &mut [u32]) -> u32 { let mut carry: u64 = 1; // Start with one, since adding one let mut sum: u64; for i in value.iter_mut() { sum = (*i as u64) + carry; *i = (sum & U32_MASK) as u32; carry = sum >> 32; } carry as u32 } pub(crate) fn sub_by_internal(value: &mut [u32], by: &[u32]) -> u32 { // The way this works is similar to long subtraction // Let's assume we're working with bytes for simplicity in an example: // 257 - 8 = 249 // 0000_0001 0000_0001 - 0000_0000 0000_1000 = 0000_0000 1111_1001 // We start by doing the first byte... // Overflow = 0 // Left = 0000_0001 (1) // Right = 0000_1000 (8) // Firstly, we make sure the left and right are scaled up to twice the size // Left = 0000_0000 0000_0001 // Right = 0000_0000 0000_1000 // We then subtract right from left // Result = Left - Right = 1111_1111 1111_1001 // We subtract the overflow, which in this case is 0. // Because left < right (1 < 8) we invert the high part. // Lo = 1111_1001 // Hi = 1111_1111 -> 0000_0001 // Lo is the field, hi is the overflow. // We do the same for the second byte... // Overflow = 1 // Left = 0000_0001 // Right = 0000_0000 // Result = Left - Right = 0000_0000 0000_0001 // We subtract the overflow... // Result = 0000_0000 0000_0001 - 1 = 0 // And we invert the high, just because (invert 0 = 0). // So our result is: // 0000_0000 1111_1001 let mut overflow = 0; let vl = value.len(); let bl = by.len(); for i in 0..vl { if i >= bl { break; } let (lo, hi) = sub_part(value[i], by[i], overflow); value[i] = lo; overflow = hi; } overflow } fn sub_part(left: u32, right: u32, overflow: u32) -> (u32, u32) { let part = 0x1_0000_0000u64 + u64::from(left) - (u64::from(right) + u64::from(overflow)); let lo = part as u32; let hi = 1 - ((part >> 32) as u32); (lo, hi) } // Returns overflow #[inline] pub(crate) fn mul_by_10(bits: &mut [u32; 3]) -> u32 { let mut overflow = 0u64; for b in bits.iter_mut() { let result = u64::from(*b) * 10u64 + overflow; let hi = (result >> 32) & U32_MASK; let lo = (result & U32_MASK) as u32; *b = lo; overflow = hi; } overflow as u32 } // Returns overflow pub(crate) fn mul_by_u32(bits: &mut [u32], m: u32) -> u32 { let mut overflow = 0; for b in bits.iter_mut() { let (lo, hi) = mul_part(*b, m, overflow); *b = lo; overflow = hi; } overflow } pub(crate) fn mul_part(left: u32, right: u32, high: u32) -> (u32, u32) { let result = u64::from(left) * u64::from(right) + u64::from(high); let hi = ((result >> 32) & U32_MASK) as u32; let lo = (result & U32_MASK) as u32; (lo, hi) } // Returns remainder pub(crate) fn div_by_u32(bits: &mut [u32], divisor: u32) -> u32 { if divisor == 0 { // Divide by zero panic!("Internal error: divide by zero"); } else if divisor == 1 { // dividend remains unchanged 0 } else { let mut remainder = 0u32; let divisor = u64::from(divisor); for part in bits.iter_mut().rev() { let temp = (u64::from(remainder) << 32) + u64::from(*part); remainder = (temp % divisor) as u32; *part = (temp / divisor) as u32; } remainder } } pub(crate) fn div_by_1x(bits: &mut [u32; 3], power: usize) -> u32 { let mut remainder = 0u32; let divisor = POWERS_10[power] as u64; let temp = ((remainder as u64) << 32) + (bits[2] as u64); remainder = (temp % divisor) as u32; bits[2] = (temp / divisor) as u32; let temp = ((remainder as u64) << 32) + (bits[1] as u64); remainder = (temp % divisor) as u32; bits[1] = (temp / divisor) as u32; let temp = ((remainder as u64) << 32) + (bits[0] as u64); remainder = (temp % divisor) as u32; bits[0] = (temp / divisor) as u32; remainder } #[inline] pub(crate) fn shl1_internal(bits: &mut [u32], carry: u32) -> u32 { let mut carry = carry; for part in bits.iter_mut() { let b = *part >> 31; *part = (*part << 1) | carry; carry = b; } carry } #[inline] pub(crate) fn cmp_internal(left: &[u32; 3], right: &[u32; 3]) -> core::cmp::Ordering { let left_hi: u32 = left[2]; let right_hi: u32 = right[2]; let left_lo: u64 = u64::from(left[1]) << 32 | u64::from(left[0]); let right_lo: u64 = u64::from(right[1]) << 32 | u64::from(right[0]); if left_hi < right_hi || (left_hi <= right_hi && left_lo < right_lo) { core::cmp::Ordering::Less } else if left_hi == right_hi && left_lo == right_lo { core::cmp::Ordering::Equal } else { core::cmp::Ordering::Greater } } #[inline] pub(crate) fn is_all_zero(bits: &[u32]) -> bool { bits.iter().all(|b| *b == 0) } #[cfg(test)] mod test { // Tests on private methods. // // All public tests should go under `tests/`. use super::*; use crate::prelude::*; #[test] fn it_can_rescale_internal() { fn extract(value: &str) -> ([u32; 3], u32) { let v = Decimal::from_str(value).unwrap(); (v.mantissa_array3(), v.scale()) } let tests = &[ ("1", 0, "1"), ("1", 1, "1.0"), ("1", 5, "1.00000"), ("1", 10, "1.0000000000"), ("1", 20, "1.00000000000000000000"), ("0.6386554621848739495798319328", 27, "0.638655462184873949579831933"), ( "843.65000000", // Scale 8 25, // 25 "843.6500000000000000000000000", // 25 ), ( "843.65000000", // Scale 8 30, // 30 "843.6500000000000000000000000000", // 28 ), ]; for &(value_raw, new_scale, expected_value) in tests { let (expected_value, _) = extract(expected_value); let (mut value, mut value_scale) = extract(value_raw); rescale_internal(&mut value, &mut value_scale, new_scale); assert_eq!(value, expected_value); } } #[test] fn test_shl1_internal() { struct TestCase { // One thing to be cautious of is that the structure of a number here for shifting left is // the reverse of how you may conceive this mentally. i.e. a[2] contains the higher order // bits: a[2] a[1] a[0] given: [u32; 3], given_carry: u32, expected: [u32; 3], expected_carry: u32, } let tests = [ TestCase { given: [1, 0, 0], given_carry: 0, expected: [2, 0, 0], expected_carry: 0, }, TestCase { given: [1, 0, 2147483648], given_carry: 1, expected: [3, 0, 0], expected_carry: 1, }, ]; for case in &tests { let mut test = [case.given[0], case.given[1], case.given[2]]; let carry = shl1_internal(&mut test, case.given_carry); assert_eq!( test, case.expected, "Bits: {:?} << 1 | {}", case.given, case.given_carry ); assert_eq!( carry, case.expected_carry, "Carry: {:?} << 1 | {}", case.given, case.given_carry ) } } } rust_decimal-1.17.0/src/ops/cmp.rs000064400000000000000000000055100072674642500151330ustar 00000000000000use crate::constants::{MAX_I32_SCALE, POWERS_10, U32_MASK, U32_MAX}; use crate::decimal::Decimal; use crate::ops::common::Dec64; use core::cmp::Ordering; pub(crate) fn cmp_impl(d1: &Decimal, d2: &Decimal) -> Ordering { if d2.is_zero() { return if d1.is_zero() { return Ordering::Equal; } else if d1.is_sign_negative() { Ordering::Less } else { Ordering::Greater }; } if d1.is_zero() { return if d2.is_sign_negative() { Ordering::Greater } else { Ordering::Less }; } // If the sign is different, then it's an easy answer if d1.is_sign_negative() != d2.is_sign_negative() { return if d1.is_sign_negative() { Ordering::Less } else { Ordering::Greater }; } // Otherwise, do a deep comparison let d1 = Dec64::new(d1); let d2 = Dec64::new(d2); // We know both signs are the same here so flip it here. // Negative is handled differently. i.e. 0.5 > 0.01 however -0.5 < -0.01 if d1.negative { cmp_internal(&d2, &d1) } else { cmp_internal(&d1, &d2) } } pub(in crate::ops) fn cmp_internal(d1: &Dec64, d2: &Dec64) -> Ordering { // This function ignores sign let mut d1_low = d1.low64; let mut d1_high = d1.hi; let mut d2_low = d2.low64; let mut d2_high = d2.hi; // If the scale factors aren't equal then if d1.scale != d2.scale { let mut diff = d2.scale as i32 - d1.scale as i32; if diff < 0 { diff = -diff; if !rescale(&mut d2_low, &mut d2_high, diff as u32) { return Ordering::Less; } } else if !rescale(&mut d1_low, &mut d1_high, diff as u32) { return Ordering::Greater; } } // They're the same scale, do a standard bitwise comparison let hi_order = d1_high.cmp(&d2_high); if hi_order != Ordering::Equal { return hi_order; } d1_low.cmp(&d2_low) } fn rescale(low64: &mut u64, high: &mut u32, diff: u32) -> bool { let mut diff = diff as i32; // We need to modify d1 by 10^diff to get it to the same scale as d2 loop { let power = if diff >= MAX_I32_SCALE { POWERS_10[9] } else { POWERS_10[diff as usize] } as u64; let tmp_lo_32 = (*low64 & U32_MASK) * power; let mut tmp = (*low64 >> 32) * power + (tmp_lo_32 >> 32); *low64 = (tmp_lo_32 & U32_MASK) + (tmp << 32); tmp >>= 32; tmp = tmp.wrapping_add((*high as u64) * power); // Indicates > 96 bits if tmp > U32_MAX { return false; } *high = tmp as u32; // Keep scaling if there is more to go diff -= MAX_I32_SCALE; if diff <= 0 { break; } } true } rust_decimal-1.17.0/src/ops/common.rs000064400000000000000000000324450072674642500156530ustar 00000000000000use crate::constants::{MAX_I32_SCALE, MAX_PRECISION_I32, POWERS_10}; use crate::Decimal; #[derive(Debug)] pub struct Buf12 { pub data: [u32; 3], } impl Buf12 { pub(super) const fn from_dec64(value: &Dec64) -> Self { Buf12 { data: [value.low64 as u32, (value.low64 >> 32) as u32, value.hi], } } pub(super) const fn from_decimal(value: &Decimal) -> Self { Buf12 { data: value.mantissa_array3(), } } #[inline(always)] pub const fn lo(&self) -> u32 { self.data[0] } #[inline(always)] pub const fn mid(&self) -> u32 { self.data[1] } #[inline(always)] pub const fn hi(&self) -> u32 { self.data[2] } #[inline(always)] pub fn set_lo(&mut self, value: u32) { self.data[0] = value; } #[inline(always)] pub fn set_mid(&mut self, value: u32) { self.data[1] = value; } #[inline(always)] pub fn set_hi(&mut self, value: u32) { self.data[2] = value; } #[inline(always)] pub const fn low64(&self) -> u64 { ((self.data[1] as u64) << 32) | (self.data[0] as u64) } #[inline(always)] pub fn set_low64(&mut self, value: u64) { self.data[1] = (value >> 32) as u32; self.data[0] = value as u32; } #[inline(always)] pub const fn high64(&self) -> u64 { ((self.data[2] as u64) << 32) | (self.data[1] as u64) } #[inline(always)] pub fn set_high64(&mut self, value: u64) { self.data[2] = (value >> 32) as u32; self.data[1] = value as u32; } // Determine the maximum value of x that ensures that the quotient when scaled up by 10^x // still fits in 96 bits. Ultimately, we want to make scale positive - if we can't then // we're going to overflow. Because x is ultimately used to lookup inside the POWERS array, it // must be a valid value 0 <= x <= 9 pub fn find_scale(&self, scale: i32) -> Option { const OVERFLOW_MAX_9_HI: u32 = 4; const OVERFLOW_MAX_8_HI: u32 = 42; const OVERFLOW_MAX_7_HI: u32 = 429; const OVERFLOW_MAX_6_HI: u32 = 4294; const OVERFLOW_MAX_5_HI: u32 = 42949; const OVERFLOW_MAX_4_HI: u32 = 429496; const OVERFLOW_MAX_3_HI: u32 = 4294967; const OVERFLOW_MAX_2_HI: u32 = 42949672; const OVERFLOW_MAX_1_HI: u32 = 429496729; const OVERFLOW_MAX_9_LOW64: u64 = 5441186219426131129; let hi = self.data[2]; let low64 = self.low64(); let mut x = 0usize; // Quick check to stop us from trying to scale any more. // if hi > OVERFLOW_MAX_1_HI { // If it's less than 0, which it probably is - overflow. We can't do anything. if scale < 0 { return None; } return Some(x); } if scale > MAX_PRECISION_I32 - 9 { // We can't scale by 10^9 without exceeding the max scale factor. // Instead, we'll try to scale by the most that we can and see if that works. // This is safe to do due to the check above. e.g. scale > 19 in the above, so it will // evaluate to 9 or less below. x = (MAX_PRECISION_I32 - scale) as usize; if hi < POWER_OVERFLOW_VALUES[x - 1].data[2] { if x as i32 + scale < 0 { // We still overflow return None; } return Some(x); } } else if hi < OVERFLOW_MAX_9_HI || hi == OVERFLOW_MAX_9_HI && low64 <= OVERFLOW_MAX_9_LOW64 { return Some(9); } // Do a binary search to find a power to scale by that is less than 9 x = if hi > OVERFLOW_MAX_5_HI { if hi > OVERFLOW_MAX_3_HI { if hi > OVERFLOW_MAX_2_HI { 1 } else { 2 } } else if hi > OVERFLOW_MAX_4_HI { 3 } else { 4 } } else if hi > OVERFLOW_MAX_7_HI { if hi > OVERFLOW_MAX_6_HI { 5 } else { 6 } } else if hi > OVERFLOW_MAX_8_HI { 7 } else { 8 }; // Double check what we've found won't overflow. Otherwise, we go one below. if hi == POWER_OVERFLOW_VALUES[x - 1].data[2] && low64 > POWER_OVERFLOW_VALUES[x - 1].low64() { x -= 1; } // Confirm we've actually resolved things if x as i32 + scale < 0 { None } else { Some(x) } } } // This is a table of the largest values that will not overflow when multiplied // by a given power as represented by the index. static POWER_OVERFLOW_VALUES: [Buf12; 8] = [ Buf12 { data: [2576980377, 2576980377, 429496729], }, Buf12 { data: [687194767, 4123168604, 42949672], }, Buf12 { data: [2645699854, 1271310319, 4294967], }, Buf12 { data: [694066715, 3133608139, 429496], }, Buf12 { data: [2216890319, 2890341191, 42949], }, Buf12 { data: [2369172679, 4154504685, 4294], }, Buf12 { data: [4102387834, 2133437386, 429], }, Buf12 { data: [410238783, 4078814305, 42], }, ]; pub(super) struct Dec64 { pub negative: bool, pub scale: u32, pub hi: u32, pub low64: u64, } impl Dec64 { pub(super) const fn new(d: &Decimal) -> Dec64 { let m = d.mantissa_array3(); if m[1] == 0 { Dec64 { negative: d.is_sign_negative(), scale: d.scale(), hi: m[2], low64: m[0] as u64, } } else { Dec64 { negative: d.is_sign_negative(), scale: d.scale(), hi: m[2], low64: ((m[1] as u64) << 32) | (m[0] as u64), } } } #[inline(always)] pub(super) const fn lo(&self) -> u32 { self.low64 as u32 } #[inline(always)] pub(super) const fn mid(&self) -> u32 { (self.low64 >> 32) as u32 } #[inline(always)] pub(super) const fn high64(&self) -> u64 { (self.low64 >> 32) | ((self.hi as u64) << 32) } pub(super) const fn to_decimal(&self) -> Decimal { Decimal::from_parts( self.low64 as u32, (self.low64 >> 32) as u32, self.hi, self.negative, self.scale, ) } } pub struct Buf16 { pub data: [u32; 4], } impl Buf16 { pub const fn zero() -> Self { Buf16 { data: [0, 0, 0, 0] } } pub const fn low64(&self) -> u64 { ((self.data[1] as u64) << 32) | (self.data[0] as u64) } pub fn set_low64(&mut self, value: u64) { self.data[1] = (value >> 32) as u32; self.data[0] = value as u32; } pub const fn mid64(&self) -> u64 { ((self.data[2] as u64) << 32) | (self.data[1] as u64) } pub fn set_mid64(&mut self, value: u64) { self.data[2] = (value >> 32) as u32; self.data[1] = value as u32; } pub const fn high64(&self) -> u64 { ((self.data[3] as u64) << 32) | (self.data[2] as u64) } pub fn set_high64(&mut self, value: u64) { self.data[3] = (value >> 32) as u32; self.data[2] = value as u32; } } #[derive(Debug)] pub struct Buf24 { pub data: [u32; 6], } impl Buf24 { pub const fn zero() -> Self { Buf24 { data: [0, 0, 0, 0, 0, 0], } } pub const fn low64(&self) -> u64 { ((self.data[1] as u64) << 32) | (self.data[0] as u64) } pub fn set_low64(&mut self, value: u64) { self.data[1] = (value >> 32) as u32; self.data[0] = value as u32; } #[allow(dead_code)] pub const fn mid64(&self) -> u64 { ((self.data[3] as u64) << 32) | (self.data[2] as u64) } pub fn set_mid64(&mut self, value: u64) { self.data[3] = (value >> 32) as u32; self.data[2] = value as u32; } #[allow(dead_code)] pub const fn high64(&self) -> u64 { ((self.data[5] as u64) << 32) | (self.data[4] as u64) } pub fn set_high64(&mut self, value: u64) { self.data[5] = (value >> 32) as u32; self.data[4] = value as u32; } pub const fn upper_word(&self) -> usize { if self.data[5] > 0 { return 5; } if self.data[4] > 0 { return 4; } if self.data[3] > 0 { return 3; } if self.data[2] > 0 { return 2; } if self.data[1] > 0 { return 1; } 0 } // Attempt to rescale the number into 96 bits. If successful, the scale is returned wrapped // in an Option. If it failed due to overflow, we return None. // * `upper` - Index of last non-zero value in self. // * `scale` - Current scale factor for this value. pub fn rescale(&mut self, upper: usize, scale: u32) -> Option { let mut scale = scale as i32; let mut upper = upper; // Determine a rescale target to start with let mut rescale_target = 0i32; if upper > 2 { rescale_target = upper as i32 * 32 - 64 - 1; rescale_target -= self.data[upper].leading_zeros() as i32; rescale_target = ((rescale_target * 77) >> 8) + 1; if rescale_target > scale { return None; } } // Make sure we scale enough to bring it into a valid range if rescale_target < scale - MAX_PRECISION_I32 { rescale_target = scale - MAX_PRECISION_I32; } if rescale_target > 0 { // We're going to keep reducing by powers of 10. So, start by reducing the scale by // that amount. scale -= rescale_target; let mut sticky = 0; let mut remainder = 0; loop { sticky |= remainder; let mut power = if rescale_target > 8 { POWERS_10[9] } else { POWERS_10[rescale_target as usize] }; let high = self.data[upper]; let high_quotient = high / power; remainder = high - high_quotient * power; for item in self.data.iter_mut().rev().skip(6 - upper) { let num = (*item as u64).wrapping_add((remainder as u64) << 32); *item = (num / power as u64) as u32; remainder = (num as u32).wrapping_sub(item.wrapping_mul(power)); } self.data[upper] = high_quotient; // If the high quotient was zero then decrease the upper bound if high_quotient == 0 && upper > 0 { upper -= 1; } if rescale_target > MAX_I32_SCALE { // Scale some more rescale_target -= MAX_I32_SCALE; continue; } // If we fit into 96 bits then we've scaled enough. Otherwise, scale once more. if upper > 2 { if scale == 0 { return None; } // Equivalent to scaling down by 10 rescale_target = 1; scale -= 1; continue; } // Round the final result. power >>= 1; let carried = if power <= remainder { // If we're less than half then we're fine. Otherwise, we round if odd or if the // sticky bit is set. if power < remainder || ((self.data[0] & 1) | sticky) != 0 { // Round up self.data[0] = self.data[0].wrapping_add(1); // Check if we carried self.data[0] == 0 } else { false } } else { false }; // If we carried then propagate through the portions if carried { let mut pos = 0; for (index, value) in self.data.iter_mut().enumerate().skip(1) { pos = index; *value = value.wrapping_add(1); if *value != 0 { break; } } // If we ended up rounding over the 96 bits then we'll try to rescale down (again) if pos > 2 { // Nothing to scale down from will cause overflow if scale == 0 { return None; } // Loop back around using scale of 10. // Reset the sticky bit and remainder before looping. upper = pos; sticky = 0; remainder = 0; rescale_target = 1; scale -= 1; continue; } } break; } } Some(scale as u32) } } rust_decimal-1.17.0/src/ops/div.rs000064400000000000000000000634700072674642500151470ustar 00000000000000use crate::constants::{MAX_PRECISION_I32, POWERS_10}; use crate::decimal::{CalculationResult, Decimal}; use crate::ops::common::{Buf12, Buf16, Dec64}; use core::cmp::Ordering; use core::ops::BitXor; impl Buf12 { // Returns true if successful, else false for an overflow fn add32(&mut self, value: u32) -> Result<(), DivError> { let value = value as u64; let new = self.low64().wrapping_add(value); self.set_low64(new); if new < value { self.data[2] = self.data[2].wrapping_add(1); if self.data[2] == 0 { return Err(DivError::Overflow); } } Ok(()) } // Divide a Decimal union by a 32 bit divisor. // Self is overwritten with the quotient. // Return value is a 32 bit remainder. fn div32(&mut self, divisor: u32) -> u32 { let divisor64 = divisor as u64; // See if we can get by using a simple u64 division if self.data[2] != 0 { let mut temp = self.high64(); let q64 = temp / divisor64; self.set_high64(q64); // Calculate the "remainder" temp = ((temp - q64 * divisor64) << 32) | (self.data[0] as u64); if temp == 0 { return 0; } let q32 = (temp / divisor64) as u32; self.data[0] = q32; ((temp as u32).wrapping_sub(q32.wrapping_mul(divisor))) as u32 } else { // Super easy divisor let low64 = self.low64(); if low64 == 0 { // Nothing to do return 0; } // Do the calc let quotient = low64 / divisor64; self.set_low64(quotient); // Remainder is the leftover that wasn't used (low64.wrapping_sub(quotient.wrapping_mul(divisor64))) as u32 } } // Divide the number by a power constant // Returns true if division was successful fn div32_const(&mut self, pow: u32) -> bool { let pow64 = pow as u64; let high64 = self.high64(); let lo = self.data[0] as u64; let div64: u64 = high64 / pow64; let div = ((((high64 - div64 * pow64) << 32) + lo) / pow64) as u32; if self.data[0] == div.wrapping_mul(pow) { self.set_high64(div64); self.data[0] = div; true } else { false } } } impl Buf16 { // Does a partial divide with a 64 bit divisor. The divisor in this case must be 64 bits // otherwise various assumptions fail (e.g. 32 bit quotient). // To assist, the upper 64 bits must be greater than the divisor for this to succeed. // Consequently, it will return the quotient as a 32 bit number and overwrite self with the // 64 bit remainder. pub(super) fn partial_divide_64(&mut self, divisor: u64) -> u32 { // We make this assertion here, however below we pivot based on the data debug_assert!(divisor > self.mid64()); // If we have an empty high bit, then divisor must be greater than the dividend due to // the assumption that the divisor REQUIRES 64 bits. if self.data[2] == 0 { let low64 = self.low64(); if low64 < divisor { // We can't divide at at all so result is 0. The dividend remains untouched since // the full amount is the remainder. return 0; } let quotient = low64 / divisor; self.set_low64(low64 - (quotient * divisor)); return quotient as u32; } // Do a simple check to see if the hi portion of the dividend is greater than the hi // portion of the divisor. let divisor_hi32 = (divisor >> 32) as u32; if self.data[2] >= divisor_hi32 { // We know that the divisor goes into this at MOST u32::max times. // So we kick things off, with that assumption let mut low64 = self.low64(); low64 = low64.wrapping_sub(divisor << 32).wrapping_add(divisor); let mut quotient = u32::MAX; // If we went negative then keep adding it back in loop { if low64 < divisor { break; } quotient = quotient.wrapping_sub(1); low64 = low64.wrapping_add(divisor); } self.set_low64(low64); return quotient; } let mid64 = self.mid64(); let divisor_hi32_64 = divisor_hi32 as u64; if mid64 < divisor_hi32_64 as u64 { // similar situation as above where we've got nothing left to divide return 0; } let mut quotient = mid64 / divisor_hi32_64; let mut remainder = self.data[0] as u64 | ((mid64 - quotient * divisor_hi32_64) << 32); // Do quotient * lo divisor let product = quotient * (divisor & 0xFFFF_FFFF); remainder = remainder.wrapping_sub(product); // Check if we've gone negative. If so, add it back if remainder > product.bitxor(u64::MAX) { loop { quotient = quotient.wrapping_sub(1); remainder = remainder.wrapping_add(divisor); if remainder < divisor { break; } } } self.set_low64(remainder); quotient as u32 } // Does a partial divide with a 96 bit divisor. The divisor in this case must require 96 bits // otherwise various assumptions fail (e.g. 32 bit quotient). pub(super) fn partial_divide_96(&mut self, divisor: &Buf12) -> u32 { let dividend = self.high64(); let divisor_hi = divisor.data[2]; if dividend < divisor_hi as u64 { // Dividend is too small - entire number is remainder return 0; } let mut quo = (dividend / divisor_hi as u64) as u32; let mut remainder = (dividend as u32).wrapping_sub(quo.wrapping_mul(divisor_hi)); // Compute full remainder let mut prod1 = quo as u64 * divisor.data[0] as u64; let mut prod2 = quo as u64 * divisor.data[1] as u64; prod2 += prod1 >> 32; prod1 = (prod1 & 0xFFFF_FFFF) | (prod2 << 32); prod2 >>= 32; let mut num = self.low64(); num = num.wrapping_sub(prod1); remainder = remainder.wrapping_sub(prod2 as u32); // If there are carries make sure they are propagated if num > prod1.bitxor(u64::MAX) { remainder = remainder.wrapping_sub(1); if remainder < (prod2 as u32).bitxor(u32::MAX) { self.set_low64(num); self.data[2] = remainder; return quo; } } else if remainder <= (prod2 as u32).bitxor(u32::MAX) { self.set_low64(num); self.data[2] = remainder; return quo; } // Remainder went negative, add divisor back until it's positive prod1 = divisor.low64(); loop { quo = quo.wrapping_sub(1); num = num.wrapping_add(prod1); remainder = remainder.wrapping_add(divisor_hi); if num < prod1 { // Detected carry. let tmp = remainder; remainder = remainder.wrapping_add(1); if tmp < divisor_hi { break; } } if remainder < divisor_hi { break; // detected carry } } self.set_low64(num); self.data[2] = remainder; quo } } enum DivError { Overflow, } pub(crate) fn div_impl(dividend: &Decimal, divisor: &Decimal) -> CalculationResult { if divisor.is_zero() { return CalculationResult::DivByZero; } if dividend.is_zero() { return CalculationResult::Ok(Decimal::ZERO); } let dividend = Dec64::new(dividend); let divisor = Dec64::new(divisor); // Pre calculate the scale and the sign let mut scale = (dividend.scale as i32) - (divisor.scale as i32); let sign_negative = dividend.negative ^ divisor.negative; // Set up some variables for modification throughout let mut require_unscale = false; let mut quotient = Buf12::from_dec64(÷nd); let divisor = Buf12::from_dec64(&divisor); // Branch depending on the complexity of the divisor if divisor.data[2] | divisor.data[1] == 0 { // We have a simple(r) divisor (32 bit) let divisor32 = divisor.data[0]; // Remainder can only be 32 bits since the divisor is 32 bits. let mut remainder = quotient.div32(divisor32); let mut power_scale = 0; // Figure out how to apply the remainder (i.e. we may have performed something like 10/3 or 8/5) loop { // Remainder is 0 so we have a simple situation if remainder == 0 { // If the scale is positive then we're actually done if scale >= 0 { break; } power_scale = 9usize.min((-scale) as usize); } else { // We may need to normalize later, so set the flag appropriately require_unscale = true; // We have a remainder so we effectively want to try to adjust the quotient and add // the remainder into the quotient. We do this below, however first of all we want // to try to avoid overflowing so we do that check first. let will_overflow = if scale == MAX_PRECISION_I32 { true } else { // Figure out how much we can scale by if let Some(s) = quotient.find_scale(scale) { power_scale = s; } else { return CalculationResult::Overflow; } // If it comes back as 0 (i.e. 10^0 = 1) then we're going to overflow since // we're doing nothing. power_scale == 0 }; if will_overflow { // No more scaling can be done, but remainder is non-zero so we round if necessary. let tmp = remainder << 1; let round = if tmp < remainder { // We round if we wrapped around true } else if tmp >= divisor32 { // If we're greater than the divisor (i.e. underflow) // or if there is a lo bit set, we round tmp > divisor32 || (quotient.data[0] & 0x1) > 0 } else { false }; // If we need to round, try to do so. if round { if let Ok(new_scale) = round_up(&mut quotient, scale) { scale = new_scale; } else { // Overflowed return CalculationResult::Overflow; } } break; } } // Do some scaling let power = POWERS_10[power_scale]; scale += power_scale as i32; // Increase the quotient by the power that was looked up let overflow = increase_scale(&mut quotient, power as u64); if overflow > 0 { return CalculationResult::Overflow; } let remainder_scaled = (remainder as u64) * (power as u64); let remainder_quotient = (remainder_scaled / (divisor32 as u64)) as u32; remainder = (remainder_scaled - remainder_quotient as u64 * divisor32 as u64) as u32; if let Err(DivError::Overflow) = quotient.add32(remainder_quotient) { if let Ok(adj) = unscale_from_overflow(&mut quotient, scale, remainder != 0) { scale = adj; } else { // Still overflowing return CalculationResult::Overflow; } break; } } } else { // We have a divisor greater than 32 bits. Both of these share some quick calculation wins // so we'll do those before branching into separate logic. // The win we can do is shifting the bits to the left as much as possible. We do this to both // the dividend and the divisor to ensure the quotient is not changed. // As a simple contrived example: if we have 4 / 2 then we could bit shift all the way to the // left meaning that the lo portion would have nothing inside of it. Of course, shifting these // left one has the same result (8/4) etc. // The advantage is that we may be able to write off lower portions of the number making things // easier. let mut power_scale = if divisor.data[2] == 0 { divisor.data[1].leading_zeros() } else { divisor.data[2].leading_zeros() } as usize; let mut remainder = Buf16::zero(); remainder.set_low64(quotient.low64() << power_scale); let tmp_high = ((quotient.data[1] as u64) + ((quotient.data[2] as u64) << 32)) >> (32 - power_scale); remainder.set_high64(tmp_high); // Work out the divisor after it's shifted let divisor64 = divisor.low64() << power_scale; // Check if the divisor is 64 bit or the full 96 bits if divisor.data[2] == 0 { // It's 64 bits quotient.data[2] = 0; // Calc mid/lo by shifting accordingly let rem_lo = remainder.data[0]; remainder.data[0] = remainder.data[1]; remainder.data[1] = remainder.data[2]; remainder.data[2] = remainder.data[3]; quotient.data[1] = remainder.partial_divide_64(divisor64); remainder.data[2] = remainder.data[1]; remainder.data[1] = remainder.data[0]; remainder.data[0] = rem_lo; quotient.data[0] = remainder.partial_divide_64(divisor64); loop { let rem_low64 = remainder.low64(); if rem_low64 == 0 { // If the scale is positive then we're actually done if scale >= 0 { break; } power_scale = 9usize.min((-scale) as usize); } else { // We may need to normalize later, so set the flag appropriately require_unscale = true; // We have a remainder so we effectively want to try to adjust the quotient and add // the remainder into the quotient. We do this below, however first of all we want // to try to avoid overflowing so we do that check first. let will_overflow = if scale == MAX_PRECISION_I32 { true } else { // Figure out how much we can scale by if let Some(s) = quotient.find_scale(scale) { power_scale = s; } else { return CalculationResult::Overflow; } // If it comes back as 0 (i.e. 10^0 = 1) then we're going to overflow since // we're doing nothing. power_scale == 0 }; if will_overflow { // No more scaling can be done, but remainder is non-zero so we round if necessary. let mut tmp = remainder.low64(); let round = if (tmp as i64) < 0 { // We round if we wrapped around true } else { tmp <<= 1; if tmp > divisor64 { true } else { tmp == divisor64 && quotient.data[0] & 0x1 != 0 } }; // If we need to round, try to do so. if round { if let Ok(new_scale) = round_up(&mut quotient, scale) { scale = new_scale; } else { // Overflowed return CalculationResult::Overflow; } } break; } } // Do some scaling let power = POWERS_10[power_scale]; scale += power_scale as i32; // Increase the quotient by the power that was looked up let overflow = increase_scale(&mut quotient, power as u64); if overflow > 0 { return CalculationResult::Overflow; } increase_scale64(&mut remainder, power as u64); let tmp = remainder.partial_divide_64(divisor64); if let Err(DivError::Overflow) = quotient.add32(tmp) { if let Ok(adj) = unscale_from_overflow(&mut quotient, scale, remainder.low64() != 0) { scale = adj; } else { // Still overflowing return CalculationResult::Overflow; } break; } } } else { // It's 96 bits // Start by finishing the shift left let divisor_mid = divisor.data[1]; let divisor_hi = divisor.data[2]; let mut divisor = divisor; divisor.set_low64(divisor64); divisor.data[2] = ((divisor_mid as u64 + ((divisor_hi as u64) << 32)) >> (32 - power_scale)) as u32; let quo = remainder.partial_divide_96(&divisor); quotient.set_low64(quo as u64); quotient.data[2] = 0; loop { let mut rem_low64 = remainder.low64(); if rem_low64 == 0 && remainder.data[2] == 0 { // If the scale is positive then we're actually done if scale >= 0 { break; } power_scale = 9usize.min((-scale) as usize); } else { // We may need to normalize later, so set the flag appropriately require_unscale = true; // We have a remainder so we effectively want to try to adjust the quotient and add // the remainder into the quotient. We do this below, however first of all we want // to try to avoid overflowing so we do that check first. let will_overflow = if scale == MAX_PRECISION_I32 { true } else { // Figure out how much we can scale by if let Some(s) = quotient.find_scale(scale) { power_scale = s; } else { return CalculationResult::Overflow; } // If it comes back as 0 (i.e. 10^0 = 1) then we're going to overflow since // we're doing nothing. power_scale == 0 }; if will_overflow { // No more scaling can be done, but remainder is non-zero so we round if necessary. let round = if (remainder.data[2] as i32) < 0 { // We round if we wrapped around true } else { let tmp = remainder.data[1] >> 31; rem_low64 <<= 1; remainder.set_low64(rem_low64); remainder.data[2] = (&remainder.data[2] << 1) + tmp; match remainder.data[2].cmp(&divisor.data[2]) { Ordering::Less => false, Ordering::Equal => { let divisor_low64 = divisor.low64(); if rem_low64 > divisor_low64 { true } else { rem_low64 == divisor_low64 && (quotient.data[0] & 1) != 0 } } Ordering::Greater => true, } }; // If we need to round, try to do so. if round { if let Ok(new_scale) = round_up(&mut quotient, scale) { scale = new_scale; } else { // Overflowed return CalculationResult::Overflow; } } break; } } // Do some scaling let power = POWERS_10[power_scale]; scale += power_scale as i32; // Increase the quotient by the power that was looked up let overflow = increase_scale(&mut quotient, power as u64); if overflow > 0 { return CalculationResult::Overflow; } let mut tmp_remainder = Buf12 { data: [remainder.data[0], remainder.data[1], remainder.data[2]], }; let overflow = increase_scale(&mut tmp_remainder, power as u64); remainder.data[0] = tmp_remainder.data[0]; remainder.data[1] = tmp_remainder.data[1]; remainder.data[2] = tmp_remainder.data[2]; remainder.data[3] = overflow; let tmp = remainder.partial_divide_96(&divisor); if let Err(DivError::Overflow) = quotient.add32(tmp) { if let Ok(adj) = unscale_from_overflow(&mut quotient, scale, (remainder.low64() | remainder.high64()) != 0) { scale = adj; } else { // Still overflowing return CalculationResult::Overflow; } break; } } } } if require_unscale { scale = unscale(&mut quotient, scale); } CalculationResult::Ok(Decimal::from_parts( quotient.data[0], quotient.data[1], quotient.data[2], sign_negative, scale as u32, )) } // Multiply num by power (multiple of 10). Power must be 32 bits. // Returns the overflow, if any fn increase_scale(num: &mut Buf12, power: u64) -> u32 { let mut tmp = (num.data[0] as u64) * power; num.data[0] = tmp as u32; tmp >>= 32; tmp += (num.data[1] as u64) * power; num.data[1] = tmp as u32; tmp >>= 32; tmp += (num.data[2] as u64) * power; num.data[2] = tmp as u32; (tmp >> 32) as u32 } // Multiply num by power (multiple of 10). Power must be 32 bits. fn increase_scale64(num: &mut Buf16, power: u64) { let mut tmp = (num.data[0] as u64) * power; num.data[0] = tmp as u32; tmp >>= 32; tmp += (num.data[1] as u64) * power; num.set_mid64(tmp) } // Adjust the number to deal with an overflow. This function follows being scaled up (i.e. multiplied // by 10, so this effectively tries to reverse that by dividing by 10 then feeding in the high bit // to undo the overflow and rounding instead. // Returns the updated scale. fn unscale_from_overflow(num: &mut Buf12, scale: i32, sticky: bool) -> Result { let scale = scale - 1; if scale < 0 { return Err(DivError::Overflow); } // This function is called when the hi portion has "overflowed" upon adding one and has wrapped // back around to 0. Consequently, we need to "feed" that back in, but also rescaling down // to reverse out the overflow. const HIGH_BIT: u64 = 0x1_0000_0000; num.data[2] = (HIGH_BIT / 10) as u32; // Calc the mid let mut tmp = ((HIGH_BIT % 10) << 32) + (num.data[1] as u64); let mut val = (tmp / 10) as u32; num.data[1] = val; // Calc the lo using a similar method tmp = ((tmp - (val as u64) * 10) << 32) + (num.data[0] as u64); val = (tmp / 10) as u32; num.data[0] = val; // Work out the remainder, and round if we have one (since it doesn't fit) let remainder = (tmp - (val as u64) * 10) as u32; if remainder > 5 || (remainder == 5 && (sticky || num.data[0] & 0x1 > 0)) { let _ = num.add32(1); } Ok(scale) } #[inline] fn round_up(num: &mut Buf12, scale: i32) -> Result { let low64 = num.low64().wrapping_add(1); num.set_low64(low64); if low64 != 0 { return Ok(scale); } let hi = num.data[2].wrapping_add(1); num.data[2] = hi; if hi != 0 { return Ok(scale); } unscale_from_overflow(num, scale, true) } fn unscale(num: &mut Buf12, scale: i32) -> i32 { // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10 we can extract. // We use this as a quick test on whether to try a given power. let mut scale = scale; while num.data[0] == 0 && scale >= 8 && num.div32_const(100000000) { scale -= 8; } if (num.data[0] & 0xF) == 0 && scale >= 4 && num.div32_const(10000) { scale -= 4; } if (num.data[0] & 0x3) == 0 && scale >= 2 && num.div32_const(100) { scale -= 2; } if (num.data[0] & 0x1) == 0 && scale >= 1 && num.div32_const(10) { scale -= 1; } scale } rust_decimal-1.17.0/src/ops/legacy.rs000064400000000000000000000705270072674642500156320ustar 00000000000000use crate::{ constants::{MAX_PRECISION_U32, POWERS_10, U32_MASK}, decimal::{CalculationResult, Decimal}, ops::array::{ add_by_internal, cmp_internal, div_by_u32, is_all_zero, mul_by_u32, mul_part, rescale_internal, shl1_internal, }, }; use core::cmp::Ordering; use num_traits::Zero; pub(crate) fn add_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { // Convert to the same scale let mut my = d1.mantissa_array3(); let mut my_scale = d1.scale(); let mut ot = d2.mantissa_array3(); let mut other_scale = d2.scale(); rescale_to_maximum_scale(&mut my, &mut my_scale, &mut ot, &mut other_scale); let mut final_scale = my_scale.max(other_scale); // Add the items together let my_negative = d1.is_sign_negative(); let other_negative = d2.is_sign_negative(); let mut negative = false; let carry; if !(my_negative ^ other_negative) { negative = my_negative; carry = add_by_internal3(&mut my, &ot); } else { let cmp = cmp_internal(&my, &ot); // -x + y // if x > y then it's negative (i.e. -2 + 1) match cmp { Ordering::Less => { negative = other_negative; sub_by_internal3(&mut ot, &my); my[0] = ot[0]; my[1] = ot[1]; my[2] = ot[2]; } Ordering::Greater => { negative = my_negative; sub_by_internal3(&mut my, &ot); } Ordering::Equal => { // -2 + 2 my[0] = 0; my[1] = 0; my[2] = 0; } } carry = 0; } // If we have a carry we underflowed. // We need to lose some significant digits (if possible) if carry > 0 { if final_scale == 0 { return CalculationResult::Overflow; } // Copy it over to a temp array for modification let mut temp = [my[0], my[1], my[2], carry]; while final_scale > 0 && temp[3] != 0 { div_by_u32(&mut temp, 10); final_scale -= 1; } // If we still have a carry bit then we overflowed if temp[3] > 0 { return CalculationResult::Overflow; } // Copy it back - we're done my[0] = temp[0]; my[1] = temp[1]; my[2] = temp[2]; } CalculationResult::Ok(Decimal::from_parts(my[0], my[1], my[2], negative, final_scale)) } pub(crate) fn sub_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { add_impl(d1, &(-*d2)) } pub(crate) fn div_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { if d2.is_zero() { return CalculationResult::DivByZero; } if d1.is_zero() { return CalculationResult::Ok(Decimal::zero()); } let dividend = d1.mantissa_array3(); let divisor = d2.mantissa_array3(); let mut quotient = [0u32, 0u32, 0u32]; let mut quotient_scale: i32 = d1.scale() as i32 - d2.scale() as i32; // We supply an extra overflow word for each of the dividend and the remainder let mut working_quotient = [dividend[0], dividend[1], dividend[2], 0u32]; let mut working_remainder = [0u32, 0u32, 0u32, 0u32]; let mut working_scale = quotient_scale; let mut remainder_scale = quotient_scale; let mut underflow; loop { div_internal(&mut working_quotient, &mut working_remainder, &divisor); underflow = add_with_scale_internal( &mut quotient, &mut quotient_scale, &mut working_quotient, &mut working_scale, ); // Multiply the remainder by 10 let mut overflow = 0; for part in working_remainder.iter_mut() { let (lo, hi) = mul_part(*part, 10, overflow); *part = lo; overflow = hi; } // Copy temp remainder into the temp quotient section working_quotient.copy_from_slice(&working_remainder); remainder_scale += 1; working_scale = remainder_scale; if underflow || is_all_zero(&working_remainder) { break; } } // If we have a really big number try to adjust the scale to 0 while quotient_scale < 0 { copy_array_diff_lengths(&mut working_quotient, "ient); working_quotient[3] = 0; working_remainder.iter_mut().for_each(|x| *x = 0); // Mul 10 let mut overflow = 0; for part in &mut working_quotient { let (lo, hi) = mul_part(*part, 10, overflow); *part = lo; overflow = hi; } for part in &mut working_remainder { let (lo, hi) = mul_part(*part, 10, overflow); *part = lo; overflow = hi; } if working_quotient[3] == 0 && is_all_zero(&working_remainder) { quotient_scale += 1; quotient[0] = working_quotient[0]; quotient[1] = working_quotient[1]; quotient[2] = working_quotient[2]; } else { // Overflow return CalculationResult::Overflow; } } if quotient_scale > 255 { quotient[0] = 0; quotient[1] = 0; quotient[2] = 0; quotient_scale = 0; } let mut quotient_negative = d1.is_sign_negative() ^ d2.is_sign_negative(); // Check for underflow let mut final_scale: u32 = quotient_scale as u32; if final_scale > MAX_PRECISION_U32 { let mut remainder = 0; // Division underflowed. We must remove some significant digits over using // an invalid scale. while final_scale > MAX_PRECISION_U32 && !is_all_zero("ient) { remainder = div_by_u32(&mut quotient, 10); final_scale -= 1; } if final_scale > MAX_PRECISION_U32 { // Result underflowed so set to zero final_scale = 0; quotient_negative = false; } else if remainder >= 5 { for part in &mut quotient { if remainder == 0 { break; } let digit: u64 = u64::from(*part) + 1; remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 }; *part = (digit & 0xFFFF_FFFF) as u32; } } } CalculationResult::Ok(Decimal::from_parts( quotient[0], quotient[1], quotient[2], quotient_negative, final_scale, )) } pub(crate) fn mul_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { // Early exit if either is zero if d1.is_zero() || d2.is_zero() { return CalculationResult::Ok(Decimal::zero()); } // We are only resulting in a negative if we have mismatched signs let negative = d1.is_sign_negative() ^ d2.is_sign_negative(); // We get the scale of the result by adding the operands. This may be too big, however // we'll correct later let mut final_scale = d1.scale() + d2.scale(); // First of all, if ONLY the lo parts of both numbers is filled // then we can simply do a standard 64 bit calculation. It's a minor // optimization however prevents the need for long form multiplication let my = d1.mantissa_array3(); let ot = d2.mantissa_array3(); if my[1] == 0 && my[2] == 0 && ot[1] == 0 && ot[2] == 0 { // Simply multiplication let mut u64_result = u64_to_array(u64::from(my[0]) * u64::from(ot[0])); // If we're above max precision then this is a very small number if final_scale > MAX_PRECISION_U32 { final_scale -= MAX_PRECISION_U32; // If the number is above 19 then this will equate to zero. // This is because the max value in 64 bits is 1.84E19 if final_scale > 19 { return CalculationResult::Ok(Decimal::zero()); } let mut rem_lo = 0; let mut power; if final_scale > 9 { // Since 10^10 doesn't fit into u32, we divide by 10^10/4 // and multiply the next divisor by 4. rem_lo = div_by_u32(&mut u64_result, 2_500_000_000); power = POWERS_10[final_scale as usize - 10] << 2; } else { power = POWERS_10[final_scale as usize]; } // Divide fits in 32 bits let rem_hi = div_by_u32(&mut u64_result, power); // Round the result. Since the divisor is a power of 10 // we check to see if the remainder is >= 1/2 divisor power >>= 1; if rem_hi >= power && (rem_hi > power || (rem_lo | (u64_result[0] & 0x1)) != 0) { u64_result[0] += 1; } final_scale = MAX_PRECISION_U32; } return CalculationResult::Ok(Decimal::from_parts( u64_result[0], u64_result[1], 0, negative, final_scale, )); } // We're using some of the high bits, so we essentially perform // long form multiplication. We compute the 9 partial products // into a 192 bit result array. // // [my-h][my-m][my-l] // x [ot-h][ot-m][ot-l] // -------------------------------------- // 1. [r-hi][r-lo] my-l * ot-l [0, 0] // 2. [r-hi][r-lo] my-l * ot-m [0, 1] // 3. [r-hi][r-lo] my-m * ot-l [1, 0] // 4. [r-hi][r-lo] my-m * ot-m [1, 1] // 5. [r-hi][r-lo] my-l * ot-h [0, 2] // 6. [r-hi][r-lo] my-h * ot-l [2, 0] // 7. [r-hi][r-lo] my-m * ot-h [1, 2] // 8. [r-hi][r-lo] my-h * ot-m [2, 1] // 9.[r-hi][r-lo] my-h * ot-h [2, 2] let mut product = [0u32, 0u32, 0u32, 0u32, 0u32, 0u32]; // We can perform a minor short circuit here. If the // high portions are both 0 then we can skip portions 5-9 let to = if my[2] == 0 && ot[2] == 0 { 2 } else { 3 }; for (my_index, my_item) in my.iter().enumerate().take(to) { for (ot_index, ot_item) in ot.iter().enumerate().take(to) { let (mut rlo, mut rhi) = mul_part(*my_item, *ot_item, 0); // Get the index for the lo portion of the product for prod in product.iter_mut().skip(my_index + ot_index) { let (res, overflow) = add_part(rlo, *prod); *prod = res; // If we have something in rhi from before then promote that if rhi > 0 { // If we overflowed in the last add, add that with rhi if overflow > 0 { let (nlo, nhi) = add_part(rhi, overflow); rlo = nlo; rhi = nhi; } else { rlo = rhi; rhi = 0; } } else if overflow > 0 { rlo = overflow; rhi = 0; } else { break; } // If nothing to do next round then break out if rlo == 0 { break; } } } } // If our result has used up the high portion of the product // then we either have an overflow or an underflow situation // Overflow will occur if we can't scale it back, whereas underflow // with kick in rounding let mut remainder = 0; while final_scale > 0 && (product[3] != 0 || product[4] != 0 || product[5] != 0) { remainder = div_by_u32(&mut product, 10u32); final_scale -= 1; } // Round up the carry if we need to if remainder >= 5 { for part in product.iter_mut() { if remainder == 0 { break; } let digit: u64 = u64::from(*part) + 1; remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 }; *part = (digit & 0xFFFF_FFFF) as u32; } } // If we're still above max precision then we'll try again to // reduce precision - we may be dealing with a limit of "0" if final_scale > MAX_PRECISION_U32 { // We're in an underflow situation // The easiest way to remove precision is to divide off the result while final_scale > MAX_PRECISION_U32 && !is_all_zero(&product) { div_by_u32(&mut product, 10); final_scale -= 1; } // If we're still at limit then we can't represent any // significant decimal digits and will return an integer only // Can also be invoked while representing 0. if final_scale > MAX_PRECISION_U32 { final_scale = 0; } } else if !(product[3] == 0 && product[4] == 0 && product[5] == 0) { // We're in an overflow situation - we're within our precision bounds // but still have bits in overflow return CalculationResult::Overflow; } CalculationResult::Ok(Decimal::from_parts( product[0], product[1], product[2], negative, final_scale, )) } pub(crate) fn rem_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { if d2.is_zero() { return CalculationResult::DivByZero; } if d1.is_zero() { return CalculationResult::Ok(Decimal::zero()); } // Rescale so comparable let initial_scale = d1.scale(); let mut quotient = d1.mantissa_array3(); let mut quotient_scale = initial_scale; let mut divisor = d2.mantissa_array3(); let mut divisor_scale = d2.scale(); rescale_to_maximum_scale(&mut quotient, &mut quotient_scale, &mut divisor, &mut divisor_scale); // Working is the remainder + the quotient // We use an aligned array since we'll be using it a lot. let mut working_quotient = [quotient[0], quotient[1], quotient[2], 0u32]; let mut working_remainder = [0u32, 0u32, 0u32, 0u32]; div_internal(&mut working_quotient, &mut working_remainder, &divisor); // Round if necessary. This is for semantic correctness, but could feasibly be removed for // performance improvements. if quotient_scale > initial_scale { let mut working = [ working_remainder[0], working_remainder[1], working_remainder[2], working_remainder[3], ]; while quotient_scale > initial_scale { if div_by_u32(&mut working, 10) > 0 { break; } quotient_scale -= 1; working_remainder.copy_from_slice(&working); } } CalculationResult::Ok(Decimal::from_parts( working_remainder[0], working_remainder[1], working_remainder[2], d1.is_sign_negative(), quotient_scale, )) } pub(crate) fn cmp_impl(d1: &Decimal, d2: &Decimal) -> Ordering { // Quick exit if major differences if d1.is_zero() && d2.is_zero() { return Ordering::Equal; } let self_negative = d1.is_sign_negative(); let other_negative = d2.is_sign_negative(); if self_negative && !other_negative { return Ordering::Less; } else if !self_negative && other_negative { return Ordering::Greater; } // If we have 1.23 and 1.2345 then we have // 123 scale 2 and 12345 scale 4 // We need to convert the first to // 12300 scale 4 so we can compare equally let left: &Decimal; let right: &Decimal; if self_negative && other_negative { // Both are negative, so reverse cmp left = d2; right = d1; } else { left = d1; right = d2; } let mut left_scale = left.scale(); let mut right_scale = right.scale(); let mut left_raw = left.mantissa_array3(); let mut right_raw = right.mantissa_array3(); if left_scale == right_scale { // Fast path for same scale if left_raw[2] != right_raw[2] { return left_raw[2].cmp(&right_raw[2]); } if left_raw[1] != right_raw[1] { return left_raw[1].cmp(&right_raw[1]); } return left_raw[0].cmp(&right_raw[0]); } // Rescale and compare rescale_to_maximum_scale(&mut left_raw, &mut left_scale, &mut right_raw, &mut right_scale); cmp_internal(&left_raw, &right_raw) } #[inline] fn add_part(left: u32, right: u32) -> (u32, u32) { let added = u64::from(left) + u64::from(right); ((added & U32_MASK) as u32, (added >> 32 & U32_MASK) as u32) } #[inline(always)] fn sub_by_internal3(value: &mut [u32; 3], by: &[u32; 3]) { let mut overflow = 0; let vl = value.len(); for i in 0..vl { let part = (0x1_0000_0000u64 + u64::from(value[i])) - (u64::from(by[i]) + overflow); value[i] = part as u32; overflow = 1 - (part >> 32); } } fn div_internal(quotient: &mut [u32; 4], remainder: &mut [u32; 4], divisor: &[u32; 3]) { // There are a couple of ways to do division on binary numbers: // 1. Using long division // 2. Using the complement method // ref: http://paulmason.me/dividing-binary-numbers-part-2/ // The complement method basically keeps trying to subtract the // divisor until it can't anymore and placing the rest in remainder. let mut complement = [ divisor[0] ^ 0xFFFF_FFFF, divisor[1] ^ 0xFFFF_FFFF, divisor[2] ^ 0xFFFF_FFFF, 0xFFFF_FFFF, ]; // Add one onto the complement add_one_internal4(&mut complement); // Make sure the remainder is 0 remainder.iter_mut().for_each(|x| *x = 0); // If we have nothing in our hi+ block then shift over till we do let mut blocks_to_process = 0; while blocks_to_process < 4 && quotient[3] == 0 { // memcpy would be useful here quotient[3] = quotient[2]; quotient[2] = quotient[1]; quotient[1] = quotient[0]; quotient[0] = 0; // Increment the counter blocks_to_process += 1; } // Let's try and do the addition... let mut block = blocks_to_process << 5; let mut working = [0u32, 0u32, 0u32, 0u32]; while block < 128 { // << 1 for quotient AND remainder. Moving the carry from the quotient to the bottom of the // remainder. let carry = shl1_internal(quotient, 0); shl1_internal(remainder, carry); // Copy the remainder of working into sub working.copy_from_slice(remainder); // Add the remainder with the complement add_by_internal(&mut working, &complement); // Check for the significant bit - move over to the quotient // as necessary if (working[3] & 0x8000_0000) == 0 { remainder.copy_from_slice(&working); quotient[0] |= 1; } // Increment our pointer block += 1; } } #[inline] fn copy_array_diff_lengths(into: &mut [u32], from: &[u32]) { for i in 0..into.len() { if i >= from.len() { break; } into[i] = from[i]; } } #[inline] fn add_one_internal4(value: &mut [u32; 4]) -> u32 { let mut carry: u64 = 1; // Start with one, since adding one let mut sum: u64; for i in value.iter_mut() { sum = (*i as u64) + carry; *i = (sum & U32_MASK) as u32; carry = sum >> 32; } carry as u32 } #[inline] fn add_by_internal3(value: &mut [u32; 3], by: &[u32; 3]) -> u32 { let mut carry: u32 = 0; let bl = by.len(); for i in 0..bl { let res1 = value[i].overflowing_add(by[i]); let res2 = res1.0.overflowing_add(carry); value[i] = res2.0; carry = (res1.1 | res2.1) as u32; } carry } #[inline] const fn u64_to_array(value: u64) -> [u32; 2] { [(value & U32_MASK) as u32, (value >> 32 & U32_MASK) as u32] } fn add_with_scale_internal( quotient: &mut [u32; 3], quotient_scale: &mut i32, working_quotient: &mut [u32; 4], working_scale: &mut i32, ) -> bool { // Add quotient and the working (i.e. quotient = quotient + working) if is_all_zero(quotient) { // Quotient is zero so we can just copy the working quotient in directly // First, make sure they are both 96 bit. while working_quotient[3] != 0 { div_by_u32(working_quotient, 10); *working_scale -= 1; } copy_array_diff_lengths(quotient, working_quotient); *quotient_scale = *working_scale; return false; } if is_all_zero(working_quotient) { return false; } // We have ensured that our working is not zero so we should do the addition // If our two quotients are different then // try to scale down the one with the bigger scale let mut temp3 = [0u32, 0u32, 0u32]; let mut temp4 = [0u32, 0u32, 0u32, 0u32]; if *quotient_scale != *working_scale { // TODO: Remove necessity for temp (without performance impact) fn div_by_10(target: &mut [u32], temp: &mut [u32], scale: &mut i32, target_scale: i32) { // Copy to the temp array temp.copy_from_slice(target); // divide by 10 until target scale is reached while *scale > target_scale { let remainder = div_by_u32(temp, 10); if remainder == 0 { *scale -= 1; target.copy_from_slice(temp); } else { break; } } } if *quotient_scale < *working_scale { div_by_10(working_quotient, &mut temp4, working_scale, *quotient_scale); } else { div_by_10(quotient, &mut temp3, quotient_scale, *working_scale); } } // If our two quotients are still different then // try to scale up the smaller scale if *quotient_scale != *working_scale { // TODO: Remove necessity for temp (without performance impact) fn mul_by_10(target: &mut [u32], temp: &mut [u32], scale: &mut i32, target_scale: i32) { temp.copy_from_slice(target); let mut overflow = 0; // Multiply by 10 until target scale reached or overflow while *scale < target_scale && overflow == 0 { overflow = mul_by_u32(temp, 10); if overflow == 0 { // Still no overflow *scale += 1; target.copy_from_slice(temp); } } } if *quotient_scale > *working_scale { mul_by_10(working_quotient, &mut temp4, working_scale, *quotient_scale); } else { mul_by_10(quotient, &mut temp3, quotient_scale, *working_scale); } } // If our two quotients are still different then // try to scale down the one with the bigger scale // (ultimately losing significant digits) if *quotient_scale != *working_scale { // TODO: Remove necessity for temp (without performance impact) fn div_by_10_lossy(target: &mut [u32], temp: &mut [u32], scale: &mut i32, target_scale: i32) { temp.copy_from_slice(target); // divide by 10 until target scale is reached while *scale > target_scale { div_by_u32(temp, 10); *scale -= 1; target.copy_from_slice(temp); } } if *quotient_scale < *working_scale { div_by_10_lossy(working_quotient, &mut temp4, working_scale, *quotient_scale); } else { div_by_10_lossy(quotient, &mut temp3, quotient_scale, *working_scale); } } // If quotient or working are zero we have an underflow condition if is_all_zero(quotient) || is_all_zero(working_quotient) { // Underflow return true; } else { // Both numbers have the same scale and can be added. // We just need to know whether we can fit them in let mut underflow = false; let mut temp = [0u32, 0u32, 0u32]; while !underflow { temp.copy_from_slice(quotient); // Add the working quotient let overflow = add_by_internal(&mut temp, working_quotient); if overflow == 0 { // addition was successful quotient.copy_from_slice(&temp); break; } else { // addition overflowed - remove significant digits and try again div_by_u32(quotient, 10); *quotient_scale -= 1; div_by_u32(working_quotient, 10); *working_scale -= 1; // Check for underflow underflow = is_all_zero(quotient) || is_all_zero(working_quotient); } } if underflow { return true; } } false } /// Rescales the given decimals to equivalent scales. /// It will firstly try to scale both the left and the right side to /// the maximum scale of left/right. If it is unable to do that it /// will try to reduce the accuracy of the other argument. /// e.g. with 1.23 and 2.345 it'll rescale the first arg to 1.230 #[inline(always)] fn rescale_to_maximum_scale(left: &mut [u32; 3], left_scale: &mut u32, right: &mut [u32; 3], right_scale: &mut u32) { if left_scale == right_scale { // Nothing to do return; } if is_all_zero(left) { *left_scale = *right_scale; return; } else if is_all_zero(right) { *right_scale = *left_scale; return; } if left_scale > right_scale { rescale_internal(right, right_scale, *left_scale); if right_scale != left_scale { rescale_internal(left, left_scale, *right_scale); } } else { rescale_internal(left, left_scale, *right_scale); if right_scale != left_scale { rescale_internal(right, right_scale, *left_scale); } } } #[cfg(test)] mod test { // Tests on private methods. // // All public tests should go under `tests/`. use super::*; use crate::prelude::*; #[test] fn it_can_rescale_to_maximum_scale() { fn extract(value: &str) -> ([u32; 3], u32) { let v = Decimal::from_str(value).unwrap(); (v.mantissa_array3(), v.scale()) } let tests = &[ ("1", "1", "1", "1"), ("1", "1.0", "1.0", "1.0"), ("1", "1.00000", "1.00000", "1.00000"), ("1", "1.0000000000", "1.0000000000", "1.0000000000"), ( "1", "1.00000000000000000000", "1.00000000000000000000", "1.00000000000000000000", ), ("1.1", "1.1", "1.1", "1.1"), ("1.1", "1.10000", "1.10000", "1.10000"), ("1.1", "1.1000000000", "1.1000000000", "1.1000000000"), ( "1.1", "1.10000000000000000000", "1.10000000000000000000", "1.10000000000000000000", ), ( "0.6386554621848739495798319328", "11.815126050420168067226890757", "0.638655462184873949579831933", "11.815126050420168067226890757", ), ( "0.0872727272727272727272727272", // Scale 28 "843.65000000", // Scale 8 "0.0872727272727272727272727", // 25 "843.6500000000000000000000000", // 25 ), ]; for &(left_raw, right_raw, expected_left, expected_right) in tests { // Left = the value to rescale // Right = the new scale we're scaling to // Expected = the expected left value after rescale let (expected_left, expected_lscale) = extract(expected_left); let (expected_right, expected_rscale) = extract(expected_right); let (mut left, mut left_scale) = extract(left_raw); let (mut right, mut right_scale) = extract(right_raw); rescale_to_maximum_scale(&mut left, &mut left_scale, &mut right, &mut right_scale); assert_eq!(left, expected_left); assert_eq!(left_scale, expected_lscale); assert_eq!(right, expected_right); assert_eq!(right_scale, expected_rscale); // Also test the transitive case let (mut left, mut left_scale) = extract(left_raw); let (mut right, mut right_scale) = extract(right_raw); rescale_to_maximum_scale(&mut right, &mut right_scale, &mut left, &mut left_scale); assert_eq!(left, expected_left); assert_eq!(left_scale, expected_lscale); assert_eq!(right, expected_right); assert_eq!(right_scale, expected_rscale); } } } rust_decimal-1.17.0/src/ops/mul.rs000064400000000000000000000134770072674642500151640ustar 00000000000000use crate::constants::{BIG_POWERS_10, MAX_I64_SCALE, MAX_PRECISION_U32, U32_MAX}; use crate::decimal::{CalculationResult, Decimal}; use crate::ops::common::Buf24; pub(crate) fn mul_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { if d1.is_zero() || d2.is_zero() { // We should think about this - does zero need to maintain precision? This treats it like // an absolute which I think is ok, especially since we have is_zero() functions etc. return CalculationResult::Ok(Decimal::ZERO); } let mut scale = d1.scale() + d2.scale(); let negative = d1.is_sign_negative() ^ d2.is_sign_negative(); let mut product = Buf24::zero(); // See if we can optimize this calculation depending on whether the hi bits are set if d1.hi() | d1.mid() == 0 { if d2.hi() | d2.mid() == 0 { // We're multiplying two 32 bit integers, so we can take some liberties to optimize this. let mut low64 = d1.lo() as u64 * d2.lo() as u64; if scale > MAX_PRECISION_U32 { // We've exceeded maximum scale so we need to start reducing the precision (aka // rounding) until we have something that fits. // If we're too big then we effectively round to zero. if scale > MAX_PRECISION_U32 + MAX_I64_SCALE { return CalculationResult::Ok(Decimal::ZERO); } scale -= MAX_PRECISION_U32 + 1; let mut power = BIG_POWERS_10[scale as usize]; let tmp = low64 / power; let remainder = low64 - tmp * power; low64 = tmp; // Round the result. Since the divisor was a power of 10, it's always even. power >>= 1; if remainder >= power && (remainder > power || (low64 as u32 & 1) > 0) { low64 += 1; } scale = MAX_PRECISION_U32; } // Early exit return CalculationResult::Ok(Decimal::from_parts( low64 as u32, (low64 >> 32) as u32, 0, negative, scale, )); } // We know that the left hand side is just 32 bits but the right hand side is either // 64 or 96 bits. mul_by_32bit_lhs(d1.lo() as u64, d2, &mut product); } else if d2.mid() | d2.hi() == 0 { // We know that the right hand side is just 32 bits. mul_by_32bit_lhs(d2.lo() as u64, d1, &mut product); } else { // We know we're not dealing with simple 32 bit operands on either side. // We compute and accumulate the 9 partial products using long multiplication // 1: ll * rl let mut tmp = d1.lo() as u64 * d2.lo() as u64; product.data[0] = tmp as u32; // 2: ll * rm let mut tmp2 = (d1.lo() as u64 * d2.mid() as u64).wrapping_add(tmp >> 32); // 3: lm * rl tmp = d1.mid() as u64 * d2.lo() as u64; tmp = tmp.wrapping_add(tmp2); product.data[1] = tmp as u32; // Detect if carry happened from the wrapping add if tmp < tmp2 { tmp2 = (tmp >> 32) | (1u64 << 32); } else { tmp2 = tmp >> 32; } // 4: lm * rm tmp = (d1.mid() as u64 * d2.mid() as u64) + tmp2; // If the high bit isn't set then we can stop here. Otherwise, we need to continue calculating // using the high bits. if (d1.hi() | d2.hi()) > 0 { // 5. ll * rh tmp2 = d1.lo() as u64 * d2.hi() as u64; tmp = tmp.wrapping_add(tmp2); // Detect if we carried let mut tmp3 = if tmp < tmp2 { 1 } else { 0 }; // 6. lh * rl tmp2 = d1.hi() as u64 * d2.lo() as u64; tmp = tmp.wrapping_add(tmp2); product.data[2] = tmp as u32; // Detect if we carried if tmp < tmp2 { tmp3 += 1; } tmp2 = (tmp3 << 32) | (tmp >> 32); // 7. lm * rh tmp = d1.mid() as u64 * d2.hi() as u64; tmp = tmp.wrapping_add(tmp2); // Check for carry tmp3 = if tmp < tmp2 { 1 } else { 0 }; // 8. lh * rm tmp2 = d1.hi() as u64 * d2.mid() as u64; tmp = tmp.wrapping_add(tmp2); product.data[3] = tmp as u32; // Check for carry if tmp < tmp2 { tmp3 += 1; } tmp = (tmp3 << 32) | (tmp >> 32); // 9. lh * rh product.set_high64(d1.hi() as u64 * d2.hi() as u64 + tmp); } else { product.set_mid64(tmp); } } // We may want to "rescale". This is the case if the mantissa is > 96 bits or if the scale // exceeds the maximum precision. let upper_word = product.upper_word(); if upper_word > 2 || scale > MAX_PRECISION_U32 { scale = if let Some(new_scale) = product.rescale(upper_word, scale) { new_scale } else { return CalculationResult::Overflow; } } CalculationResult::Ok(Decimal::from_parts( product.data[0], product.data[1], product.data[2], negative, scale, )) } #[inline(always)] fn mul_by_32bit_lhs(d1: u64, d2: &Decimal, product: &mut Buf24) { let mut tmp = d1 * d2.lo() as u64; product.data[0] = tmp as u32; tmp = (d1 * d2.mid() as u64).wrapping_add(tmp >> 32); product.data[1] = tmp as u32; tmp >>= 32; // If we're multiplying by a 96 bit integer then continue the calculation if d2.hi() > 0 { tmp = tmp.wrapping_add(d1 * d2.hi() as u64); if tmp > U32_MAX { product.set_mid64(tmp); } else { product.data[2] = tmp as u32; } } else { product.data[2] = tmp as u32; } } rust_decimal-1.17.0/src/ops/rem.rs000064400000000000000000000224650072674642500151470ustar 00000000000000use crate::constants::{MAX_I32_SCALE, MAX_PRECISION_I32, POWERS_10}; use crate::decimal::{CalculationResult, Decimal}; use crate::ops::common::{Buf12, Buf16, Buf24, Dec64}; pub(crate) fn rem_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { if d2.is_zero() { return CalculationResult::DivByZero; } if d1.is_zero() { return CalculationResult::Ok(Decimal::ZERO); } // We handle the structs a bit different here. Firstly, we ignore both the sign/scale of d2. // This is because during a remainder operation we do not care about the sign of the divisor // and only concern ourselves with that of the dividend. let mut d1 = Dec64::new(d1); let d2_scale = d2.scale(); let mut d2 = Buf12::from_decimal(d2); let cmp = crate::ops::cmp::cmp_internal( &d1, &Dec64 { negative: d1.negative, scale: d2_scale, hi: d2.hi(), low64: d2.low64(), }, ); match cmp { core::cmp::Ordering::Equal => { // Same numbers meaning that remainder is zero return CalculationResult::Ok(Decimal::ZERO); } core::cmp::Ordering::Less => { // d1 < d2, e.g. 1/2. This means that the result is the value of d1 return CalculationResult::Ok(d1.to_decimal()); } core::cmp::Ordering::Greater => {} } // At this point we know that the dividend > divisor and that they are both non-zero. let mut scale = d1.scale as i32 - d2_scale as i32; if scale > 0 { // Scale up the divisor loop { let power = if scale >= MAX_I32_SCALE { POWERS_10[9] } else { POWERS_10[scale as usize] } as u64; let mut tmp = d2.lo() as u64 * power; d2.set_lo(tmp as u32); tmp >>= 32; tmp = tmp.wrapping_add((d2.mid() as u64 + ((d2.hi() as u64) << 32)) * power); d2.set_mid(tmp as u32); d2.set_hi((tmp >> 32) as u32); // Keep scaling if there is more to go scale -= MAX_I32_SCALE; if scale <= 0 { break; } } scale = 0; } loop { // If the dividend is smaller than the divisor then try to scale that up first if scale < 0 { let mut quotient = Buf12 { data: [d1.lo(), d1.mid(), d1.hi], }; loop { // Figure out how much we can scale by let power_scale; if let Some(u) = quotient.find_scale(MAX_PRECISION_I32 + scale) { if u >= POWERS_10.len() { power_scale = 9; } else { power_scale = u; } } else { return CalculationResult::Overflow; }; if power_scale == 0 { break; } let power = POWERS_10[power_scale] as u64; scale += power_scale as i32; let mut tmp = quotient.data[0] as u64 * power; quotient.data[0] = tmp as u32; tmp >>= 32; quotient.set_high64(tmp.wrapping_add(quotient.high64().wrapping_mul(power))); if power_scale != 9 { break; } if scale >= 0 { break; } } d1.low64 = quotient.low64(); d1.hi = quotient.data[2]; d1.scale = d2_scale; } // if the high portion is empty then return the modulus of the bottom portion if d1.hi == 0 { d1.low64 %= d2.low64(); return CalculationResult::Ok(d1.to_decimal()); } else if (d2.mid() | d2.hi()) == 0 { let mut tmp = d1.high64(); tmp = ((tmp % d2.lo() as u64) << 32) | (d1.lo() as u64); d1.low64 = tmp % d2.lo() as u64; d1.hi = 0; } else { // Divisor is > 32 bits return rem_full(&d1, &d2, scale); } if scale >= 0 { break; } } CalculationResult::Ok(d1.to_decimal()) } fn rem_full(d1: &Dec64, d2: &Buf12, scale: i32) -> CalculationResult { let mut scale = scale; // First normalize the divisor let shift = if d2.hi() == 0 { d2.mid().leading_zeros() } else { d2.hi().leading_zeros() }; let mut buffer = Buf24::zero(); let mut overflow = 0u32; buffer.set_low64(d1.low64 << shift); buffer.set_mid64(((d1.mid() as u64).wrapping_add((d1.hi as u64) << 32)) >> (32 - shift)); let mut upper = 3; // We start at 3 due to bit shifting while scale < 0 { let power = if -scale >= MAX_I32_SCALE { POWERS_10[9] } else { POWERS_10[-scale as usize] } as u64; let mut tmp64 = buffer.data[0] as u64 * power; buffer.data[0] = tmp64 as u32; for (index, part) in buffer.data.iter_mut().enumerate().skip(1) { if index > upper { break; } tmp64 >>= 32; tmp64 = tmp64.wrapping_add((*part as u64).wrapping_mul(power)); *part = tmp64 as u32; } // If we have overflow then also process that if upper == 6 { tmp64 >>= 32; tmp64 = tmp64.wrapping_add((overflow as u64).wrapping_mul(power)); overflow = tmp64 as u32; } // Make sure the high bit is not set if tmp64 > 0x7FFF_FFFF { upper += 1; if upper > 5 { overflow = (tmp64 >> 32) as u32; } else { buffer.data[upper] = (tmp64 >> 32) as u32; } } scale += MAX_I32_SCALE; } // TODO: Optimize slice logic let mut tmp = Buf16::zero(); let divisor = d2.low64() << shift; if d2.hi() == 0 { // Do some division if upper == 6 { upper -= 1; tmp.data = [buffer.data[4], buffer.data[5], overflow, 0]; tmp.partial_divide_64(divisor); buffer.data[4] = tmp.data[0]; buffer.data[5] = tmp.data[1]; } if upper == 5 { upper -= 1; tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], 0]; tmp.partial_divide_64(divisor); buffer.data[3] = tmp.data[0]; buffer.data[4] = tmp.data[1]; buffer.data[5] = tmp.data[2]; } if upper == 4 { tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], 0]; tmp.partial_divide_64(divisor); buffer.data[2] = tmp.data[0]; buffer.data[3] = tmp.data[1]; buffer.data[4] = tmp.data[2]; } tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], 0]; tmp.partial_divide_64(divisor); buffer.data[1] = tmp.data[0]; buffer.data[2] = tmp.data[1]; buffer.data[3] = tmp.data[2]; tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], 0]; tmp.partial_divide_64(divisor); buffer.data[0] = tmp.data[0]; buffer.data[1] = tmp.data[1]; buffer.data[2] = tmp.data[2]; let low64 = buffer.low64() >> shift; CalculationResult::Ok(Decimal::from_parts( low64 as u32, (low64 >> 32) as u32, 0, d1.negative, d1.scale, )) } else { let divisor_low64 = divisor; let divisor = Buf12 { data: [ divisor_low64 as u32, (divisor_low64 >> 32) as u32, (((d2.mid() as u64) + ((d2.hi() as u64) << 32)) >> (32 - shift)) as u32, ], }; // Do some division if upper == 6 { upper -= 1; tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], overflow]; tmp.partial_divide_96(&divisor); buffer.data[3] = tmp.data[0]; buffer.data[4] = tmp.data[1]; buffer.data[5] = tmp.data[2]; } if upper == 5 { upper -= 1; tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], buffer.data[5]]; tmp.partial_divide_96(&divisor); buffer.data[2] = tmp.data[0]; buffer.data[3] = tmp.data[1]; buffer.data[4] = tmp.data[2]; buffer.data[5] = tmp.data[3]; } if upper == 4 { tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], buffer.data[4]]; tmp.partial_divide_96(&divisor); buffer.data[1] = tmp.data[0]; buffer.data[2] = tmp.data[1]; buffer.data[3] = tmp.data[2]; buffer.data[4] = tmp.data[3]; } tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], buffer.data[3]]; tmp.partial_divide_96(&divisor); buffer.data[0] = tmp.data[0]; buffer.data[1] = tmp.data[1]; buffer.data[2] = tmp.data[2]; buffer.data[3] = tmp.data[3]; let low64 = (buffer.low64() >> shift) + ((buffer.data[2] as u64) << (32 - shift) << 32); CalculationResult::Ok(Decimal::from_parts( low64 as u32, (low64 >> 32) as u32, buffer.data[2] >> shift, d1.negative, d1.scale, )) } } rust_decimal-1.17.0/src/ops.rs000064400000000000000000000020510072674642500143510ustar 00000000000000// This code (in fact, this library) is heavily inspired by the dotnet Decimal number library // implementation. Consequently, a huge thank you for to all the contributors to that project // whose work has also inspired the solutions found here. pub(crate) mod array; #[cfg(feature = "legacy-ops")] mod legacy; #[cfg(feature = "legacy-ops")] pub(crate) use legacy::{add_impl, cmp_impl, div_impl, mul_impl, rem_impl, sub_impl}; #[cfg(not(feature = "legacy-ops"))] mod add; #[cfg(not(feature = "legacy-ops"))] mod cmp; #[cfg(not(feature = "legacy-ops"))] pub(in crate::ops) mod common; #[cfg(not(feature = "legacy-ops"))] mod div; #[cfg(not(feature = "legacy-ops"))] mod mul; #[cfg(not(feature = "legacy-ops"))] mod rem; #[cfg(not(feature = "legacy-ops"))] pub(crate) use add::{add_impl, sub_impl}; #[cfg(not(feature = "legacy-ops"))] pub(crate) use cmp::cmp_impl; #[cfg(not(feature = "legacy-ops"))] pub(crate) use div::div_impl; #[cfg(not(feature = "legacy-ops"))] pub(crate) use mul::mul_impl; #[cfg(not(feature = "legacy-ops"))] pub(crate) use rem::rem_impl; rust_decimal-1.17.0/src/serde.rs000064400000000000000000000271410072674642500146610ustar 00000000000000use crate::Decimal; use alloc::string::ToString; use core::{fmt, str::FromStr}; use num_traits::FromPrimitive; use serde::{self, de::Unexpected}; #[cfg(not(feature = "serde-str"))] impl<'de> serde::Deserialize<'de> for Decimal { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { deserializer.deserialize_any(DecimalVisitor) } } #[cfg(all(feature = "serde-str", not(feature = "serde-float")))] impl<'de> serde::Deserialize<'de> for Decimal { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { deserializer.deserialize_str(DecimalVisitor) } } #[cfg(all(feature = "serde-str", feature = "serde-float"))] impl<'de> serde::Deserialize<'de> for Decimal { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { deserializer.deserialize_f64(DecimalVisitor) } } // It's a shame this needs to be redefined for this feature and not able to be referenced directly #[cfg(feature = "serde-arbitrary-precision")] const DECIMAL_KEY_TOKEN: &str = "$serde_json::private::Number"; struct DecimalVisitor; impl<'de> serde::de::Visitor<'de> for DecimalVisitor { type Value = Decimal; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a Decimal type representing a fixed-point number") } fn visit_i64(self, value: i64) -> Result where E: serde::de::Error, { match Decimal::from_i64(value) { Some(s) => Ok(s), None => Err(E::invalid_value(Unexpected::Signed(value), &self)), } } fn visit_u64(self, value: u64) -> Result where E: serde::de::Error, { match Decimal::from_u64(value) { Some(s) => Ok(s), None => Err(E::invalid_value(Unexpected::Unsigned(value), &self)), } } fn visit_f64(self, value: f64) -> Result where E: serde::de::Error, { Decimal::from_str(&value.to_string()).map_err(|_| E::invalid_value(Unexpected::Float(value), &self)) } fn visit_str(self, value: &str) -> Result where E: serde::de::Error, { Decimal::from_str(value) .or_else(|_| Decimal::from_scientific(value)) .map_err(|_| E::invalid_value(Unexpected::Str(value), &self)) } #[cfg(feature = "serde-arbitrary-precision")] fn visit_map(self, map: A) -> Result where A: serde::de::MapAccess<'de>, { let mut map = map; let value = map.next_key::()?; if value.is_none() { return Err(serde::de::Error::invalid_type(Unexpected::Map, &self)); } let v: DecimalFromString = map.next_value()?; Ok(v.value) } } #[cfg(feature = "serde-arbitrary-precision")] struct DecimalKey; #[cfg(feature = "serde-arbitrary-precision")] impl<'de> serde::de::Deserialize<'de> for DecimalKey { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { struct FieldVisitor; impl<'de> serde::de::Visitor<'de> for FieldVisitor { type Value = (); fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid decimal field") } fn visit_str(self, s: &str) -> Result<(), E> where E: serde::de::Error, { if s == DECIMAL_KEY_TOKEN { Ok(()) } else { Err(serde::de::Error::custom("expected field with custom name")) } } } deserializer.deserialize_identifier(FieldVisitor)?; Ok(DecimalKey) } } #[cfg(feature = "serde-arbitrary-precision")] pub struct DecimalFromString { pub value: Decimal, } #[cfg(feature = "serde-arbitrary-precision")] impl<'de> serde::de::Deserialize<'de> for DecimalFromString { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = DecimalFromString; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("string containing a decimal") } fn visit_str(self, value: &str) -> Result where E: serde::de::Error, { let d = Decimal::from_str(value) .or_else(|_| Decimal::from_scientific(value)) .map_err(serde::de::Error::custom)?; Ok(DecimalFromString { value: d }) } } deserializer.deserialize_str(Visitor) } } #[cfg(not(feature = "serde-float"))] impl serde::Serialize for Decimal { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let value = crate::str::to_str_internal(self, true, None); serializer.serialize_str(value.0.as_ref()) } } #[cfg(all(feature = "serde-float", not(feature = "serde-arbitrary-precision")))] impl serde::Serialize for Decimal { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { use num_traits::ToPrimitive; serializer.serialize_f64(self.to_f64().unwrap()) } } #[cfg(all(feature = "serde-float", feature = "serde-arbitrary-precision"))] impl serde::Serialize for Decimal { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serde_json::Number::from_str(&self.to_string()) .map_err(serde::ser::Error::custom)? .serialize(serializer) } } #[cfg(test)] mod test { use super::*; use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] struct Record { amount: Decimal, } #[test] #[cfg(not(feature = "serde-str"))] fn deserialize_valid_decimal() { let data = [ ("{\"amount\":\"1.234\"}", "1.234"), ("{\"amount\":1234}", "1234"), ("{\"amount\":1234.56}", "1234.56"), ("{\"amount\":\"1.23456e3\"}", "1234.56"), ]; for &(serialized, value) in data.iter() { let result = serde_json::from_str(serialized); assert_eq!( true, result.is_ok(), "expected successful deserialization for {}. Error: {:?}", serialized, result.err().unwrap() ); let record: Record = result.unwrap(); assert_eq!( value, record.amount.to_string(), "expected: {}, actual: {}", value, record.amount.to_string() ); } } #[test] #[cfg(feature = "serde-arbitrary-precision")] fn deserialize_basic_decimal() { let d: Decimal = serde_json::from_str("1.1234127836128763").unwrap(); // Typically, this would not work without this feature enabled due to rounding assert_eq!(d.to_string(), "1.1234127836128763"); } #[test] #[should_panic] fn deserialize_invalid_decimal() { let serialized = "{\"amount\":\"foo\"}"; let _: Record = serde_json::from_str(serialized).unwrap(); } #[test] #[cfg(not(feature = "serde-float"))] fn serialize_decimal() { let record = Record { amount: Decimal::new(1234, 3), }; let serialized = serde_json::to_string(&record).unwrap(); assert_eq!("{\"amount\":\"1.234\"}", serialized); } #[test] #[cfg(not(feature = "serde-float"))] fn serialize_negative_zero() { let record = Record { amount: -Decimal::ZERO }; let serialized = serde_json::to_string(&record).unwrap(); assert_eq!("{\"amount\":\"-0\"}", serialized); } #[test] #[cfg(feature = "serde-float")] fn serialize_decimal() { let record = Record { amount: Decimal::new(1234, 3), }; let serialized = serde_json::to_string(&record).unwrap(); assert_eq!("{\"amount\":1.234}", serialized); } #[test] #[cfg(all(feature = "serde-float", feature = "serde-arbitrary-precision"))] fn serialize_decimal_roundtrip() { let record = Record { // 4.81 is intentionally chosen as it is unrepresentable as a floating point number, meaning this test // would fail if the `serde-arbitrary-precision` was not activated. amount: Decimal::new(481, 2), }; let serialized = serde_json::to_string(&record).unwrap(); assert_eq!("{\"amount\":4.81}", serialized); let deserialized: Record = serde_json::from_str(&serialized).unwrap(); assert_eq!(record.amount, deserialized.amount); } #[test] #[cfg(all(feature = "serde-str", not(feature = "serde-float")))] fn serialize_decimal_roundtrip() { let record = Record { amount: Decimal::new(481, 2), }; let serialized = serde_json::to_string(&record).unwrap(); assert_eq!("{\"amount\":\"4.81\"}", serialized); let deserialized: Record = serde_json::from_str(&serialized).unwrap(); assert_eq!(record.amount, deserialized.amount); } #[test] #[cfg(all(feature = "serde-str", not(feature = "serde-float")))] fn bincode_serialization() { use bincode::{deserialize, serialize}; let data = [ "0", "0.00", "3.14159", "-3.14159", "1234567890123.4567890", "-1234567890123.4567890", "5233.9008808150288439427720175", "-5233.9008808150288439427720175", ]; for &raw in data.iter() { let value = Decimal::from_str(raw).unwrap(); let encoded = serialize(&value).unwrap(); let decoded: Decimal = deserialize(&encoded[..]).unwrap(); assert_eq!(value, decoded); assert_eq!(8usize + raw.len(), encoded.len()); } } #[test] #[cfg(all(feature = "serde-str", feature = "serde-float"))] fn bincode_serialization() { use bincode::{deserialize, serialize}; let data = [ ("0", "0"), ("0.00", "0.00"), ("3.14159", "3.14159"), ("-3.14159", "-3.14159"), ("1234567890123.4567890", "1234567890123.4568"), ("-1234567890123.4567890", "-1234567890123.4568"), ]; for &(value, expected) in data.iter() { let value = Decimal::from_str(value).unwrap(); let expected = Decimal::from_str(expected).unwrap(); let encoded = serialize(&value).unwrap(); let decoded: Decimal = deserialize(&encoded[..]).unwrap(); assert_eq!(expected, decoded); assert_eq!(8usize, encoded.len()); } } #[test] #[cfg(all(feature = "serde-str", not(feature = "serde-float")))] fn bincode_nested_serialization() { // Issue #361 #[derive(Deserialize, Serialize, Debug)] pub struct Foo { value: Decimal, } let s = Foo { value: Decimal::new(-1, 3).round_dp(0), }; let ser = bincode::serialize(&s).unwrap(); let des: Foo = bincode::deserialize(&ser).unwrap(); assert_eq!(des.value, s.value); } } rust_decimal-1.17.0/src/str.rs000064400000000000000000000435650072674642500143770ustar 00000000000000use crate::{ constants::{MAX_PRECISION, MAX_STR_BUFFER_SIZE}, error::Error, ops::array::{add_by_internal, add_one_internal, div_by_u32, is_all_zero, mul_by_10, mul_by_u32}, Decimal, }; use arrayvec::{ArrayString, ArrayVec}; use alloc::{string::String, vec::Vec}; use core::fmt; // impl that doesn't allocate for serialization purposes. pub(crate) fn to_str_internal( value: &Decimal, append_sign: bool, precision: Option, ) -> (ArrayString<[u8; MAX_STR_BUFFER_SIZE]>, Option) { // Get the scale - where we need to put the decimal point let scale = value.scale() as usize; // Convert to a string and manipulate that (neg at front, inject decimal) let mut chars = ArrayVec::<[_; MAX_STR_BUFFER_SIZE]>::new(); let mut working = value.mantissa_array3(); while !is_all_zero(&working) { let remainder = div_by_u32(&mut working, 10u32); chars.push(char::from(b'0' + remainder as u8)); } while scale > chars.len() { chars.push('0'); } let (prec, additional) = match precision { Some(prec) => { let max: usize = MAX_PRECISION.into(); if prec > max { (max, Some(prec - max)) } else { (prec, None) } } None => (scale, None), }; let len = chars.len(); let whole_len = len - scale; let mut rep = ArrayString::new(); // Append the negative sign if necessary while also keeping track of the length of an "empty" string representation let empty_len = if append_sign && value.is_sign_negative() { rep.push('-'); 1 } else { 0 }; for i in 0..whole_len + prec { if i == len - scale { if i == 0 { rep.push('0'); } rep.push('.'); } if i >= len { rep.push('0'); } else { let c = chars[len - i - 1]; rep.push(c); } } // corner case for when we truncated everything in a low fractional if rep.len() == empty_len { rep.push('0'); } (rep, additional) } pub(crate) fn fmt_scientific_notation( value: &Decimal, exponent_symbol: &str, f: &mut fmt::Formatter<'_>, ) -> fmt::Result { #[cfg(not(feature = "std"))] use alloc::string::ToString; // Get the scale - this is the e value. With multiples of 10 this may get bigger. let mut exponent = -(value.scale() as isize); // Convert the integral to a string let mut chars = Vec::new(); let mut working = value.mantissa_array3(); while !is_all_zero(&working) { let remainder = div_by_u32(&mut working, 10u32); chars.push(char::from(b'0' + remainder as u8)); } // First of all, apply scientific notation rules. That is: // 1. If non-zero digit comes first, move decimal point left so that e is a positive integer // 2. If decimal point comes first, move decimal point right until after the first non-zero digit // Since decimal notation naturally lends itself this way, we just need to inject the decimal // point in the right place and adjust the exponent accordingly. let len = chars.len(); let mut rep; if len > 1 { if chars.iter().take(len - 1).all(|c| *c == '0') { // Chomp off the zero's. rep = chars.iter().skip(len - 1).collect::(); } else { chars.insert(len - 1, '.'); rep = chars.iter().rev().collect::(); } exponent += (len - 1) as isize; } else { rep = chars.iter().collect::(); } rep.push_str(exponent_symbol); rep.push_str(&exponent.to_string()); f.pad_integral(value.is_sign_positive(), "", &rep) } // dedicated implementation for the most common case. pub(crate) fn parse_str_radix_10(str: &str) -> Result { if str.is_empty() { return Err(Error::from("Invalid decimal: empty")); } let mut offset = 0; let mut len = str.len(); let bytes = str.as_bytes(); let mut negative = false; // assume positive // handle the sign if bytes[offset] == b'-' { negative = true; // leading minus means negative offset += 1; len -= 1; } else if bytes[offset] == b'+' { // leading + allowed offset += 1; len -= 1; } // should now be at numeric part of the significand let mut digits_before_dot: i32 = -1; // digits before '.', -1 if no '.' let mut coeff = ArrayVec::<[_; MAX_STR_BUFFER_SIZE]>::new(); // integer significand array let mut maybe_round = false; while len > 0 { let b = bytes[offset]; match b { b'0'..=b'9' => { coeff.push(u32::from(b - b'0')); offset += 1; len -= 1; // If the coefficient is longer than the max, exit early if coeff.len() as u32 > 28 { maybe_round = true; break; } } b'.' => { if digits_before_dot >= 0 { return Err(Error::from("Invalid decimal: two decimal points")); } digits_before_dot = coeff.len() as i32; offset += 1; len -= 1; } b'_' => { // Must start with a number... if coeff.is_empty() { return Err(Error::from("Invalid decimal: must start lead with a number")); } offset += 1; len -= 1; } _ => return Err(Error::from("Invalid decimal: unknown character")), } } // If we exited before the end of the string then do some rounding if necessary if maybe_round && offset < bytes.len() { let next_byte = bytes[offset]; let digit = match next_byte { b'0'..=b'9' => u32::from(next_byte - b'0'), b'_' => 0, b'.' => { // Still an error if we have a second dp if digits_before_dot >= 0 { return Err(Error::from("Invalid decimal: two decimal points")); } 0 } _ => return Err(Error::from("Invalid decimal: unknown character")), }; // Round at midpoint if digit >= 5 { let mut index = coeff.len() - 1; loop { let new_digit = coeff[index] + 1; if new_digit <= 9 { coeff[index] = new_digit; break; } else { coeff[index] = 0; if index == 0 { coeff.insert(0, 1u32); digits_before_dot += 1; coeff.pop(); break; } } index -= 1; } } } // here when no characters left if coeff.is_empty() { return Err(Error::from("Invalid decimal: no digits found")); } let mut scale = if digits_before_dot >= 0 { // we had a decimal place so set the scale (coeff.len() as u32) - (digits_before_dot as u32) } else { 0 }; let mut data = [0u32, 0u32, 0u32]; let mut tmp = [0u32, 0u32, 0u32]; let len = coeff.len(); for (i, digit) in coeff.iter().enumerate() { // If the data is going to overflow then we should go into recovery mode tmp[0] = data[0]; tmp[1] = data[1]; tmp[2] = data[2]; let overflow = mul_by_10(&mut tmp); if overflow > 0 { // This means that we have more data to process, that we're not sure what to do with. // This may or may not be an issue - depending on whether we're past a decimal point // or not. if (i as i32) < digits_before_dot && i + 1 < len { return Err(Error::from("Invalid decimal: overflow from too many digits")); } if *digit >= 5 { let carry = add_one_internal(&mut data); if carry > 0 { // Highly unlikely scenario which is more indicative of a bug return Err(Error::from("Invalid decimal: overflow when rounding")); } } // We're also one less digit so reduce the scale let diff = (len - i) as u32; if diff > scale { return Err(Error::from("Invalid decimal: overflow from scale mismatch")); } scale -= diff; break; } else { data[0] = tmp[0]; data[1] = tmp[1]; data[2] = tmp[2]; let carry = add_by_internal(&mut data, &[*digit]); if carry > 0 { // Highly unlikely scenario which is more indicative of a bug return Err(Error::from("Invalid decimal: overflow from carry")); } } } Ok(Decimal::from_parts(data[0], data[1], data[2], negative, scale)) } pub(crate) fn parse_str_radix_n(str: &str, radix: u32) -> Result { if str.is_empty() { return Err(Error::from("Invalid decimal: empty")); } if radix < 2 { return Err(Error::from("Unsupported radix < 2")); } if radix > 36 { // As per trait documentation return Err(Error::from("Unsupported radix > 36")); } let mut offset = 0; let mut len = str.len(); let bytes = str.as_bytes(); let mut negative = false; // assume positive // handle the sign if bytes[offset] == b'-' { negative = true; // leading minus means negative offset += 1; len -= 1; } else if bytes[offset] == b'+' { // leading + allowed offset += 1; len -= 1; } // should now be at numeric part of the significand let mut digits_before_dot: i32 = -1; // digits before '.', -1 if no '.' let mut coeff = ArrayVec::<[_; 96]>::new(); // integer significand array // Supporting different radix let (max_n, max_alpha_lower, max_alpha_upper) = if radix <= 10 { (b'0' + (radix - 1) as u8, 0, 0) } else { let adj = (radix - 11) as u8; (b'9', adj + b'a', adj + b'A') }; // Estimate the max precision. All in all, it needs to fit into 96 bits. // Rather than try to estimate, I've included the constants directly in here. We could, // perhaps, replace this with a formula if it's faster - though it does appear to be log2. let estimated_max_precision = match radix { 2 => 96, 3 => 61, 4 => 48, 5 => 42, 6 => 38, 7 => 35, 8 => 32, 9 => 31, 10 => 28, 11 => 28, 12 => 27, 13 => 26, 14 => 26, 15 => 25, 16 => 24, 17 => 24, 18 => 24, 19 => 23, 20 => 23, 21 => 22, 22 => 22, 23 => 22, 24 => 21, 25 => 21, 26 => 21, 27 => 21, 28 => 20, 29 => 20, 30 => 20, 31 => 20, 32 => 20, 33 => 20, 34 => 19, 35 => 19, 36 => 19, _ => return Err(Error::from("Unsupported radix")), }; let mut maybe_round = false; while len > 0 { let b = bytes[offset]; match b { b'0'..=b'9' => { if b > max_n { return Err(Error::from("Invalid decimal: invalid character")); } coeff.push(u32::from(b - b'0')); offset += 1; len -= 1; // If the coefficient is longer than the max, exit early if coeff.len() as u32 > estimated_max_precision { maybe_round = true; break; } } b'a'..=b'z' => { if b > max_alpha_lower { return Err(Error::from("Invalid decimal: invalid character")); } coeff.push(u32::from(b - b'a') + 10); offset += 1; len -= 1; if coeff.len() as u32 > estimated_max_precision { maybe_round = true; break; } } b'A'..=b'Z' => { if b > max_alpha_upper { return Err(Error::from("Invalid decimal: invalid character")); } coeff.push(u32::from(b - b'A') + 10); offset += 1; len -= 1; if coeff.len() as u32 > estimated_max_precision { maybe_round = true; break; } } b'.' => { if digits_before_dot >= 0 { return Err(Error::from("Invalid decimal: two decimal points")); } digits_before_dot = coeff.len() as i32; offset += 1; len -= 1; } b'_' => { // Must start with a number... if coeff.is_empty() { return Err(Error::from("Invalid decimal: must start lead with a number")); } offset += 1; len -= 1; } _ => return Err(Error::from("Invalid decimal: unknown character")), } } // If we exited before the end of the string then do some rounding if necessary if maybe_round && offset < bytes.len() { let next_byte = bytes[offset]; let digit = match next_byte { b'0'..=b'9' => { if next_byte > max_n { return Err(Error::from("Invalid decimal: invalid character")); } u32::from(next_byte - b'0') } b'a'..=b'z' => { if next_byte > max_alpha_lower { return Err(Error::from("Invalid decimal: invalid character")); } u32::from(next_byte - b'a') + 10 } b'A'..=b'Z' => { if next_byte > max_alpha_upper { return Err(Error::from("Invalid decimal: invalid character")); } u32::from(next_byte - b'A') + 10 } b'_' => 0, b'.' => { // Still an error if we have a second dp if digits_before_dot >= 0 { return Err(Error::from("Invalid decimal: two decimal points")); } 0 } _ => return Err(Error::from("Invalid decimal: unknown character")), }; // Round at midpoint let midpoint = if radix & 0x1 == 1 { radix / 2 } else { (radix + 1) / 2 }; if digit >= midpoint { let mut index = coeff.len() - 1; loop { let new_digit = coeff[index] + 1; if new_digit <= 9 { coeff[index] = new_digit; break; } else { coeff[index] = 0; if index == 0 { coeff.insert(0, 1u32); digits_before_dot += 1; coeff.pop(); break; } } index -= 1; } } } // here when no characters left if coeff.is_empty() { return Err(Error::from("Invalid decimal: no digits found")); } let mut scale = if digits_before_dot >= 0 { // we had a decimal place so set the scale (coeff.len() as u32) - (digits_before_dot as u32) } else { 0 }; // Parse this using specified radix let mut data = [0u32, 0u32, 0u32]; let mut tmp = [0u32, 0u32, 0u32]; let len = coeff.len(); for (i, digit) in coeff.iter().enumerate() { // If the data is going to overflow then we should go into recovery mode tmp[0] = data[0]; tmp[1] = data[1]; tmp[2] = data[2]; let overflow = mul_by_u32(&mut tmp, radix); if overflow > 0 { // This means that we have more data to process, that we're not sure what to do with. // This may or may not be an issue - depending on whether we're past a decimal point // or not. if (i as i32) < digits_before_dot && i + 1 < len { return Err(Error::from("Invalid decimal: overflow from too many digits")); } if *digit >= 5 { let carry = add_one_internal(&mut data); if carry > 0 { // Highly unlikely scenario which is more indicative of a bug return Err(Error::from("Invalid decimal: overflow when rounding")); } } // We're also one less digit so reduce the scale let diff = (len - i) as u32; if diff > scale { return Err(Error::from("Invalid decimal: overflow from scale mismatch")); } scale -= diff; break; } else { data[0] = tmp[0]; data[1] = tmp[1]; data[2] = tmp[2]; let carry = add_by_internal(&mut data, &[*digit]); if carry > 0 { // Highly unlikely scenario which is more indicative of a bug return Err(Error::from("Invalid decimal: overflow from carry")); } } } Ok(Decimal::from_parts(data[0], data[1], data[2], negative, scale)) } #[cfg(test)] mod test { use crate::Decimal; use arrayvec::ArrayString; use core::{fmt::Write, str::FromStr}; #[test] fn display_does_not_overflow_max_capacity() { let num = Decimal::from_str("1.2").unwrap(); let mut buffer = ArrayString::<[u8; 64]>::new(); let _ = buffer.write_fmt(format_args!("{:.31}", num)).unwrap(); assert_eq!("1.2000000000000000000000000000000", buffer.as_str()); } } rust_decimal-1.17.0/tests/decimal_tests.rs000064400000000000000000004773300072674642500167630ustar 00000000000000mod macros; use core::{ cmp::Ordering::*, convert::{TryFrom, TryInto}, str::FromStr, }; use num_traits::{Signed, ToPrimitive}; use rust_decimal::{Decimal, Error, RoundingStrategy}; #[test] #[cfg(feature = "c-repr")] fn layout_is_correct() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); } #[test] fn it_can_extract_the_mantissa() { let tests = [ ("1", 1i128, 0), ("1.123456", 1123456i128, 6), ("-0.123456", -123456i128, 6), ]; for &(input, mantissa, scale) in &tests { let num = Decimal::from_str(input).unwrap(); assert_eq!(num.mantissa(), mantissa, "Mantissa for {}", input); assert_eq!(num.scale(), scale, "Scale for {}", input); } } // Parsing #[test] fn it_creates_a_new_negative_decimal() { let a = Decimal::new(-100, 2); assert_eq!(a.is_sign_negative(), true); assert_eq!(a.scale(), 2); assert_eq!("-1.00", a.to_string()); } #[test] fn it_creates_a_new_decimal_using_numeric_boundaries() { let a = Decimal::new(i64::MAX, 2); assert_eq!(a.is_sign_negative(), false); assert_eq!(a.scale(), 2); assert_eq!("92233720368547758.07", a.to_string()); let b = Decimal::new(i64::MIN, 2); assert_eq!(b.is_sign_negative(), true); assert_eq!(b.scale(), 2); assert_eq!("-92233720368547758.08", b.to_string()); } #[test] fn it_parses_empty_string() { assert!(Decimal::from_str("").is_err()); assert!(Decimal::from_str(" ").is_err()); } #[test] fn it_parses_positive_int_string() { let a = Decimal::from_str("233").unwrap(); assert_eq!(a.is_sign_negative(), false); assert_eq!(a.scale(), 0); assert_eq!("233", a.to_string()); } #[test] fn it_parses_negative_int_string() { let a = Decimal::from_str("-233").unwrap(); assert_eq!(a.is_sign_negative(), true); assert_eq!(a.scale(), 0); assert_eq!("-233", a.to_string()); } #[test] fn it_parses_positive_float_string() { let a = Decimal::from_str("233.323223").unwrap(); assert_eq!(a.is_sign_negative(), false); assert_eq!(a.scale(), 6); assert_eq!("233.323223", a.to_string()); } #[test] fn it_parses_negative_float_string() { let a = Decimal::from_str("-233.43343").unwrap(); assert_eq!(a.is_sign_negative(), true); assert_eq!(a.scale(), 5); assert_eq!("-233.43343", a.to_string()); } #[test] fn it_parses_positive_tiny_float_string() { let a = Decimal::from_str(".000001").unwrap(); assert_eq!(a.is_sign_negative(), false); assert_eq!(a.scale(), 6); assert_eq!("0.000001", a.to_string()); } #[test] fn it_parses_negative_tiny_float_string() { let a = Decimal::from_str("-0.000001").unwrap(); assert_eq!(a.is_sign_negative(), true); assert_eq!(a.scale(), 6); assert_eq!("-0.000001", a.to_string()); } #[test] fn it_parses_big_integer_string() { let a = Decimal::from_str("79228162514264337593543950330").unwrap(); assert_eq!("79228162514264337593543950330", a.to_string()); } #[test] fn it_parses_big_float_string() { let a = Decimal::from_str("79.228162514264337593543950330").unwrap(); assert_eq!("79.228162514264337593543950330", a.to_string()); } #[test] fn it_can_serialize_deserialize() { let tests = [ "12.3456789", "5233.9008808150288439427720175", "-5233.9008808150288439427720175", ]; for test in &tests { let a = Decimal::from_str(test).unwrap(); let bytes = a.serialize(); let b = Decimal::deserialize(bytes); assert_eq!(test.to_string(), b.to_string()); } } #[test] fn it_can_deserialize_unbounded_values() { // Mantissa for these: 19393111376951473493673267553 let tests = [ ( [1u8, 0, 28, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], // Scale 28: -1.9393111376951473493673267553 "-1.9393111376951473493673267553", ), ( [1u8, 0, 29, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], // Scale 29: -0.19393111376951473493673267553 "-0.1939311137695147349367326755", ), ( [1u8, 0, 30, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], // Scale 30: -0.019393111376951473493673267553 "-0.0193931113769514734936732676", ), ( [1u8, 0, 31, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], // Scale 31: -0.0019393111376951473493673267553 "-0.0019393111376951473493673268", ), ]; for &(bytes, expected) in &tests { let dec = Decimal::deserialize(bytes); let string = format!("{:.9999}", dec); let dec2 = Decimal::from_str(&string).unwrap(); assert_eq!(dec, dec2); assert_eq!(dec.to_string(), expected, "dec.to_string()"); assert_eq!(dec2.to_string(), expected, "dec2.to_string()"); } } // Formatting #[test] fn it_formats() { let a = Decimal::from_str("233.323223").unwrap(); assert_eq!(format!("{}", a), "233.323223"); assert_eq!(format!("{:.9}", a), "233.323223000"); assert_eq!(format!("{:.0}", a), "233"); assert_eq!(format!("{:.2}", a), "233.32"); assert_eq!(format!("{:010.2}", a), "0000233.32"); assert_eq!(format!("{:0<10.2}", a), "233.320000"); } #[test] fn it_formats_neg() { let a = Decimal::from_str("-233.323223").unwrap(); assert_eq!(format!("{}", a), "-233.323223"); assert_eq!(format!("{:.9}", a), "-233.323223000"); assert_eq!(format!("{:.0}", a), "-233"); assert_eq!(format!("{:.2}", a), "-233.32"); assert_eq!(format!("{:010.2}", a), "-000233.32"); assert_eq!(format!("{:0<10.2}", a), "-233.32000"); } #[test] fn it_formats_small() { let a = Decimal::from_str("0.2223").unwrap(); assert_eq!(format!("{}", a), "0.2223"); assert_eq!(format!("{:.9}", a), "0.222300000"); assert_eq!(format!("{:.0}", a), "0"); assert_eq!(format!("{:.2}", a), "0.22"); assert_eq!(format!("{:010.2}", a), "0000000.22"); assert_eq!(format!("{:0<10.2}", a), "0.22000000"); } #[test] fn it_formats_small_leading_zeros() { let a = Decimal::from_str("0.0023554701772169").unwrap(); assert_eq!(format!("{}", a), "0.0023554701772169"); assert_eq!(format!("{:.9}", a), "0.002355470"); assert_eq!(format!("{:.0}", a), "0"); assert_eq!(format!("{:.2}", a), "0.00"); assert_eq!(format!("{:010.2}", a), "0000000.00"); assert_eq!(format!("{:0<10.2}", a), "0.00000000"); } #[test] fn it_formats_small_neg() { let a = Decimal::from_str("-0.2223").unwrap(); assert_eq!(format!("{}", a), "-0.2223"); assert_eq!(format!("{:.9}", a), "-0.222300000"); assert_eq!(format!("{:.0}", a), "-0"); assert_eq!(format!("{:.2}", a), "-0.22"); assert_eq!(format!("{:010.2}", a), "-000000.22"); assert_eq!(format!("{:0<10.2}", a), "-0.2200000"); } #[test] fn it_formats_zero() { let a = Decimal::from_str("0").unwrap(); assert_eq!(format!("{}", a), "0"); assert_eq!(format!("{:.9}", a), "0.000000000"); assert_eq!(format!("{:.0}", a), "0"); assert_eq!(format!("{:.2}", a), "0.00"); assert_eq!(format!("{:010.2}", a), "0000000.00"); assert_eq!(format!("{:0<10.2}", a), "0.00000000"); } #[test] fn it_formats_int() { let a = Decimal::from_str("5").unwrap(); assert_eq!(format!("{}", a), "5"); assert_eq!(format!("{:.9}", a), "5.000000000"); assert_eq!(format!("{:.0}", a), "5"); assert_eq!(format!("{:.2}", a), "5.00"); assert_eq!(format!("{:010.2}", a), "0000005.00"); assert_eq!(format!("{:0<10.2}", a), "5.00000000"); } #[test] fn it_formats_lower_exp() { let tests = [ ("0.00001", "1e-5"), ("-0.00001", "-1e-5"), ("42.123", "4.2123e1"), ("-42.123", "-4.2123e1"), ("100", "1e2"), ]; for (value, expected) in &tests { let a = Decimal::from_str(value).unwrap(); assert_eq!(&format!("{:e}", a), *expected, "format!(\"{{:e}}\", {})", a); } } #[test] fn it_formats_lower_exp_padding() { let tests = [ ("0.00001", "01e-5"), ("-0.00001", "-1e-5"), ("42.123", "4.2123e1"), ("-42.123", "-4.2123e1"), ("100", "001e2"), ]; for (value, expected) in &tests { let a = Decimal::from_str(value).unwrap(); assert_eq!(&format!("{:05e}", a), *expected, "format!(\"{{:05e}}\", {})", a); } } // Negation #[test] fn it_negates_decimals() { fn neg(a: &str, b: &str) { let a = Decimal::from_str(a).unwrap(); let result = -a; assert_eq!(b, result.to_string(), "- {}", a.to_string()); } let tests = &[ ("1", "-1"), ("2", "-2"), ("2454495034", "-2454495034"), ("0.1", "-0.1"), ("11.815126050420168067226890757", "-11.815126050420168067226890757"), ]; for &(a, b) in tests { neg(a, b); neg(b, a); } } // Addition #[test] fn it_can_add_simple() { // This is the most basic test for addition, intended largely for micro-optimization. let two = Decimal::ONE + Decimal::ONE; assert_eq!(two.to_u32(), Some(2)); } #[test] fn it_adds_decimals() { fn add(a: &str, b: &str, c: &str) { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); let result = a + b; assert_eq!(c, result.to_string(), "{} + {}", a.to_string(), b.to_string()); let result = b + a; assert_eq!(c, result.to_string(), "{} + {}", b.to_string(), a.to_string()); } let tests = &[ ("0", "0", "0"), ("0", "-0", "0"), ("-0", "0", "0"), ("-0", "-0", "0"), ("2", "3", "5"), ("2454495034", "3451204593", "5905699627"), ("24544.95034", ".3451204593", "24545.2954604593"), (".1", ".1", "0.2"), (".10", ".1", "0.20"), (".1", "-.1", "0.0"), ("0", "1.001", "1.001"), ("2", "-3", "-1"), ("-2", "3", "1"), ("-2", "-3", "-5"), ("3", "-2", "1"), ("-3", "2", "-1"), ("1.234", "2.4567", "3.6907"), ( "11.815126050420168067226890757", "0.6386554621848739495798319328", "12.453781512605042016806722690", ), ( "-11.815126050420168067226890757", "0.6386554621848739495798319328", "-11.176470588235294117647058824", ), ( "11.815126050420168067226890757", "-0.6386554621848739495798319328", "11.176470588235294117647058824", ), ( "-11.815126050420168067226890757", "-0.6386554621848739495798319328", "-12.453781512605042016806722690", ), ( "11815126050420168067226890757", "0.4386554621848739495798319328", "11815126050420168067226890757", ), ( "-11815126050420168067226890757", "0.4386554621848739495798319328", "-11815126050420168067226890757", ), ( "11815126050420168067226890757", "-0.4386554621848739495798319328", "11815126050420168067226890757", ), ( "-11815126050420168067226890757", "-0.4386554621848739495798319328", "-11815126050420168067226890757", ), ( "0.0872727272727272727272727272", "843.65000000", "843.7372727272727272727272727", ), ( "7314.6229858868828353570724702", "1000", // Overflow causes this to round "8314.622985886882835357072470", ), ( "108053.27500000000000000000000", "0.00000000000000000000000", "108053.27500000000000000000000", ), ( "108053.27500000000000000000000", // This zero value has too high precision and will be trimmed "0.000000000000000000000000", "108053.27500000000000000000000", ), ( "108053.27500000000000000000000", // This value has too high precision and will be rounded "0.000000000000000000000001", "108053.27500000000000000000000", ), ( "108053.27500000000000000000000", // This value has too high precision and will be rounded "0.000000000000000000000005", either!("108053.27500000000000000000000", "108053.27500000000000000000001"), ), ( "8097370036018690744.2590371109596744091", "3807285637671831400.15346897797550749555", "11904655673690522144.412506089", ), ]; for &(a, b, c) in tests { add(a, b, c); } } #[test] fn it_can_addassign() { let mut a = Decimal::from_str("1.01").unwrap(); let b = Decimal::from_str("0.99").unwrap(); a += b; assert_eq!("2.00", a.to_string()); a += &b; assert_eq!("2.99", a.to_string()); let mut c = &mut a; c += b; assert_eq!("3.98", a.to_string()); let mut c = &mut a; c += &b; assert_eq!("4.97", a.to_string()); } // Subtraction #[test] fn it_subtracts_decimals() { fn sub(a: &str, b: &str, c: &str) { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); let result = a - b; assert_eq!(c, result.to_string(), "{} - {}", a.to_string(), b.to_string()); } let tests = &[ ("0", "0", "0"), ("0", "-0", "0"), ("-0", "0", "0"), ("-0", "-0", "0"), ("2", "3", "-1"), ("3451204593", "2323322332", "1127882261"), ("24544.95034", ".3451204593", "24544.6052195407"), (".1", ".1", "0.0"), (".1", "-.1", "0.2"), ("1.001", "0", "1.001"), ("2", "-3", "5"), ("-2", "3", "-5"), ("-2", "-3", "1"), ("3", "-2", "5"), ("-3", "2", "-5"), ("1.234", "2.4567", "-1.2227"), ("844.13000000", "843.65000000", "0.48000000"), ("79228162514264337593543950335", "79228162514264337593543950335", "0"), // 0xFFFF_FFFF_FFFF_FFFF_FFF_FFFF - 0xFFFF_FFFF_FFFF_FFFF_FFF_FFFF ("79228162514264337593543950335", "0", "79228162514264337593543950335"), ("79228162514264337593543950335", "79228162514264337593543950333", "2"), ("4951760157141521099596496896", "1", "4951760157141521099596496895"), // 0x1000_0000_0000_0000_0000_0000 - 1 = 0x0FFF_FFFF_FFFF_FFFF_FFF_FFFF ("79228162514264337593543950334", "79228162514264337593543950335", "-1"), ("1", "4951760157141521099596496895", "-4951760157141521099596496894"), ("18446744073709551615", "-18446744073709551615", "36893488147419103230"), // 0xFFFF_FFFF_FFFF_FFFF - -0xFFFF_FFFF_FFFF_FFFF ]; for &(a, b, c) in tests { sub(a, b, c); } } #[test] fn it_can_subassign() { let mut a = Decimal::from_str("1.01").unwrap(); let b = Decimal::from_str("0.51").unwrap(); a -= b; assert_eq!("0.50", a.to_string()); a -= &b; assert_eq!("-0.01", a.to_string()); let mut c = &mut a; c -= b; assert_eq!("-0.52", a.to_string()); let mut c = &mut a; c -= &b; assert_eq!("-1.03", a.to_string()); } // Multiplication #[test] fn it_multiplies_decimals() { fn mul(a: &str, b: &str, c: &str) { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); let result = a * b; assert_eq!(c, result.to_string(), "{} * {}", a.to_string(), b.to_string()); let result = b * a; assert_eq!(c, result.to_string(), "{} * {}", b.to_string(), a.to_string()); } let tests = &[ ("2", "3", "6"), ("2454495034", "3451204593", "8470964534836491162"), ("24544.95034", ".3451204593", "8470.964534836491162"), (".1", ".1", "0.01"), ("0", "1.001", "0"), ("2", "-3", "-6"), ("-2", "3", "-6"), ("-2", "-3", "6"), ("1", "2.01", "2.01"), ("1.0", "2.01", "2.010"), // Scale is always additive ( "0.00000000000000001", "0.00000000000000001", "0.0000000000000000000000000000", ), ("0.0000000000000000000000000001", "0.0000000000000000000000000001", "0"), ( "0.6386554621848739495798319328", "11.815126050420168067226890757", "7.5457947885036367488171739292", ), ( "2123456789012345678901234567.8", "11.815126050420168067226890757", "25088909624801327937270048761", ), ( "2123456789012345678901234567.8", "-11.815126050420168067226890757", "-25088909624801327937270048761", ), ( "2.1234567890123456789012345678", "2.1234567890123456789012345678", "4.5090687348026215523554336227", ), ( "0.48000000", "0.1818181818181818181818181818", either!("0.0872727272727272727272727273", "0.0872727272727272727272727272"), ), ]; for &(a, b, c) in tests { mul(a, b, c); } } #[test] #[should_panic(expected = "Multiplication overflowed")] fn it_panics_when_multiply_with_overflow() { let a = Decimal::from_str("2000000000000000000001").unwrap(); let b = Decimal::from_str("3000000000000000000001").unwrap(); let _ = a * b; } #[test] fn it_can_mulassign() { let mut a = Decimal::from_str("1.25").unwrap(); let b = Decimal::from_str("0.01").unwrap(); a *= b; assert_eq!("0.0125", a.to_string()); a *= &b; assert_eq!("0.000125", a.to_string()); let mut c = &mut a; c *= b; assert_eq!("0.00000125", a.to_string()); let mut c = &mut a; c *= &b; assert_eq!("0.0000000125", a.to_string()); } // Division #[test] fn it_divides_decimals() { fn div(a: &str, b: &str, c: &str) { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); let result = a / b; assert_eq!(c, result.to_string(), "{} / {}", a.to_string(), b.to_string()); } let tests = &[ ("6", "3", "2"), ("10", "2", "5"), ("2.2", "1.1", "2"), ("-2.2", "-1.1", "2"), ("12.88", "5.6", "2.3"), ( "1023427554493", "43432632", either!("23563.562864276795382789603909", "23563.562864276795382789603908"), ), ("10000", "3", "3333.3333333333333333333333333"), ("2", "3", "0.6666666666666666666666666667"), ("1", "3", "0.3333333333333333333333333333"), ("-2", "3", "-0.6666666666666666666666666667"), ("2", "-3", "-0.6666666666666666666666666667"), ("-2", "-3", "0.6666666666666666666666666667"), ( "1234.5678", "0.1234567890123456", either!("9999.99926999999982999953127", "9999.999269999999829999531269"), ), ("1234.567890123456789012345678", "1.234567890123456789012345678", "1000"), ( "32.91625929004387114334488", "3.27629537", either!("10.046792359274942644546996384", "10.046792359274942644546996383"), ), ("5000", "1000.26957490549", "4.9986524887277738570721416846"), ("6142.6941216127122745222131114", "2", "3071.3470608063561372611065557"), ( "3071.3470608063561372611065557", "1228.87000756", either!("2.4993262443638869285360708423", "2.4993262443638869285360708422"), ), ( "590.3274854004009467754255123", "53.68997202826239", "10.995116277759516850521689988", ), ]; for &(a, b, c) in tests { div(a, b, c); } } #[test] #[should_panic(expected = "Division by zero")] fn it_can_divide_by_zero() { let a = Decimal::from_str("2").unwrap(); let _ = a / Decimal::ZERO; } #[test] fn it_can_divassign() { let mut a = Decimal::from_str("1.25").unwrap(); let b = Decimal::from_str("0.01").unwrap(); a /= b; assert_eq!("125", a.to_string()); a /= &b; assert_eq!("12500", a.to_string()); let mut c = &mut a; c /= b; assert_eq!("1250000", a.to_string()); let mut c = &mut a; c /= &b; assert_eq!("125000000", a.to_string()); } // Modulus and Remainder are not the same thing! // https://math.stackexchange.com/q/801962/82277 #[test] fn it_rems_decimals() { fn rem(a: &str, b: &str, c: &str) { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); // a = qb + r let result = a % b; assert_eq!(c, result.to_string(), "{} % {}", a.to_string(), b.to_string()); } let tests = &[ ("2", "3", "2"), ("-2", "3", "-2"), ("2", "-3", "2"), ("-2", "-3", "-2"), ("6", "3", "0"), ("42.2", "11.9", "6.5"), ("2.1", "3", "2.1"), ("2", "3.1", "2"), ("2.0", "3.1", "2.0"), ("4", "3.1", "0.9"), ("2", "2", "0"), ("2", "-2", "0"), // Legacy keeps sign from lhs operand ("-2", "2", "0"), ("-2", "-2", "0"), ]; for &(a, b, c) in tests { rem(a, b, c); } } #[test] fn it_can_remassign() { let mut a = Decimal::from_str("5").unwrap(); let b = Decimal::from_str("2").unwrap(); a %= b; assert_eq!("1", a.to_string()); a %= &b; assert_eq!("1", a.to_string()); let mut c = &mut a; c %= b; assert_eq!("1", a.to_string()); let mut c = &mut a; c %= &b; assert_eq!("1", a.to_string()); } #[test] fn it_eqs_decimals() { fn eq(a: &str, b: &str, c: bool) { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); assert_eq!(c, a.eq(&b), "{} == {}", a.to_string(), b.to_string()); assert_eq!(c, b.eq(&a), "{} == {}", b.to_string(), a.to_string()); } let tests = &[ ("1", "1", true), ("1", "-1", false), ("1", "1.00", true), ("1.2345000000000", "1.2345", true), ("1.0000000000000000000000000000", "1.0000000000000000000000000000", true), ( "1.0000000000000000000000000001", "1.0000000000000000000000000000", false, ), ]; for &(a, b, c) in tests { eq(a, b, c); } } #[test] fn it_cmps_decimals() { fn cmp(a: &str, b: &str, c: core::cmp::Ordering) { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); assert_eq!( c, a.cmp(&b), "{} {} {}", a.to_string(), match c { Less => "<", Equal => "==", Greater => ">", }, b.to_string() ); } let tests = &[ ("1", "1", Equal), ("1", "-1", Greater), ("1", "1.00", Equal), ("1.2345000000000", "1.2345", Equal), ( "1.0000000000000000000000000001", "1.0000000000000000000000000000", Greater, ), ("1.0000000000000000000000000000", "1.0000000000000000000000000001", Less), ("-1", "100", Less), ("-100", "1", Less), ("0", "0.5", Less), ("0.5", "0", Greater), ("100", "0.0098", Greater), ("1000000000000000", "999000000000000.0001", Greater), ("2.0001", "2.0001", Equal), ( "11.815126050420168067226890757", "0.6386554621848739495798319328", Greater, ), ("0.6386554621848739495798319328", "11.815126050420168067226890757", Less), ("-0.5", "-0.01", Less), ("-0.5", "-0.1", Less), ("-0.01", "-0.5", Greater), ("-0.1", "-0.5", Greater), // 000 equality ("0.00000000", "0.00000000", Equal), // 000 000 same scale ("0.00000000", "0.00000000", Equal), ("-0.00000000", "0.00000000", Equal), ("0.00000000", "-0.00000000", Equal), ("-0.00000000", "-0.00000000", Equal), // 000 000 different scale ("0.000000000", "0.00000000000000000000000", Equal), ("-0.000000000", "0.00000000000000000000000", Equal), ("0.000000000", "-0.00000000000000000000000", Equal), ("-0.000000000", "-0.00000000000000000000000", Equal), // 000 100 same scale ("0.00000000", "6.56792910", Less), ("-0.00000000", "6.56792910", Less), ("0.00000000", "-6.56792910", Greater), ("-0.00000000", "-6.56792910", Greater), // 000 100 different scale ("0.0000000000000000000", "0.00000000000000001916236746", Less), ("-0.0000000000000000000", "0.00000000000000001916236746", Less), ("0.0000000000000000000", "-0.00000000000000001916236746", Greater), ("-0.0000000000000000000", "-0.00000000000000001916236746", Greater), // 000 010 same scale ("0.00000000", "49037796231.72571136", Less), ("-0.00000000", "49037796231.72571136", Less), ("0.00000000", "-49037796231.72571136", Greater), ("-0.00000000", "-49037796231.72571136", Greater), // 000 010 different scale ("0", "14459264155.12895488", Less), ("-0", "14459264155.12895488", Less), ("0", "-14459264155.12895488", Greater), ("-0", "-14459264155.12895488", Greater), // 000 110 same scale ("0.00000000", "38675108055.09052783", Less), ("-0.00000000", "38675108055.09052783", Less), ("0.00000000", "-38675108055.09052783", Greater), ("-0.00000000", "-38675108055.09052783", Greater), // 000 110 different scale ("0.00", "1495767034080324868", Less), ("-0.00", "1495767034080324868", Less), ("0.00", "-1495767034080324868", Greater), ("-0.00", "-1495767034080324868", Greater), // 000 001 same scale ("0.00000000", "359299289270893106016.81305600", Less), ("-0.00000000", "359299289270893106016.81305600", Less), ("0.00000000", "-359299289270893106016.81305600", Greater), ("-0.00000000", "-359299289270893106016.81305600", Greater), // 000 001 different scale ("0.00000000000000000000000000", "261631091689.9518486763536384", Less), ("-0.00000000000000000000000000", "261631091689.9518486763536384", Less), ( "0.00000000000000000000000000", "-261631091689.9518486763536384", Greater, ), ( "-0.00000000000000000000000000", "-261631091689.9518486763536384", Greater, ), // 000 101 same scale ("0.00000000", "184137107696737410476.63166815", Less), ("-0.00000000", "184137107696737410476.63166815", Less), ("0.00000000", "-184137107696737410476.63166815", Greater), ("-0.00000000", "-184137107696737410476.63166815", Greater), // 000 101 different scale ("0.000000000", "2286857871088.7514840434334478", Less), ("-0.000000000", "2286857871088.7514840434334478", Less), ("0.000000000", "-2286857871088.7514840434334478", Greater), ("-0.000000000", "-2286857871088.7514840434334478", Greater), // 000 011 same scale ("0.00000000", "169194696640288819908.07715840", Less), ("-0.00000000", "169194696640288819908.07715840", Less), ("0.00000000", "-169194696640288819908.07715840", Greater), ("-0.00000000", "-169194696640288819908.07715840", Greater), // 000 011 different scale ("0.00000000", "2757550691.7650076909569048576", Less), ("-0.00000000", "2757550691.7650076909569048576", Less), ("0.00000000", "-2757550691.7650076909569048576", Greater), ("-0.00000000", "-2757550691.7650076909569048576", Greater), // 000 111 same scale ("0.00000000", "133610725292915899001.10059212", Less), ("-0.00000000", "133610725292915899001.10059212", Less), ("0.00000000", "-133610725292915899001.10059212", Greater), ("-0.00000000", "-133610725292915899001.10059212", Greater), // 000 111 different scale ("0.00000000000000000000", "86.25432767926620368822165265", Less), ("-0.00000000000000000000", "86.25432767926620368822165265", Less), ("0.00000000000000000000", "-86.25432767926620368822165265", Greater), ("-0.00000000000000000000", "-86.25432767926620368822165265", Greater), // 100 equality ("0.0000000000598992228", "0.0000000000598992228", Equal), // 100 000 same scale ("0.0000000000598992228", "0.0000000000000000000", Greater), ("-0.0000000000598992228", "0.0000000000000000000", Less), ("0.0000000000598992228", "-0.0000000000000000000", Greater), ("-0.0000000000598992228", "-0.0000000000000000000", Less), // 100 000 different scale ("0.1797407597", "0.0000000000000000000", Greater), ("-0.1797407597", "0.0000000000000000000", Less), ("0.1797407597", "-0.0000000000000000000", Greater), ("-0.1797407597", "-0.0000000000000000000", Less), // 100 100 same scale ("0.0000000000598992228", "0.0000000000064510789", Greater), ("-0.0000000000598992228", "0.0000000000064510789", Less), ("0.0000000000598992228", "-0.0000000000064510789", Greater), ("-0.0000000000598992228", "-0.0000000000064510789", Less), // 100 100 different scale ("0.000000000000011217354", "0.0000000000217735186", Less), ("-0.000000000000011217354", "0.0000000000217735186", Less), ("0.000000000000011217354", "-0.0000000000217735186", Greater), ("-0.000000000000011217354", "-0.0000000000217735186", Greater), // 100 010 same scale ("0.0000000000598992228", "0.0659116848159129600", Less), ("-0.0000000000598992228", "0.0659116848159129600", Less), ("0.0000000000598992228", "-0.0659116848159129600", Greater), ("-0.0000000000598992228", "-0.0659116848159129600", Greater), // 100 010 different scale ("0.00042035421", "0.004709460588143575040", Less), ("-0.00042035421", "0.004709460588143575040", Less), ("0.00042035421", "-0.004709460588143575040", Greater), ("-0.00042035421", "-0.004709460588143575040", Greater), // 100 110 same scale ("0.0000000000598992228", "0.0755686585127091375", Less), ("-0.0000000000598992228", "0.0755686585127091375", Less), ("0.0000000000598992228", "-0.0755686585127091375", Greater), ("-0.0000000000598992228", "-0.0755686585127091375", Greater), // 100 110 different scale ("14872.94465", "1284707831905.854085", Less), ("-14872.94465", "1284707831905.854085", Less), ("14872.94465", "-1284707831905.854085", Greater), ("-14872.94465", "-1284707831905.854085", Greater), // 100 001 same scale ("0.0000000000598992228", "888767595.6145376468836286464", Less), ("-0.0000000000598992228", "888767595.6145376468836286464", Less), ("0.0000000000598992228", "-888767595.6145376468836286464", Greater), ("-0.0000000000598992228", "-888767595.6145376468836286464", Greater), // 100 001 different scale ("0.0002108155975", "36.07555527243968476014968832", Less), ("-0.0002108155975", "36.07555527243968476014968832", Less), ("0.0002108155975", "-36.07555527243968476014968832", Greater), ("-0.0002108155975", "-36.07555527243968476014968832", Greater), // 100 101 same scale ("0.0000000000598992228", "125730345.7412344676309569911", Less), ("-0.0000000000598992228", "125730345.7412344676309569911", Less), ("0.0000000000598992228", "-125730345.7412344676309569911", Greater), ("-0.0000000000598992228", "-125730345.7412344676309569911", Greater), // 100 101 different scale ("0.0000000000871576741", "1.7283925558865766140690239662", Less), ("-0.0000000000871576741", "1.7283925558865766140690239662", Less), ("0.0000000000871576741", "-1.7283925558865766140690239662", Greater), ("-0.0000000000871576741", "-1.7283925558865766140690239662", Greater), // 100 011 same scale ("0.0000000000598992228", "645513262.9193254090737451008", Less), ("-0.0000000000598992228", "645513262.9193254090737451008", Less), ("0.0000000000598992228", "-645513262.9193254090737451008", Greater), ("-0.0000000000598992228", "-645513262.9193254090737451008", Greater), // 100 011 different scale ("0.000000000000000760885021", "3718370638.2004059326675681280", Less), ("-0.000000000000000760885021", "3718370638.2004059326675681280", Less), ("0.000000000000000760885021", "-3718370638.2004059326675681280", Greater), ( "-0.000000000000000760885021", "-3718370638.2004059326675681280", Greater, ), // 100 111 same scale ("0.0000000000598992228", "422482675.5515775479939306437", Less), ("-0.0000000000598992228", "422482675.5515775479939306437", Less), ("0.0000000000598992228", "-422482675.5515775479939306437", Greater), ("-0.0000000000598992228", "-422482675.5515775479939306437", Greater), // 100 111 different scale ("0.000000044182898", "25.452953262109919998605674045", Less), ("-0.000000044182898", "25.452953262109919998605674045", Less), ("0.000000044182898", "-25.452953262109919998605674045", Greater), ("-0.000000044182898", "-25.452953262109919998605674045", Greater), // 010 equality ("423.1744746042687488", "423.1744746042687488", Equal), // 010 000 same scale ("423.1744746042687488", "0.0000000000000000", Greater), ("-423.1744746042687488", "0.0000000000000000", Less), ("423.1744746042687488", "-0.0000000000000000", Greater), ("-423.1744746042687488", "-0.0000000000000000", Less), // 010 000 different scale ("9002354991.192604672", "0.00000000000000000000000000", Greater), ("-9002354991.192604672", "0.00000000000000000000000000", Less), ("9002354991.192604672", "-0.00000000000000000000000000", Greater), ("-9002354991.192604672", "-0.00000000000000000000000000", Less), // 010 100 same scale ("423.1744746042687488", "0.0000000981820809", Greater), ("-423.1744746042687488", "0.0000000981820809", Less), ("423.1744746042687488", "-0.0000000981820809", Greater), ("-423.1744746042687488", "-0.0000000981820809", Less), // 010 100 different scale ("4327019125101559.808", "0.00484846050", Greater), ("-4327019125101559.808", "0.00484846050", Less), ("4327019125101559.808", "-0.00484846050", Greater), ("-4327019125101559.808", "-0.00484846050", Less), // 010 010 same scale ("423.1744746042687488", "786.9082854590775296", Less), ("-423.1744746042687488", "786.9082854590775296", Less), ("423.1744746042687488", "-786.9082854590775296", Greater), ("-423.1744746042687488", "-786.9082854590775296", Greater), // 010 010 different scale ("793.0067125291450368", "0.0001587297248335626240", Greater), ("-793.0067125291450368", "0.0001587297248335626240", Less), ("793.0067125291450368", "-0.0001587297248335626240", Greater), ("-793.0067125291450368", "-0.0001587297248335626240", Less), // 010 110 same scale ("423.1744746042687488", "300.0541360230572049", Greater), ("-423.1744746042687488", "300.0541360230572049", Less), ("423.1744746042687488", "-300.0541360230572049", Greater), ("-423.1744746042687488", "-300.0541360230572049", Less), // 010 110 different scale ("90627.2042582540288", "4472414566654924.741", Less), ("-90627.2042582540288", "4472414566654924.741", Less), ("90627.2042582540288", "-4472414566654924.741", Greater), ("-90627.2042582540288", "-4472414566654924.741", Greater), // 010 001 same scale ("423.1744746042687488", "3960577151543.5796707636412416", Less), ("-423.1744746042687488", "3960577151543.5796707636412416", Less), ("423.1744746042687488", "-3960577151543.5796707636412416", Greater), ("-423.1744746042687488", "-3960577151543.5796707636412416", Greater), // 010 001 different scale ("0.000008867461591822499840", "185286.04378228713249986052096", Less), ("-0.000008867461591822499840", "185286.04378228713249986052096", Less), ("0.000008867461591822499840", "-185286.04378228713249986052096", Greater), ( "-0.000008867461591822499840", "-185286.04378228713249986052096", Greater, ), // 010 101 same scale ("423.1744746042687488", "2825958416017.6213507229869501", Less), ("-423.1744746042687488", "2825958416017.6213507229869501", Less), ("423.1744746042687488", "-2825958416017.6213507229869501", Greater), ("-423.1744746042687488", "-2825958416017.6213507229869501", Greater), // 010 101 different scale ("0.01901870767742648320", "37662082383021542232.529651212", Less), ("-0.01901870767742648320", "37662082383021542232.529651212", Less), ("0.01901870767742648320", "-37662082383021542232.529651212", Greater), ("-0.01901870767742648320", "-37662082383021542232.529651212", Greater), // 010 011 same scale ("423.1744746042687488", "3628063966991.6759417059016704", Less), ("-423.1744746042687488", "3628063966991.6759417059016704", Less), ("423.1744746042687488", "-3628063966991.6759417059016704", Greater), ("-423.1744746042687488", "-3628063966991.6759417059016704", Greater), // 010 011 different scale ("45359904.32470925312", "2.4203452488052342918570049536", Greater), ("-45359904.32470925312", "2.4203452488052342918570049536", Less), ("45359904.32470925312", "-2.4203452488052342918570049536", Greater), ("-45359904.32470925312", "-2.4203452488052342918570049536", Less), // 010 111 same scale ("423.1744746042687488", "2629665172331.9610693820109120", Less), ("-423.1744746042687488", "2629665172331.9610693820109120", Less), ("423.1744746042687488", "-2629665172331.9610693820109120", Greater), ("-423.1744746042687488", "-2629665172331.9610693820109120", Greater), // 010 111 different scale ("0.0006420803252266205184", "8172.032417576265900588945489", Less), ("-0.0006420803252266205184", "8172.032417576265900588945489", Less), ("0.0006420803252266205184", "-8172.032417576265900588945489", Greater), ("-0.0006420803252266205184", "-8172.032417576265900588945489", Greater), // 110 equality ("844.5530620517286511", "844.5530620517286511", Equal), // 110 000 same scale ("844.5530620517286511", "0.0000000000000000", Greater), ("-844.5530620517286511", "0.0000000000000000", Less), ("844.5530620517286511", "-0.0000000000000000", Greater), ("-844.5530620517286511", "-0.0000000000000000", Less), // 110 000 different scale ("3285.530033386074797", "0.00000000000000000000", Greater), ("-3285.530033386074797", "0.00000000000000000000", Less), ("3285.530033386074797", "-0.00000000000000000000", Greater), ("-3285.530033386074797", "-0.00000000000000000000", Less), // 110 100 same scale ("844.5530620517286511", "0.0000001953470063", Greater), ("-844.5530620517286511", "0.0000001953470063", Less), ("844.5530620517286511", "-0.0000001953470063", Greater), ("-844.5530620517286511", "-0.0000001953470063", Less), // 110 100 different scale ("371284.0210972493371", "0.000000000000001307794657", Greater), ("-371284.0210972493371", "0.000000000000001307794657", Less), ("371284.0210972493371", "-0.000000000000001307794657", Greater), ("-371284.0210972493371", "-0.000000000000001307794657", Less), // 110 010 same scale ("844.5530620517286511", "612.1542773033140224", Greater), ("-844.5530620517286511", "612.1542773033140224", Less), ("844.5530620517286511", "-612.1542773033140224", Greater), ("-844.5530620517286511", "-612.1542773033140224", Less), // 110 010 different scale ("0.00004869219159821525572", "23341676485159.15776", Less), ("-0.00004869219159821525572", "23341676485159.15776", Less), ("0.00004869219159821525572", "-23341676485159.15776", Greater), ("-0.00004869219159821525572", "-23341676485159.15776", Greater), // 110 110 same scale ("844.5530620517286511", "326.6132818317622015", Greater), ("-844.5530620517286511", "326.6132818317622015", Less), ("844.5530620517286511", "-326.6132818317622015", Greater), ("-844.5530620517286511", "-326.6132818317622015", Less), // 110 110 different scale ("4752139369958.820619", "1330851022882027.972", Less), ("-4752139369958.820619", "1330851022882027.972", Less), ("4752139369958.820619", "-1330851022882027.972", Greater), ("-4752139369958.820619", "-1330851022882027.972", Greater), // 110 001 same scale ("844.5530620517286511", "3585610241942.1922435648192512", Less), ("-844.5530620517286511", "3585610241942.1922435648192512", Less), ("844.5530620517286511", "-3585610241942.1922435648192512", Greater), ("-844.5530620517286511", "-3585610241942.1922435648192512", Greater), // 110 001 different scale ("539313715923.4424678", "12410950080603997079634706432", Less), ("-539313715923.4424678", "12410950080603997079634706432", Less), ("539313715923.4424678", "-12410950080603997079634706432", Greater), ("-539313715923.4424678", "-12410950080603997079634706432", Greater), // 110 101 same scale ("844.5530620517286511", "1947825396031.6933708908343230", Less), ("-844.5530620517286511", "1947825396031.6933708908343230", Less), ("844.5530620517286511", "-1947825396031.6933708908343230", Greater), ("-844.5530620517286511", "-1947825396031.6933708908343230", Greater), // 110 101 different scale ("0.2301405445512525317", "245433629587.71206426704897154", Less), ("-0.2301405445512525317", "245433629587.71206426704897154", Less), ("0.2301405445512525317", "-245433629587.71206426704897154", Greater), ("-0.2301405445512525317", "-245433629587.71206426704897154", Greater), // 110 011 same scale ("844.5530620517286511", "3637850451015.0843464291450880", Less), ("-844.5530620517286511", "3637850451015.0843464291450880", Less), ("844.5530620517286511", "-3637850451015.0843464291450880", Greater), ("-844.5530620517286511", "-3637850451015.0843464291450880", Greater), // 110 011 different scale ("0.00000000717944802566514691", "18143443615.480512395717115904", Less), ("-0.00000000717944802566514691", "18143443615.480512395717115904", Less), ( "0.00000000717944802566514691", "-18143443615.480512395717115904", Greater, ), ( "-0.00000000717944802566514691", "-18143443615.480512395717115904", Greater, ), // 110 111 same scale ("844.5530620517286511", "2738424264600.4917875777303163", Less), ("-844.5530620517286511", "2738424264600.4917875777303163", Less), ("844.5530620517286511", "-2738424264600.4917875777303163", Greater), ("-844.5530620517286511", "-2738424264600.4917875777303163", Greater), // 110 111 different scale ("0.0000000007762706076409491335", "2489879185787497651.6458518595", Less), ( "-0.0000000007762706076409491335", "2489879185787497651.6458518595", Less, ), ( "0.0000000007762706076409491335", "-2489879185787497651.6458518595", Greater, ), ( "-0.0000000007762706076409491335", "-2489879185787497651.6458518595", Greater, ), // 001 equality ("316007568232.9263258873102336", "316007568232.9263258873102336", Equal), // 001 000 same scale ("316007568232.9263258873102336", "0.0000000000000000", Greater), ("-316007568232.9263258873102336", "0.0000000000000000", Less), ("316007568232.9263258873102336", "-0.0000000000000000", Greater), ("-316007568232.9263258873102336", "-0.0000000000000000", Less), // 001 000 different scale ("3522055990024364385815547084.8", "0.000000000000", Greater), ("-3522055990024364385815547084.8", "0.000000000000", Less), ("3522055990024364385815547084.8", "-0.000000000000", Greater), ("-3522055990024364385815547084.8", "-0.000000000000", Less), // 001 100 same scale ("316007568232.9263258873102336", "0.0000001073412971", Greater), ("-316007568232.9263258873102336", "0.0000001073412971", Less), ("316007568232.9263258873102336", "-0.0000001073412971", Greater), ("-316007568232.9263258873102336", "-0.0000001073412971", Less), // 001 100 different scale ("1319006.0491408208640440532992", "0.00000000000000611866432", Greater), ("-1319006.0491408208640440532992", "0.00000000000000611866432", Less), ("1319006.0491408208640440532992", "-0.00000000000000611866432", Greater), ("-1319006.0491408208640440532992", "-0.00000000000000611866432", Less), // 001 010 same scale ("316007568232.9263258873102336", "159.1054215143227392", Greater), ("-316007568232.9263258873102336", "159.1054215143227392", Less), ("316007568232.9263258873102336", "-159.1054215143227392", Greater), ("-316007568232.9263258873102336", "-159.1054215143227392", Less), // 001 010 different scale ("2470144.7146711063666704252928", "211.0186916505714688", Greater), ("-2470144.7146711063666704252928", "211.0186916505714688", Less), ("2470144.7146711063666704252928", "-211.0186916505714688", Greater), ("-2470144.7146711063666704252928", "-211.0186916505714688", Less), // 001 110 same scale ("316007568232.9263258873102336", "15.1186658969096112", Greater), ("-316007568232.9263258873102336", "15.1186658969096112", Less), ("316007568232.9263258873102336", "-15.1186658969096112", Greater), ("-316007568232.9263258873102336", "-15.1186658969096112", Less), // 001 110 different scale ("3840504199004148630832.3360768", "7581138850996748864", Greater), ("-3840504199004148630832.3360768", "7581138850996748864", Less), ("3840504199004148630832.3360768", "-7581138850996748864", Greater), ("-3840504199004148630832.3360768", "-7581138850996748864", Less), // 001 001 same scale ("316007568232.9263258873102336", "810157633226.6053390856880128", Less), ("-316007568232.9263258873102336", "810157633226.6053390856880128", Less), ( "316007568232.9263258873102336", "-810157633226.6053390856880128", Greater, ), ( "-316007568232.9263258873102336", "-810157633226.6053390856880128", Greater, ), // 001 001 different scale ("1951046382014.4037956952260608", "3626102772868740412010083.1232", Less), ( "-1951046382014.4037956952260608", "3626102772868740412010083.1232", Less, ), ( "1951046382014.4037956952260608", "-3626102772868740412010083.1232", Greater, ), ( "-1951046382014.4037956952260608", "-3626102772868740412010083.1232", Greater, ), // 001 101 same scale ("316007568232.9263258873102336", "3258394380359.1965879291312453", Less), ("-316007568232.9263258873102336", "3258394380359.1965879291312453", Less), ( "316007568232.9263258873102336", "-3258394380359.1965879291312453", Greater, ), ( "-316007568232.9263258873102336", "-3258394380359.1965879291312453", Greater, ), // 001 101 different scale ( "17580513970289834.943527780352", "3.7977957031395371036126086595", Greater, ), ( "-17580513970289834.943527780352", "3.7977957031395371036126086595", Less, ), ( "17580513970289834.943527780352", "-3.7977957031395371036126086595", Greater, ), ( "-17580513970289834.943527780352", "-3.7977957031395371036126086595", Less, ), // 001 011 same scale ("316007568232.9263258873102336", "1154574080460.9867510617997312", Less), ("-316007568232.9263258873102336", "1154574080460.9867510617997312", Less), ( "316007568232.9263258873102336", "-1154574080460.9867510617997312", Greater, ), ( "-316007568232.9263258873102336", "-1154574080460.9867510617997312", Greater, ), // 001 011 different scale ("2008379587.5525351789031325696", "32824109460554.341800487157760", Less), ( "-2008379587.5525351789031325696", "32824109460554.341800487157760", Less, ), ( "2008379587.5525351789031325696", "-32824109460554.341800487157760", Greater, ), ( "-2008379587.5525351789031325696", "-32824109460554.341800487157760", Greater, ), // 001 111 same scale ("316007568232.9263258873102336", "2816795479724.6069787794805311", Less), ("-316007568232.9263258873102336", "2816795479724.6069787794805311", Less), ( "316007568232.9263258873102336", "-2816795479724.6069787794805311", Greater, ), ( "-316007568232.9263258873102336", "-2816795479724.6069787794805311", Greater, ), // 001 111 different scale ( "3536806574745420013541890.4576", "9793146.81730411145833529126", Greater, ), ("-3536806574745420013541890.4576", "9793146.81730411145833529126", Less), ( "3536806574745420013541890.4576", "-9793146.81730411145833529126", Greater, ), ("-3536806574745420013541890.4576", "-9793146.81730411145833529126", Less), // 101 equality ( "254208186622762823842.71629992", "254208186622762823842.71629992", Equal, ), // 101 000 same scale ("254208186622762823842.71629992", "0.00000000", Greater), ("-254208186622762823842.71629992", "0.00000000", Less), ("254208186622762823842.71629992", "-0.00000000", Greater), ("-254208186622762823842.71629992", "-0.00000000", Less), // 101 000 different scale ("975421950664039.3614304091804", "0.0000000000000000", Greater), ("-975421950664039.3614304091804", "0.0000000000000000", Less), ("975421950664039.3614304091804", "-0.0000000000000000", Greater), ("-975421950664039.3614304091804", "-0.0000000000000000", Less), // 101 100 same scale ("254208186622762823842.71629992", "10.74141379", Greater), ("-254208186622762823842.71629992", "10.74141379", Less), ("254208186622762823842.71629992", "-10.74141379", Greater), ("-254208186622762823842.71629992", "-10.74141379", Less), // 101 100 different scale ("2552221405032275.8358630506229", "0.000000000000000592656995", Greater), ("-2552221405032275.8358630506229", "0.000000000000000592656995", Less), ("2552221405032275.8358630506229", "-0.000000000000000592656995", Greater), ("-2552221405032275.8358630506229", "-0.000000000000000592656995", Less), // 101 010 same scale ("254208186622762823842.71629992", "62767493748.48499712", Greater), ("-254208186622762823842.71629992", "62767493748.48499712", Less), ("254208186622762823842.71629992", "-62767493748.48499712", Greater), ("-254208186622762823842.71629992", "-62767493748.48499712", Less), // 101 010 different scale ("197346074219.25327589999174264", "0.7623493575178715136", Greater), ("-197346074219.25327589999174264", "0.7623493575178715136", Less), ("197346074219.25327589999174264", "-0.7623493575178715136", Greater), ("-197346074219.25327589999174264", "-0.7623493575178715136", Less), // 101 110 same scale ("254208186622762823842.71629992", "76597126194.51389094", Greater), ("-254208186622762823842.71629992", "76597126194.51389094", Less), ("254208186622762823842.71629992", "-76597126194.51389094", Greater), ("-254208186622762823842.71629992", "-76597126194.51389094", Less), // 101 110 different scale ("25899773651648.380071130043467", "61306258142804903.38", Less), ("-25899773651648.380071130043467", "61306258142804903.38", Less), ("25899773651648.380071130043467", "-61306258142804903.38", Greater), ("-25899773651648.380071130043467", "-61306258142804903.38", Greater), // 101 001 same scale ( "254208186622762823842.71629992", "83547191565151621967.28086528", Greater, ), ("-254208186622762823842.71629992", "83547191565151621967.28086528", Less), ( "254208186622762823842.71629992", "-83547191565151621967.28086528", Greater, ), ( "-254208186622762823842.71629992", "-83547191565151621967.28086528", Less, ), // 101 001 different scale ("244762.90302171293318719286219", "9964072.600255221193011888128", Less), ("-244762.90302171293318719286219", "9964072.600255221193011888128", Less), ( "244762.90302171293318719286219", "-9964072.600255221193011888128", Greater, ), ( "-244762.90302171293318719286219", "-9964072.600255221193011888128", Greater, ), // 101 101 same scale ( "254208186622762823842.71629992", "106541875981662806348.63716235", Greater, ), ( "-254208186622762823842.71629992", "106541875981662806348.63716235", Less, ), ( "254208186622762823842.71629992", "-106541875981662806348.63716235", Greater, ), ( "-254208186622762823842.71629992", "-106541875981662806348.63716235", Less, ), // 101 101 different scale ("362319.18250030256385507568342", "3619454249020577423546109236", Less), ("-362319.18250030256385507568342", "3619454249020577423546109236", Less), ( "362319.18250030256385507568342", "-3619454249020577423546109236", Greater, ), ( "-362319.18250030256385507568342", "-3619454249020577423546109236", Greater, ), // 101 011 same scale ( "254208186622762823842.71629992", "156781478378762688050.06557184", Greater, ), ( "-254208186622762823842.71629992", "156781478378762688050.06557184", Less, ), ( "254208186622762823842.71629992", "-156781478378762688050.06557184", Greater, ), ( "-254208186622762823842.71629992", "-156781478378762688050.06557184", Less, ), // 101 011 different scale ("2486073465266.0337130589931876", "153874906950888902858.19691008", Less), ( "-2486073465266.0337130589931876", "153874906950888902858.19691008", Less, ), ( "2486073465266.0337130589931876", "-153874906950888902858.19691008", Greater, ), ( "-2486073465266.0337130589931876", "-153874906950888902858.19691008", Greater, ), // 101 111 same scale ( "254208186622762823842.71629992", "10101645723744656462.08148676", Greater, ), ("-254208186622762823842.71629992", "10101645723744656462.08148676", Less), ( "254208186622762823842.71629992", "-10101645723744656462.08148676", Greater, ), ( "-254208186622762823842.71629992", "-10101645723744656462.08148676", Less, ), // 101 111 different scale ("14107601965.909635434653634526", "111238758546502973973477.99454", Less), ( "-14107601965.909635434653634526", "111238758546502973973477.99454", Less, ), ( "14107601965.909635434653634526", "-111238758546502973973477.99454", Greater, ), ( "-14107601965.909635434653634526", "-111238758546502973973477.99454", Greater, ), // 011 equality ( "272322.48219624218537039495168", "272322.48219624218537039495168", Equal, ), // 011 000 same scale ("272322.48219624218537039495168", "0.00000000000000000000000", Greater), ("-272322.48219624218537039495168", "0.00000000000000000000000", Less), ("272322.48219624218537039495168", "-0.00000000000000000000000", Greater), ("-272322.48219624218537039495168", "-0.00000000000000000000000", Less), // 011 000 different scale ("3214885516.0787854158246969344", "0.00000000000000000", Greater), ("-3214885516.0787854158246969344", "0.00000000000000000", Less), ("3214885516.0787854158246969344", "-0.00000000000000000", Greater), ("-3214885516.0787854158246969344", "-0.00000000000000000", Less), // 011 100 same scale ("272322.48219624218537039495168", "0.00000000000000379487994", Greater), ("-272322.48219624218537039495168", "0.00000000000000379487994", Less), ("272322.48219624218537039495168", "-0.00000000000000379487994", Greater), ("-272322.48219624218537039495168", "-0.00000000000000379487994", Less), // 011 100 different scale ("388166715906.19371912596291584", "0.000000000000001996700736", Greater), ("-388166715906.19371912596291584", "0.000000000000001996700736", Less), ("388166715906.19371912596291584", "-0.000000000000001996700736", Greater), ("-388166715906.19371912596291584", "-0.000000000000001996700736", Less), // 011 010 same scale ("272322.48219624218537039495168", "0.00000175873997328613376", Greater), ("-272322.48219624218537039495168", "0.00000175873997328613376", Less), ("272322.48219624218537039495168", "-0.00000175873997328613376", Greater), ("-272322.48219624218537039495168", "-0.00000175873997328613376", Less), // 011 010 different scale ( "17963864.0946434527121637376", "0.0000000001112699367808040960", Greater, ), ("-17963864.0946434527121637376", "0.0000000001112699367808040960", Less), ( "17963864.0946434527121637376", "-0.0000000001112699367808040960", Greater, ), ("-17963864.0946434527121637376", "-0.0000000001112699367808040960", Less), // 011 110 same scale ("272322.48219624218537039495168", "0.00009168252278596474115", Greater), ("-272322.48219624218537039495168", "0.00009168252278596474115", Less), ("272322.48219624218537039495168", "-0.00009168252278596474115", Greater), ("-272322.48219624218537039495168", "-0.00009168252278596474115", Less), // 011 110 different scale ("3.1530040332824324172729548800", "0.05636139104712652784", Greater), ("-3.1530040332824324172729548800", "0.05636139104712652784", Less), ("3.1530040332824324172729548800", "-0.05636139104712652784", Greater), ("-3.1530040332824324172729548800", "-0.05636139104712652784", Less), // 011 001 same scale ( "272322.48219624218537039495168", "78956.15667671475190631497728", Greater, ), ("-272322.48219624218537039495168", "78956.15667671475190631497728", Less), ( "272322.48219624218537039495168", "-78956.15667671475190631497728", Greater, ), ( "-272322.48219624218537039495168", "-78956.15667671475190631497728", Less, ), // 011 001 different scale ( "247159638929.90774677497446400", "18573048.37697991870462820352", Greater, ), ("-247159638929.90774677497446400", "18573048.37697991870462820352", Less), ( "247159638929.90774677497446400", "-18573048.37697991870462820352", Greater, ), ( "-247159638929.90774677497446400", "-18573048.37697991870462820352", Less, ), // 011 101 same scale ("272322.48219624218537039495168", "311953.28803357654172947915942", Less), ( "-272322.48219624218537039495168", "311953.28803357654172947915942", Less, ), ( "272322.48219624218537039495168", "-311953.28803357654172947915942", Greater, ), ( "-272322.48219624218537039495168", "-311953.28803357654172947915942", Greater, ), // 011 101 different scale ("2922696937234119470.0273745920", "22441101906503785827606686629", Less), ("-2922696937234119470.0273745920", "22441101906503785827606686629", Less), ( "2922696937234119470.0273745920", "-22441101906503785827606686629", Greater, ), ( "-2922696937234119470.0273745920", "-22441101906503785827606686629", Greater, ), // 011 011 same scale ("272322.48219624218537039495168", "348316.28306497394164006649856", Less), ( "-272322.48219624218537039495168", "348316.28306497394164006649856", Less, ), ( "272322.48219624218537039495168", "-348316.28306497394164006649856", Greater, ), ( "-272322.48219624218537039495168", "-348316.28306497394164006649856", Greater, ), // 011 011 different scale ("178190346.76624086261395619840", "62208030746.22038852927225856", Less), ("-178190346.76624086261395619840", "62208030746.22038852927225856", Less), ( "178190346.76624086261395619840", "-62208030746.22038852927225856", Greater, ), ( "-178190346.76624086261395619840", "-62208030746.22038852927225856", Greater, ), // 011 111 same scale ( "272322.48219624218537039495168", "41534.52021391898039335355927", Greater, ), ("-272322.48219624218537039495168", "41534.52021391898039335355927", Less), ( "272322.48219624218537039495168", "-41534.52021391898039335355927", Greater, ), ( "-272322.48219624218537039495168", "-41534.52021391898039335355927", Less, ), // 011 111 different scale ("11.959910518519677083499626496", "2844684364802261541879551.2259", Less), ( "-11.959910518519677083499626496", "2844684364802261541879551.2259", Less, ), ( "11.959910518519677083499626496", "-2844684364802261541879551.2259", Greater, ), ( "-11.959910518519677083499626496", "-2844684364802261541879551.2259", Greater, ), // 111 equality ( "3836286746260530032892706.6174", "3836286746260530032892706.6174", Equal, ), // 111 000 same scale ("3836286746260530032892706.6174", "0.0000", Greater), ("-3836286746260530032892706.6174", "0.0000", Less), ("3836286746260530032892706.6174", "-0.0000", Greater), ("-3836286746260530032892706.6174", "-0.0000", Less), // 111 000 different scale ("4401861854803552.033657814547", "0.0000000", Greater), ("-4401861854803552.033657814547", "0.0000000", Less), ("4401861854803552.033657814547", "-0.0000000", Greater), ("-4401861854803552.033657814547", "-0.0000000", Less), // 111 100 same scale ("3836286746260530032892706.6174", "68758.6561", Greater), ("-3836286746260530032892706.6174", "68758.6561", Less), ("3836286746260530032892706.6174", "-68758.6561", Greater), ("-3836286746260530032892706.6174", "-68758.6561", Less), // 111 100 different scale ( "18794337354296131695536777.153", "0.000000000000000001563875977", Greater, ), ("-18794337354296131695536777.153", "0.000000000000000001563875977", Less), ( "18794337354296131695536777.153", "-0.000000000000000001563875977", Greater, ), ( "-18794337354296131695536777.153", "-0.000000000000000001563875977", Less, ), // 111 010 same scale ("3836286746260530032892706.6174", "439097665563236.7616", Greater), ("-3836286746260530032892706.6174", "439097665563236.7616", Less), ("3836286746260530032892706.6174", "-439097665563236.7616", Greater), ("-3836286746260530032892706.6174", "-439097665563236.7616", Less), // 111 010 different scale ("219364497389.57405761662363679", "0.6569644274462228480", Greater), ("-219364497389.57405761662363679", "0.6569644274462228480", Less), ("219364497389.57405761662363679", "-0.6569644274462228480", Greater), ("-219364497389.57405761662363679", "-0.6569644274462228480", Less), // 111 110 same scale ("3836286746260530032892706.6174", "100013274294974.8269", Greater), ("-3836286746260530032892706.6174", "100013274294974.8269", Less), ("3836286746260530032892706.6174", "-100013274294974.8269", Greater), ("-3836286746260530032892706.6174", "-100013274294974.8269", Less), // 111 110 different scale ("76072704083682.85472479207171", "50.52437989651117182", Greater), ("-76072704083682.85472479207171", "50.52437989651117182", Less), ("76072704083682.85472479207171", "-50.52437989651117182", Greater), ("-76072704083682.85472479207171", "-50.52437989651117182", Less), // 111 001 same scale ( "3836286746260530032892706.6174", "2766133872545894272402142.0032", Greater, ), ( "-3836286746260530032892706.6174", "2766133872545894272402142.0032", Less, ), ( "3836286746260530032892706.6174", "-2766133872545894272402142.0032", Greater, ), ( "-3836286746260530032892706.6174", "-2766133872545894272402142.0032", Less, ), // 111 001 different scale ( "38199979438250010.80610984395", "31104752430710.408848162684928", Greater, ), ("-38199979438250010.80610984395", "31104752430710.408848162684928", Less), ( "38199979438250010.80610984395", "-31104752430710.408848162684928", Greater, ), ( "-38199979438250010.80610984395", "-31104752430710.408848162684928", Less, ), // 111 101 same scale ( "3836286746260530032892706.6174", "441847458119168110406908.6115", Greater, ), ("-3836286746260530032892706.6174", "441847458119168110406908.6115", Less), ( "3836286746260530032892706.6174", "-441847458119168110406908.6115", Greater, ), ( "-3836286746260530032892706.6174", "-441847458119168110406908.6115", Less, ), // 111 101 different scale ("255945012905633524.15746865235", "1005021647855597114428453997.8", Less), ( "-255945012905633524.15746865235", "1005021647855597114428453997.8", Less, ), ( "255945012905633524.15746865235", "-1005021647855597114428453997.8", Greater, ), ( "-255945012905633524.15746865235", "-1005021647855597114428453997.8", Greater, ), // 111 011 same scale ( "3836286746260530032892706.6174", "1111481055212557787730018.3040", Greater, ), ( "-3836286746260530032892706.6174", "1111481055212557787730018.3040", Less, ), ( "3836286746260530032892706.6174", "-1111481055212557787730018.3040", Greater, ), ( "-3836286746260530032892706.6174", "-1111481055212557787730018.3040", Less, ), // 111 011 different scale ( "79710135995301690627798250.27", "4613684285077.479267304996864", Greater, ), ("-79710135995301690627798250.27", "4613684285077.479267304996864", Less), ( "79710135995301690627798250.27", "-4613684285077.479267304996864", Greater, ), ("-79710135995301690627798250.27", "-4613684285077.479267304996864", Less), // 111 111 same scale ( "3836286746260530032892706.6174", "1881105048659612897896770.8539", Greater, ), ( "-3836286746260530032892706.6174", "1881105048659612897896770.8539", Less, ), ( "3836286746260530032892706.6174", "-1881105048659612897896770.8539", Greater, ), ( "-3836286746260530032892706.6174", "-1881105048659612897896770.8539", Less, ), // 111 111 different scale ( "3879592276836332218003.2886500", "35612499407667292.686490959658", Greater, ), ( "-3879592276836332218003.2886500", "35612499407667292.686490959658", Less, ), ( "3879592276836332218003.2886500", "-35612499407667292.686490959658", Greater, ), ( "-3879592276836332218003.2886500", "-35612499407667292.686490959658", Less, ), ]; for &(a, b, c) in tests { cmp(a, b, c); } } #[test] fn it_floors_decimals() { let tests = &[ ("1", "1"), ("1.00", "1"), ("1.2345", "1"), ("-1", "-1"), ("-1.00", "-1"), ("-1.2345", "-2"), ]; for &(a, expected) in tests { let a = Decimal::from_str(a).unwrap(); assert_eq!(expected, a.floor().to_string(), "Failed flooring {}", a); } } #[test] fn it_ceils_decimals() { let tests = &[ ("1", "1"), ("1.00", "1"), ("1.2345", "2"), ("-1", "-1"), ("-1.00", "-1"), ("-1.2345", "-1"), ]; for &(a, expected) in tests { let a = Decimal::from_str(a).unwrap(); assert_eq!(expected, a.ceil().to_string(), "Failed ceiling {}", a); } } #[test] fn it_finds_max_of_two() { let tests = &[("1", "1", "1"), ("2", "1", "2"), ("1", "2", "2")]; for &(a, b, expected) in tests { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); assert_eq!(expected, a.max(b).to_string()); } } #[test] fn it_finds_min_of_two() { let tests = &[("1", "1", "1"), ("2", "1", "1"), ("1", "2", "1")]; for &(a, b, expected) in tests { let a = Decimal::from_str(a).unwrap(); let b = Decimal::from_str(b).unwrap(); assert_eq!(expected, a.min(b).to_string()); } } #[test] fn test_max_compares() { let x = "225.33543601344182".parse::().unwrap(); let y = Decimal::MAX; assert!(x < y); assert!(y > x); assert_ne!(y, x); } #[test] fn test_min_compares() { let x = "225.33543601344182".parse::().unwrap(); let y = Decimal::MIN; assert!(x > y); assert!(y < x); assert_ne!(y, x); } #[test] fn it_can_parse_from_i32() { use num_traits::FromPrimitive; let tests = &[ (0i32, "0"), (1i32, "1"), (-1i32, "-1"), (i32::MAX, "2147483647"), (i32::MIN, "-2147483648"), ]; for &(input, expected) in tests { let parsed = Decimal::from_i32(input).unwrap(); assert_eq!( expected, parsed.to_string(), "expected {} does not match parsed {}", expected, parsed ); assert_eq!( input.to_string(), parsed.to_string(), "i32 to_string {} does not match parsed {}", input, parsed ); } } #[test] fn it_can_parse_from_i64() { use num_traits::FromPrimitive; let tests = &[ (0i64, "0"), (1i64, "1"), (-1i64, "-1"), (i64::MAX, "9223372036854775807"), (i64::MIN, "-9223372036854775808"), ]; for &(input, expected) in tests { let parsed = Decimal::from_i64(input).unwrap(); assert_eq!( expected, parsed.to_string(), "expected {} does not match parsed {}", expected, parsed ); assert_eq!( input.to_string(), parsed.to_string(), "i64 to_string {} does not match parsed {}", input, parsed ); } } #[test] fn it_can_round_to_2dp() { let a = Decimal::from_str("6.12345").unwrap(); let b = (Decimal::from_str("100").unwrap() * a).round() / Decimal::from_str("100").unwrap(); assert_eq!("6.12", b.to_string()); } #[test] fn it_can_round_using_basic_midpoint_rules() { let tests = &[ ("3.5", RoundingStrategy::MidpointAwayFromZero, "4"), ("2.8", RoundingStrategy::MidpointAwayFromZero, "3"), ("2.5", RoundingStrategy::MidpointAwayFromZero, "3"), ("2.1", RoundingStrategy::MidpointAwayFromZero, "2"), ("-2.1", RoundingStrategy::MidpointAwayFromZero, "-2"), ("-2.5", RoundingStrategy::MidpointAwayFromZero, "-3"), ("-2.8", RoundingStrategy::MidpointAwayFromZero, "-3"), ("-3.5", RoundingStrategy::MidpointAwayFromZero, "-4"), ("3.5", RoundingStrategy::MidpointNearestEven, "4"), ("2.8", RoundingStrategy::MidpointNearestEven, "3"), ("2.5", RoundingStrategy::MidpointNearestEven, "2"), ("2.1", RoundingStrategy::MidpointNearestEven, "2"), ("-2.1", RoundingStrategy::MidpointNearestEven, "-2"), ("-2.5", RoundingStrategy::MidpointNearestEven, "-2"), ("-2.8", RoundingStrategy::MidpointNearestEven, "-3"), ("-3.5", RoundingStrategy::MidpointNearestEven, "-4"), ("3.5", RoundingStrategy::MidpointTowardZero, "3"), ("2.8", RoundingStrategy::MidpointTowardZero, "3"), ("2.5", RoundingStrategy::MidpointTowardZero, "2"), ("2.1", RoundingStrategy::MidpointTowardZero, "2"), ("-2.1", RoundingStrategy::MidpointTowardZero, "-2"), ("-2.5", RoundingStrategy::MidpointTowardZero, "-2"), ("-2.8", RoundingStrategy::MidpointTowardZero, "-3"), ("-3.5", RoundingStrategy::MidpointTowardZero, "-3"), ("2.8", RoundingStrategy::ToNegativeInfinity, "2"), ("2.5", RoundingStrategy::ToNegativeInfinity, "2"), ("2.1", RoundingStrategy::ToNegativeInfinity, "2"), ("-2.1", RoundingStrategy::ToNegativeInfinity, "-3"), ("-2.5", RoundingStrategy::ToNegativeInfinity, "-3"), ("-2.8", RoundingStrategy::ToNegativeInfinity, "-3"), ("2.8", RoundingStrategy::ToPositiveInfinity, "3"), ("2.5", RoundingStrategy::ToPositiveInfinity, "3"), ("2.1", RoundingStrategy::ToPositiveInfinity, "3"), ("-2.1", RoundingStrategy::ToPositiveInfinity, "-2"), ("-2.5", RoundingStrategy::ToPositiveInfinity, "-2"), ("-2.8", RoundingStrategy::ToPositiveInfinity, "-2"), ("2.8", RoundingStrategy::ToZero, "2"), ("2.5", RoundingStrategy::ToZero, "2"), ("2.1", RoundingStrategy::ToZero, "2"), ("-2.1", RoundingStrategy::ToZero, "-2"), ("-2.5", RoundingStrategy::ToZero, "-2"), ("-2.8", RoundingStrategy::ToZero, "-2"), ("2.8", RoundingStrategy::AwayFromZero, "3"), ("2.5", RoundingStrategy::AwayFromZero, "3"), ("2.1", RoundingStrategy::AwayFromZero, "3"), ("-2.1", RoundingStrategy::AwayFromZero, "-3"), ("-2.5", RoundingStrategy::AwayFromZero, "-3"), ("-2.8", RoundingStrategy::AwayFromZero, "-3"), ]; for &(input, strategy, expected) in tests { let a = Decimal::from_str(input).unwrap(); let b = a.round_dp_with_strategy(0, strategy); assert_eq!(expected, b.to_string(), "{} > {} for {:?}", input, expected, strategy); } } #[test] fn it_can_round_using_bankers_rounding() { let tests = &[ ("6.12345", 2, "6.12"), ("6.126", 2, "6.13"), ("-6.126", 2, "-6.13"), ("6.5", 0, "6"), ("7.5", 0, "8"), ("1.2250", 2, "1.22"), ("1.2252", 2, "1.23"), ("1.2249", 2, "1.22"), ("6.1", 2, "6.1"), ("0.0000", 2, "0.00"), ("0.6666666666666666666666666666", 2, "0.67"), ("1.40", 0, "1"), ("2.60", 0, "3"), ("2.1234567890123456789012345678", 27, "2.123456789012345678901234568"), ]; for &(input, dp, expected) in tests { let a = Decimal::from_str(input).unwrap(); #[allow(deprecated)] let b = a.round_dp_with_strategy(dp, RoundingStrategy::BankersRounding); assert_eq!(expected, b.to_string(), "BankersRounding"); // Recommended replacement let b = a.round_dp_with_strategy(dp, RoundingStrategy::MidpointNearestEven); assert_eq!(expected, b.to_string(), "MidpointNearestEven"); } } #[test] fn it_can_round_complex_numbers_using_bankers_rounding() { // Issue #71 let rate = Decimal::new(19, 2); // 0.19 let one = Decimal::new(1, 0); // 1 let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 #[allow(deprecated)] let part = part.round_dp_with_strategy(2, RoundingStrategy::BankersRounding); // 0.16 assert_eq!("0.16", part.to_string(), "BankersRounding"); // Recommended replacement let part = part.round_dp_with_strategy(2, RoundingStrategy::MidpointNearestEven); // 0.16 assert_eq!("0.16", part.to_string(), "MidpointNearestEven"); } #[test] fn it_can_round_using_round_half_up() { let tests = &[ ("0", 0, "0"), ("1.234", 3, "1.234"), ("1.12", 5, "1.12"), ("6.34567", 2, "6.35"), ("6.5", 0, "7"), ("12.49", 0, "12"), ("0.6666666666666666666666666666", 2, "0.67"), ("1.40", 0, "1"), ("2.60", 0, "3"), ("2.1234567890123456789012345678", 27, "2.123456789012345678901234568"), ]; for &(input, dp, expected) in tests { let a = Decimal::from_str(input).unwrap(); #[allow(deprecated)] let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundHalfUp); assert_eq!(expected, b.to_string(), "RoundHalfUp"); // Recommended replacement let b = a.round_dp_with_strategy(dp, RoundingStrategy::MidpointAwayFromZero); assert_eq!(expected, b.to_string(), "MidpointAwayFromZero"); } } #[test] fn it_can_round_complex_numbers_using_round_half_up() { // Issue #71 let rate = Decimal::new(19, 2); // 0.19 let one = Decimal::new(1, 0); // 1 let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 #[allow(deprecated)] let part = part.round_dp_with_strategy(2, RoundingStrategy::RoundHalfUp); // 0.16 assert_eq!("0.16", part.to_string(), "RoundHalfUp"); // Recommended replacement let part = part.round_dp_with_strategy(2, RoundingStrategy::MidpointAwayFromZero); // 0.16 assert_eq!("0.16", part.to_string(), "MidpointAwayFromZero"); } #[test] fn it_can_round_using_round_half_down() { let tests = &[ ("0", 0, "0"), ("1.234", 3, "1.234"), ("1.12", 5, "1.12"), ("6.34567", 2, "6.35"), ("6.51", 0, "7"), ("12.5", 0, "12"), ("0.6666666666666666666666666666", 2, "0.67"), ("1.40", 0, "1"), ("2.60", 0, "3"), ("2.1234567890123456789012345678", 27, "2.123456789012345678901234568"), ]; for &(input, dp, expected) in tests { let a = Decimal::from_str(input).unwrap(); #[allow(deprecated)] let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundHalfDown); assert_eq!(expected, b.to_string(), "RoundHalfDown"); // Recommended replacement let b = a.round_dp_with_strategy(dp, RoundingStrategy::MidpointTowardZero); assert_eq!(expected, b.to_string(), "MidpointTowardZero"); } } #[test] fn it_can_round_complex_numbers_using_round_half_down() { // Issue #71 let rate = Decimal::new(19, 2); // 0.19 let one = Decimal::new(1, 0); // 1 let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 #[allow(deprecated)] let part = part.round_dp_with_strategy(2, RoundingStrategy::RoundHalfDown); // 0.16 assert_eq!("0.16", part.to_string(), "RoundHalfDown"); // Recommended replacement let part = part.round_dp_with_strategy(2, RoundingStrategy::MidpointTowardZero); // 0.16 assert_eq!("0.16", part.to_string(), "RoundHalfDown"); } #[test] fn it_can_round_to_2dp_using_explicit_function() { let a = Decimal::from_str("6.12345").unwrap(); let b = a.round_dp(2u32); assert_eq!("6.12", b.to_string()); } #[test] fn it_can_round_up_to_2dp_using_explicit_function() { let a = Decimal::from_str("6.126").unwrap(); let b = a.round_dp(2u32); assert_eq!("6.13", b.to_string()); } #[test] fn it_can_round_down_to_2dp_using_explicit_function() { let a = Decimal::from_str("-6.126").unwrap(); let b = a.round_dp(2u32); assert_eq!("-6.13", b.to_string()); } #[test] fn it_can_round_down_using_bankers_rounding() { let a = Decimal::from_str("6.5").unwrap(); let b = a.round_dp(0u32); assert_eq!("6", b.to_string()); } #[test] fn it_can_round_up_using_bankers_rounding() { let a = Decimal::from_str("7.5").unwrap(); let b = a.round_dp(0u32); assert_eq!("8", b.to_string()); } #[test] fn it_can_round_correctly_using_bankers_rounding_1() { let a = Decimal::from_str("1.2250").unwrap(); let b = a.round_dp(2u32); assert_eq!("1.22", b.to_string()); } #[test] fn it_can_round_correctly_using_bankers_rounding_2() { let a = Decimal::from_str("1.2251").unwrap(); let b = a.round_dp(2u32); assert_eq!("1.23", b.to_string()); } #[test] fn it_can_round_down_when_required() { let a = Decimal::from_str("1.2249").unwrap(); let b = a.round_dp(2u32); assert_eq!("1.22", b.to_string()); } #[test] fn it_can_round_to_2dp_using_explicit_function_without_changing_value() { let a = Decimal::from_str("6.1").unwrap(); let b = a.round_dp(2u32); assert_eq!("6.1", b.to_string()); } #[test] fn it_can_round_zero() { let a = Decimal::from_str("0.0000").unwrap(); let b = a.round_dp(2u32); assert_eq!("0.00", b.to_string()); } #[test] fn it_can_round_large_decimals() { let a = Decimal::from_str("0.6666666666666666666666666666").unwrap(); let b = a.round_dp(2u32); assert_eq!("0.67", b.to_string()); } #[test] fn it_can_round_simple_numbers_down() { let a = Decimal::from_str("1.40").unwrap(); let b = a.round_dp(0u32); assert_eq!("1", b.to_string()); } #[test] fn it_can_round_simple_numbers_up() { let a = Decimal::from_str("2.60").unwrap(); let b = a.round_dp(0u32); assert_eq!("3", b.to_string()); } #[test] fn it_can_round_simple_numbers_with_high_precision() { let a = Decimal::from_str("2.1234567890123456789012345678").unwrap(); let b = a.round_dp(27u32); assert_eq!("2.123456789012345678901234568", b.to_string()); } #[test] fn it_can_round_complex_numbers() { // Issue #71 let rate = Decimal::new(19, 2); // 0.19 let one = Decimal::new(1, 0); // 1 let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 let part = part.round_dp(2); // 0.16 assert_eq!("0.16", part.to_string()); } #[test] fn it_can_round_down() { let tests = &[ ("0.470", 1, "0.4"), ("-0.470", 1, "-0.4"), // Toward zero ("0.400", 1, "0.4"), ("-0.400", 1, "-0.4"), ]; for &(input, dp, expected) in tests { let a = Decimal::from_str(input).unwrap(); #[allow(deprecated)] let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundDown); assert_eq!(expected, b.to_string(), "RoundDown"); // Recommended replacement let b = a.round_dp_with_strategy(dp, RoundingStrategy::ToZero); assert_eq!(expected, b.to_string(), "ToZero"); } } #[test] fn it_can_round_up() { let tests = &[ ("2.8", 0, "3"), ("2.5", 0, "3"), ("2.1", 0, "3"), ("-2.1", 0, "-3"), ("-2.5", 0, "-3"), ("-2.8", 0, "-3"), ("0.320", 1, "0.4"), ("-0.320", 1, "-0.4"), ("0.300", 1, "0.3"), ("-0.300", 1, "-0.3"), ]; for &(input, dp, expected) in tests { let a = Decimal::from_str(input).unwrap(); #[allow(deprecated)] let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundUp); assert_eq!(expected, b.to_string(), "RoundUp"); // Recommended replacement let b = a.round_dp_with_strategy(dp, RoundingStrategy::AwayFromZero); assert_eq!(expected, b.to_string(), "AwayFromZero"); } } #[test] fn it_can_round_significant_figures() { let tests = &[ ("305.459", 0u32, Some("0")), ("305.459", 1, Some("300")), ("305.459", 2, Some("310")), ("305.459", 3, Some("305")), ("305.459", 4, Some("305.5")), ("305.459", 5, Some("305.46")), ("305.459", 6, Some("305.459")), ("305.459", 7, Some("305.4590")), ("305.459", 10, Some("305.4590000")), ("-305.459", 3, Some("-305")), ("-305.459", 2, Some("-310")), // We ignore the negative ("-305.459", 5, Some("-305.46")), ( "79228162514264337593543950335", 29, Some("79228162514264337593543950335"), ), ("79228162514264337593543950335", 1, None), ( "79228162514264337593543950335", 2, Some("79000000000000000000000000000"), ), ( "79228162514264337593543950335", 30, Some("79228162514264337593543950335"), ), ( "79228162514264337593543950335", u32::MAX, Some("79228162514264337593543950335"), ), ]; for &(input, sf, expected) in tests { let input = Decimal::from_str(input).unwrap(); let result = input.round_sf(sf); if let Some(expected) = expected { assert!(result.is_some(), "Expected result for {}.round_sf({})", input, sf); assert_eq!(expected, result.unwrap().to_string(), "{}.round_sf({})", input, sf); } else { assert!(result.is_none(), "Unexpected result for {}.round_sf({})", input, sf); } } } #[test] fn it_can_round_significant_figures_with_strategy() { let tests = &[ ("12301", 3u32, RoundingStrategy::AwayFromZero, Some("12400")), ("123.01", 3u32, RoundingStrategy::AwayFromZero, Some("124")), ("1.2301", 3u32, RoundingStrategy::AwayFromZero, Some("1.24")), ("0.12301", 3u32, RoundingStrategy::AwayFromZero, Some("0.124")), ("0.012301", 3u32, RoundingStrategy::AwayFromZero, Some("0.0124")), ("0.0000012301", 3u32, RoundingStrategy::AwayFromZero, Some("0.00000124")), ("1.012301", 3u32, RoundingStrategy::AwayFromZero, Some("1.02")), ]; for &(input, sf, strategy, expected) in tests { let input = Decimal::from_str(input).unwrap(); let result = input.round_sf_with_strategy(sf, strategy); if let Some(expected) = expected { assert!( result.is_some(), "Expected result for {}.round_sf_with_strategy({}, {:?})", input, sf, strategy ); assert_eq!( expected, result.unwrap().to_string(), "{}.round_sf_with_strategy({}, {:?})", input, sf, strategy ); } else { assert!( result.is_none(), "Unexpected result for {}.round_sf_with_strategy({}, {:?})", input, sf, strategy ); } } } #[test] fn it_can_trunc() { let tests = &[("1.00000000000000000000", "1"), ("1.000000000000000000000001", "1")]; for &(value, expected) in tests { let value = Decimal::from_str(value).unwrap(); let expected = Decimal::from_str(expected).unwrap(); let trunc = value.trunc(); assert_eq!(expected.to_string(), trunc.to_string()); } } #[test] fn it_can_fract() { let tests = &[ ("1.00000000000000000000", "0.00000000000000000000"), ("1.000000000000000000000001", "0.000000000000000000000001"), ]; for &(value, expected) in tests { let value = Decimal::from_str(value).unwrap(); let expected = Decimal::from_str(expected).unwrap(); let fract = value.fract(); assert_eq!(expected.to_string(), fract.to_string()); } } #[test] fn it_can_normalize() { let tests = &[ ("1.00000000000000000000", "1"), ("1.10000000000000000000000", "1.1"), ("1.00010000000000000000000", "1.0001"), ("1", "1"), ("1.1", "1.1"), ("1.0001", "1.0001"), ("-0", "0"), ("-0.0", "0"), ("-0.010", "-0.01"), ("0.0", "0"), ]; for &(value, expected) in tests { let value = Decimal::from_str(value).unwrap(); let expected = Decimal::from_str(expected).unwrap(); let normalized = value.normalize(); assert_eq!(expected.to_string(), normalized.to_string()); } } #[test] fn it_can_return_the_max_value() { assert_eq!("79228162514264337593543950335", Decimal::MAX.to_string()); } #[test] fn it_can_return_the_min_value() { assert_eq!("-79228162514264337593543950335", Decimal::MIN.to_string()); } #[test] fn it_can_go_from_and_into() { let d = Decimal::from_str("5").unwrap(); let di8 = 5u8.into(); let di32 = 5i32.into(); let disize = 5isize.into(); let di64 = 5i64.into(); let du8 = 5u8.into(); let du32 = 5u32.into(); let dusize = 5usize.into(); let du64 = 5u64.into(); assert_eq!(d, di8); assert_eq!(di8, di32); assert_eq!(di32, disize); assert_eq!(disize, di64); assert_eq!(di64, du8); assert_eq!(du8, du32); assert_eq!(du32, dusize); assert_eq!(dusize, du64); } #[test] fn it_converts_to_f64() { let tests = &[ ("5", Some(5f64)), ("-5", Some(-5f64)), ("0.1", Some(0.1f64)), ("0.0", Some(0f64)), ("-0.0", Some(0f64)), ("0.0000000000025", Some(0.25e-11f64)), ("1000000.0000000000025", Some(1e6f64)), ("0.000000000000000000000000025", Some(0.25e-25_f64)), ( "2.1234567890123456789012345678", Some(2.1234567890123456789012345678_f64), ), ( "21234567890123456789012345678", None, // Cannot be represented in an f64 ), ("1.59283191", Some(1.59283191_f64)), ("2.2238", Some(2.2238_f64)), ("2.2238123", Some(2.2238123_f64)), ("22238", Some(22238_f64)), ]; for &(value, expected) in tests { let value = Decimal::from_str(value).unwrap().to_f64(); assert_eq!(expected, value); } } #[test] fn it_converts_to_f64_try() { let tests = &[ ("5", Some(5f64)), ("-5", Some(-5f64)), ("0.1", Some(0.1f64)), ("0.0", Some(0f64)), ("-0.0", Some(0f64)), ("0.0000000000025", Some(0.25e-11f64)), ("1000000.0000000000025", Some(1e6f64)), ("0.000000000000000000000000025", Some(0.25e-25_f64)), ( "2.1234567890123456789012345678", Some(2.1234567890123456789012345678_f64), ), ( "21234567890123456789012345678", None, // Cannot be represented in an f64 ), ("1.59283191", Some(1.59283191_f64)), ]; for &(value, expected) in tests { let value = Decimal::from_str(value).unwrap().try_into().ok(); assert_eq!(expected, value); } } #[test] fn it_converts_to_i64() { assert_eq!(5i64, Decimal::from_str("5").unwrap().to_i64().unwrap()); assert_eq!(-5i64, Decimal::from_str("-5").unwrap().to_i64().unwrap()); assert_eq!(5i64, Decimal::from_str("5.12345").unwrap().to_i64().unwrap()); assert_eq!(-5i64, Decimal::from_str("-5.12345").unwrap().to_i64().unwrap()); assert_eq!( 0x7FFF_FFFF_FFFF_FFFF, Decimal::from_str("9223372036854775807").unwrap().to_i64().unwrap() ); assert_eq!(None, Decimal::from_str("92233720368547758089").unwrap().to_i64()); } #[test] fn it_converts_to_u64() { assert_eq!(5u64, Decimal::from_str("5").unwrap().to_u64().unwrap()); assert_eq!(None, Decimal::from_str("-5").unwrap().to_u64()); assert_eq!(5u64, Decimal::from_str("5.12345").unwrap().to_u64().unwrap()); assert_eq!( 0xFFFF_FFFF_FFFF_FFFF, Decimal::from_str("18446744073709551615").unwrap().to_u64().unwrap() ); assert_eq!(None, Decimal::from_str("18446744073709551616").unwrap().to_u64()); } #[test] fn it_converts_to_i128() { let tests = &[ ("5", Some(5i128)), ("-5", Some(-5i128)), ("5.12345", Some(5i128)), ("-5.12345", Some(-5i128)), ("9223372036854775807", Some(0x7FFF_FFFF_FFFF_FFFF)), ("92233720368547758089", Some(92233720368547758089i128)), ]; for (dec, expected) in tests { assert_eq!(Decimal::from_str(dec).unwrap().to_i128(), *expected); } assert_eq!( 79_228_162_514_264_337_593_543_950_335_i128, Decimal::MAX.to_i128().unwrap() ); } #[test] fn it_converts_to_u128() { let tests = &[ ("5", Some(5u128)), ("-5", None), ("5.12345", Some(5u128)), ("-5.12345", None), ("18446744073709551615", Some(0xFFFF_FFFF_FFFF_FFFF)), ("18446744073709551616", Some(18446744073709551616u128)), ]; for (dec, expected) in tests { assert_eq!(Decimal::from_str(dec).unwrap().to_u128(), *expected); } assert_eq!( 79_228_162_514_264_337_593_543_950_335_u128, Decimal::MAX.to_u128().unwrap() ); } #[test] fn it_converts_from_i128() { let tests: &[(i128, Option<&str>)] = &[ (5, Some("5")), (-5, Some("-5")), (0x7FFF_FFFF_FFFF_FFFF, Some("9223372036854775807")), (92233720368547758089, Some("92233720368547758089")), (0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF, Some("79228162514264337593543950335")), (0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, None), ]; for (value, expected) in tests { if let Some(expected_value) = expected { let decimal = Decimal::from_str(expected_value).unwrap(); assert_eq!(num_traits::FromPrimitive::from_i128(*value), Some(decimal)); } } } #[test] fn it_converts_from_u128() { let tests: &[(u128, Option<&str>)] = &[ (5, Some("5")), (0xFFFF_FFFF_FFFF_FFFF, Some("18446744073709551615")), (0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF, Some("79228162514264337593543950335")), (0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, None), ]; for (value, expected) in tests { if let Some(expected_value) = expected { let decimal = Decimal::from_str(expected_value).unwrap(); assert_eq!(num_traits::FromPrimitive::from_u128(*value), Some(decimal)); } } } #[test] fn it_converts_from_f32() { use num_traits::FromPrimitive; let tests = [ (0.1_f32, "0.1"), (1_f32, "1"), (0_f32, "0"), (0.12345_f32, "0.12345"), (0.1234567800123456789012345678_f32, "0.12345678"), (0.12345678901234567890123456789_f32, "0.12345679"), (0.00000000000000000000000000001_f32, "0"), (5.1_f32, "5.1"), ]; for &(input, expected) in &tests { assert_eq!( expected, Decimal::from_f32(input).unwrap().to_string(), "from_f32({})", input ); assert_eq!( expected, Decimal::try_from(input).unwrap().to_string(), "try_from({})", input ); } } #[test] fn it_converts_from_f32_limits() { use num_traits::FromPrimitive; assert!(Decimal::from_f32(f32::NAN).is_none(), "from_f32(f32::NAN)"); assert!(Decimal::from_f32(f32::INFINITY).is_none(), "from_f32(f32::INFINITY)"); assert!(Decimal::try_from(f32::NAN).is_err(), "try_from(f32::NAN)"); assert!(Decimal::try_from(f32::INFINITY).is_err(), "try_from(f32::INFINITY)"); // These overflow assert!(Decimal::from_f32(f32::MAX).is_none(), "from_f32(f32::MAX)"); assert!(Decimal::from_f32(f32::MIN).is_none(), "from_f32(f32::MIN)"); assert!(Decimal::try_from(f32::MAX).is_err(), "try_from(f32::MAX)"); assert!(Decimal::try_from(f32::MIN).is_err(), "try_from(f32::MIN)"); } #[test] fn it_converts_from_f32_retaining_bits() { let tests = [ (0.1_f32, "0.100000001490116119384765625"), (2_f32, "2"), (4.000_f32, "4"), (5.1_f32, "5.099999904632568359375"), ]; for &(input, expected) in &tests { assert_eq!( expected, Decimal::from_f32_retain(input).unwrap().to_string(), "from_f32_retain({})", input ); } } #[test] fn it_converts_from_f64() { use num_traits::FromPrimitive; let tests = [ (0.1_f64, "0.1"), (1_f64, "1"), (0_f64, "0"), (0.12345_f64, "0.12345"), (0.1234567890123456089012345678_f64, "0.1234567890123456"), (0.12345678901234567890123456789_f64, "0.1234567890123457"), (0.00000000000000000000000000001_f64, "0"), (0.6927_f64, "0.6927"), (0.00006927_f64, "0.00006927"), (0.000000006927_f64, "0.000000006927"), (5.1_f64, "5.1"), ]; for &(input, expected) in &tests { assert_eq!( expected, Decimal::from_f64(input).unwrap().to_string(), "from_f64({})", input ); assert_eq!( expected, Decimal::try_from(input).unwrap().to_string(), "try_from({})", input ); } } #[test] fn it_converts_from_f64_limits() { use num_traits::FromPrimitive; assert!(Decimal::from_f64(f64::NAN).is_none(), "from_f64(f64::NAN)"); assert!(Decimal::from_f64(f64::INFINITY).is_none(), "from_f64(f64::INFINITY)"); assert!(Decimal::try_from(f64::NAN).is_err(), "try_from(f64::NAN)"); assert!(Decimal::try_from(f64::INFINITY).is_err(), "try_from(f64::INFINITY)"); // These overflow assert!(Decimal::from_f64(f64::MAX).is_none(), "from_f64(f64::MAX)"); assert!(Decimal::from_f64(f64::MIN).is_none(), "from_f64(f64::MIN)"); assert!(Decimal::try_from(f64::MAX).is_err(), "try_from(f64::MIN)"); assert!(Decimal::try_from(f64::MIN).is_err(), "try_from(f64::MAX)"); } #[test] fn it_converts_from_f64_retaining_bits() { let tests = [ (0.1_f64, "0.1000000000000000055511151231"), (2_f64, "2"), (4.000_f64, "4"), (5.1_f64, "5.0999999999999996447286321175"), ]; for &(input, expected) in &tests { assert_eq!( expected, Decimal::from_f64_retain(input).unwrap().to_string(), "from_f64_retain({})", input ); } } #[test] fn it_handles_simple_underflow() { // Issue #71 let rate = Decimal::new(19, 2); // 0.19 let one = Decimal::new(1, 0); // 1 let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 let result = one * part; assert_eq!("0.1596638655462184873949579832", result.to_string()); // 169 * 0.1596638655462184873949579832 = 26.983193277310924 let result = part * Decimal::new(169, 0); assert_eq!("26.983193277310924369747899161", result.to_string()); let result = Decimal::new(169, 0) * part; assert_eq!("26.983193277310924369747899161", result.to_string()); } #[test] fn it_can_parse_highly_significant_numbers() { let tests = &[ ("11.111111111111111111111111111", "11.111111111111111111111111111"), ("11.11111111111111111111111111111", "11.111111111111111111111111111"), ("11.1111111111111111111111111115", "11.111111111111111111111111112"), ("115.111111111111111111111111111", "115.11111111111111111111111111"), ("1115.11111111111111111111111111", "1115.1111111111111111111111111"), ("11.1111111111111111111111111195", "11.111111111111111111111111120"), ("99.9999999999999999999999999995", "100.00000000000000000000000000"), ("-11.1111111111111111111111111195", "-11.111111111111111111111111120"), ("-99.9999999999999999999999999995", "-100.00000000000000000000000000"), ("3.1415926535897932384626433832", "3.1415926535897932384626433832"), ( "8808257419827262908.5944405087133154018", "8808257419827262908.594440509", ), ( "8097370036018690744.2590371109596744091", "8097370036018690744.259037111", ), ( "8097370036018690744.2590371149596744091", "8097370036018690744.259037115", ), ( "8097370036018690744.2590371159596744091", "8097370036018690744.259037116", ), ("1.234567890123456789012345678949999", "1.2345678901234567890123456789"), ]; for &(value, expected) in tests { assert_eq!(expected, Decimal::from_str(value).unwrap().to_string()); } } #[test] fn it_can_parse_alternative_formats() { let tests = &[ ("1_000", "1000"), ("1_000_000", "1000000"), ("10_000_000", "10000000"), ("100_000", "100000"), // At the moment, we'll accept this ("1_____________0", "10"), ]; for &(value, expected) in tests { assert_eq!(expected, Decimal::from_str(value).unwrap().to_string()); } } #[test] fn it_can_parse_fractional_numbers_with_underscore_separators() { let a = Decimal::from_str("0.1_23_456").unwrap(); assert_eq!(a.is_sign_negative(), false); assert_eq!(a.scale(), 6); assert_eq!("0.123456", a.to_string()); } #[test] fn it_can_parse_numbers_with_underscore_separators_before_decimal_point() { let a = Decimal::from_str("1_234.56").unwrap(); assert_eq!(a.is_sign_negative(), false); assert_eq!(a.scale(), 2); assert_eq!("1234.56", a.to_string()); } #[test] fn it_can_parse_numbers_and_round_correctly_with_underscore_separators_before_decimal_point() { let tests = &[ ( "8_097_370_036_018_690_744.2590371159596744091", "8097370036018690744.259037116", ), ( "8097370036018690744.259_037_115_959_674_409_1", "8097370036018690744.259037116", ), ( "8_097_370_036_018_690_744.259_037_115_959_674_409_1", "8097370036018690744.259037116", ), ]; for &(value, expected) in tests { assert_eq!(expected, Decimal::from_str(value).unwrap().to_string()); } } #[test] fn it_can_reject_invalid_formats() { let tests = &["_1", "1.0.0", "10_00.0_00.0"]; for &value in tests { assert!( Decimal::from_str(value).is_err(), "This succeeded unexpectedly: {}", value ); } } #[test] fn it_can_reject_large_numbers_with_panic() { let tests = &[ // The maximum number supported is 79,228,162,514,264,337,593,543,950,335 "79228162514264337593543950336", "79228162514264337593543950337", "79228162514264337593543950338", "79228162514264337593543950339", "79228162514264337593543950340", ]; for &value in tests { assert!( Decimal::from_str(value).is_err(), "This succeeded unexpectedly: {}", value ); } } #[test] fn it_can_parse_individual_parts() { let pi = Decimal::from_parts(1102470952, 185874565, 1703060790, false, 28); assert_eq!(pi.to_string(), "3.1415926535897932384626433832"); } #[test] fn it_can_parse_scientific_notation() { let tests = &[ ("9.7e-7", "0.00000097"), ("9e-7", "0.0000009"), ("1.2e10", "12000000000"), ("1.2e+10", "12000000000"), ("12e10", "120000000000"), ("9.7E-7", "0.00000097"), ("1.2345E-24", "0.0000000000000000000000012345"), ("12345E-28", "0.0000000000000000000000012345"), ("1.2345E0", "1.2345"), ("1E28", "10000000000000000000000000000"), ]; for &(value, expected) in tests { assert_eq!(expected, Decimal::from_scientific(value).unwrap().to_string()); } } #[test] fn it_errors_parsing_large_scientific_notation() { let result = Decimal::from_scientific("1.2345E-28"); assert!(result.is_err()); assert_eq!( result.err(), Some(Error::ScaleExceedsMaximumPrecision(32)) // 4 + 28 ); let result = Decimal::from_scientific("12345E29"); assert!(result.is_err()); assert_eq!(result.err(), Some(Error::ScaleExceedsMaximumPrecision(29))); let result = Decimal::from_scientific("12345E28"); assert!(result.is_err()); assert_eq!(result.err(), Some(Error::ExceedsMaximumPossibleValue)); } #[test] fn it_can_parse_different_radix() { let tests = &[ // Input, Radix, Success, to_string() ("123", 10, true, "123"), ("123", 8, true, "83"), ("123", 16, true, "291"), ("abc", 10, false, ""), ("abc", 16, true, "2748"), ("78", 10, true, "78"), ("78", 8, false, ""), ("101", 2, true, "5"), // Parse base 2 ("1111_1111_1111_1111_1111_1111_1111_1111", 2, true, "4294967295"), // Max supported value ( "1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_\ 1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111", 2, true, &Decimal::MAX.to_string(), ), // We limit to 28 dp ( "843.6500000000000000000000000000", 10, true, "843.6500000000000000000000000", ), ]; for &(input, radix, success, expected) in tests { let result = Decimal::from_str_radix(input, radix); assert_eq!( success, result.is_ok(), "Failed to parse: {} radix {}: {:?}", input, radix, result.err() ); if result.is_ok() { assert_eq!( expected, result.unwrap().to_string(), "Original input: {} radix {}", input, radix ); } } } #[test] fn it_can_calculate_signum() { let tests = &[("123", 1), ("-123", -1), ("0", 0)]; for &(input, expected) in tests { let input = Decimal::from_str(input).unwrap(); assert_eq!(expected, input.signum().to_i32().unwrap(), "Input: {}", input); } } #[test] fn it_can_calculate_abs_sub() { let tests = &[ ("123", "124", 0), ("123", "123", 0), ("123", "122", 123), ("-123", "-124", 123), ("-123", "-123", 0), ("-123", "-122", 0), ]; for &(input1, input2, expected) in tests { let input1 = Decimal::from_str(input1).unwrap(); let input2 = Decimal::from_str(input2).unwrap(); assert_eq!( expected, input1.abs_sub(&input2).to_i32().unwrap(), "Input: {} {}", input1, input2 ); } } #[test] #[should_panic(expected = "Scale exceeds the maximum precision allowed: 29 > 28")] fn it_panics_when_scale_too_large() { let _ = Decimal::new(1, 29); } #[test] fn test_zero_eq_negative_zero() { let zero: Decimal = 0.into(); assert_eq!(zero, zero); assert_eq!(-zero, zero); assert_eq!(zero, -zero); } #[test] fn declarative_dec_sum() { let vs = (0..10).map(|i| i.into()).collect::>(); let sum: Decimal = vs.iter().cloned().sum(); assert_eq!(sum, Decimal::from(45)) } #[test] fn declarative_ref_dec_sum() { let vs = (0..10).map(|i| i.into()).collect::>(); let sum: Decimal = vs.iter().sum(); assert_eq!(sum, Decimal::from(45)) } #[cfg(feature = "postgres")] #[test] fn to_from_sql() { use bytes::BytesMut; use postgres::types::{FromSql, Kind, ToSql, Type}; let tests = &[ "3950.123456", "3950", "0.1", "0.01", "0.001", "0.0001", "0.00001", "0.000001", "1", "-100", "-123.456", "119996.25", "1000000", "9999999.99999", "12340.56789", "79228162514264337593543950335", // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF (96 bit) "4951760157141521099596496895", // 0x0FFF_FFFF_FFFF_FFFF_FFFF_FFFF (95 bit) "4951760157141521099596496896", // 0x1000_0000_0000_0000_0000_0000 "18446744073709551615", "-18446744073709551615", ]; let t = Type::new("".into(), 0, Kind::Simple, "".into()); for test in tests { let input = Decimal::from_str(test).unwrap(); let mut bytes = BytesMut::new(); input.to_sql(&t, &mut bytes).unwrap(); let output = Decimal::from_sql(&t, &bytes).unwrap(); assert_eq!(input, output); } } fn hash_it(d: Decimal) -> u64 { use core::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; let mut h = DefaultHasher::new(); d.hash(&mut h); h.finish() } #[test] fn it_computes_equal_hashes_for_equal_values() { // From the Rust Hash docs: // // "When implementing both Hash and Eq, it is important that the following property holds: // // k1 == k2 -> hash(k1) == hash(k2)" let k1 = Decimal::from_str("1").unwrap(); let k2 = Decimal::from_str("1.0").unwrap(); let k3 = Decimal::from_str("1.00").unwrap(); let k4 = Decimal::from_str("1.01").unwrap(); assert_eq!(k1, k2); assert_eq!(k1, k3); assert_ne!(k1, k4); let h1 = hash_it(k1); let h2 = hash_it(k2); let h3 = hash_it(k3); let h4 = hash_it(k4); assert_eq!(h1, h2); assert_eq!(h1, h3); assert_ne!(h1, h4); // Test the application of Hash calculation to a HashMap. let mut map = std::collections::HashMap::new(); map.insert(k1, k1.to_string()); // map[k2] should overwrite map[k1] because k1 == k2. map.insert(k2, k2.to_string()); assert_eq!("1.0", map.get(&k3).expect("could not get k3")); assert_eq!(1, map.len()); // map[k3] should overwrite map[k2] because k3 == k2. map.insert(k3, k3.to_string()); // map[k4] should not overwrite map[k3] because k4 != k3. map.insert(k4, k4.to_string()); assert_eq!(2, map.len()); assert_eq!("1.00", map.get(&k1).expect("could not get k1")); } #[test] fn it_computes_equal_hashes_for_positive_and_negative_zero() { // Verify 0 and -0 have the same hash let k1 = Decimal::from_str("0").unwrap(); let k2 = Decimal::from_str("-0").unwrap(); assert_eq!(k1, k2); let h1 = hash_it(k1); let h2 = hash_it(k2); assert_eq!(h1, h2); // Verify 0 and -0.0 have the same hash let k1 = Decimal::from_str("0").unwrap(); let k2 = Decimal::from_str("-0.0").unwrap(); assert_eq!(k1, k2); let h1 = hash_it(k1); let h2 = hash_it(k2); assert_eq!(h1, h2); } #[test] #[should_panic(expected = "Number less than minimum value that can be represented.")] fn it_handles_i128_min() { let _ = Decimal::from_i128_with_scale(i128::MIN, 0); } #[test] fn it_handles_i128_min_safely() { let result = Decimal::try_from_i128_with_scale(i128::MIN, 0); assert!(result.is_err()); assert_eq!(result.err().unwrap(), Error::LessThanMinimumPossibleValue); } #[test] fn it_can_rescale() { let tests = &[ ("0", 6, "0.000000"), ("0.000000", 2, "0.00"), ("0.12345600000", 6, "0.123456"), ("0.123456", 12, "0.123456000000"), ("0.123456", 0, "0"), ("0.000001", 4, "0.0000"), ("1233456", 4, "1233456.0000"), ("1.2", 30, "1.2000000000000000000000000000"), ("79228162514264337593543950335", 0, "79228162514264337593543950335"), ("4951760157141521099596496895", 1, "4951760157141521099596496895.0"), ("4951760157141521099596496896", 1, "4951760157141521099596496896.0"), ("18446744073709551615", 6, "18446744073709551615.000000"), ("-18446744073709551615", 6, "-18446744073709551615.000000"), ]; for &(value_raw, new_scale, expected_value) in tests { let new_value = Decimal::from_str(expected_value).unwrap(); let mut value = Decimal::from_str(value_raw).unwrap(); value.rescale(new_scale); assert_eq!(new_value.to_string(), value.to_string()); } } #[test] fn test_constants() { assert_eq!("0", Decimal::ZERO.to_string()); assert_eq!("1", Decimal::ONE.to_string()); assert_eq!("-1", Decimal::NEGATIVE_ONE.to_string()); assert_eq!("10", Decimal::TEN.to_string()); assert_eq!("100", Decimal::ONE_HUNDRED.to_string()); assert_eq!("1000", Decimal::ONE_THOUSAND.to_string()); assert_eq!("2", Decimal::TWO.to_string()); } // Mathematical features #[cfg(feature = "maths")] mod maths { use super::*; use rust_decimal::MathematicalOps; use num_traits::One; #[test] fn test_constants() { assert_eq!("3.1415926535897932384626433833", Decimal::PI.to_string()); assert_eq!("6.2831853071795864769252867666", Decimal::TWO_PI.to_string()); assert_eq!("1.5707963267948966192313216916", Decimal::HALF_PI.to_string()); assert_eq!("2.7182818284590452353602874714", Decimal::E.to_string()); assert_eq!("0.3678794411714423215955237702", Decimal::E_INVERSE.to_string()); } #[test] fn test_powu() { let test_cases = &[ // x, y, expected x ^ y ("4", 3_u64, "64"), ("3.222", 5_u64, "347.238347228449632"), ("0.1", 0_u64, "1"), ("342.4", 1_u64, "342.4"), ("2.0", 16_u64, "65536"), ]; for &(x, y, expected) in test_cases { let x = Decimal::from_str(x).unwrap(); let pow = x.powu(y); assert_eq!(pow.to_string(), expected, "{} ^ {}", x, y); } } #[test] #[should_panic(expected = "Pow overflowed")] fn test_powu_panic() { let two = Decimal::new(2, 0); let _ = two.powu(128); } #[test] fn test_checked_powu() { let test_cases = &[ (Decimal::new(4, 0), 3_u64, Some(Decimal::new(64, 0))), ( Decimal::from_str("3.222").unwrap(), 5_u64, Some(Decimal::from_str("347.238347228449632").unwrap()), ), ( Decimal::from_str("0.1").unwrap(), 0_u64, Some(Decimal::from_str("1").unwrap()), ), ( Decimal::from_str("342.4").unwrap(), 1_u64, Some(Decimal::from_str("342.4").unwrap()), ), ( Decimal::from_str("2.0").unwrap(), 16_u64, Some(Decimal::from_str("65536").unwrap()), ), (Decimal::from_str("2.0").unwrap(), 128_u64, None), ]; for case in test_cases { assert_eq!(case.2, case.0.checked_powu(case.1)); } } #[test] fn test_powi() { let test_cases = &[ // x, y, expected x ^ y ("0", 0, "1"), ("1", 0, "1"), ("0", 1, "0"), ("2", 3, "8"), ("-2", 3, "-8"), ("2", -3, "0.125"), ("-2", -3, "-0.125"), ( "3", -3, either!("0.037037037037037037037037037", "0.0370370370370370370370370370"), ), ("6", 3, "216"), ("0.5", 2, "0.25"), ]; for &(x, y, expected) in test_cases { let x = Decimal::from_str(x).unwrap(); let pow = x.powi(y); assert_eq!(pow.to_string(), expected, "{} ^ {}", x, y); } } #[test] fn test_powd() { let test_cases = &[ // x, y, expected x ^ y ("0", "0", "1"), ("1", "0", "1"), ("0", "1", "0"), ("2", "3", "8"), ("-2", "3", "-8"), ("2", "-3", "0.125"), ("-2", "-3", "-0.125"), ("2.0", "3.0", "8"), ("-2.0", "3.0", "-8"), ("2.0", "-3.0", "0.125"), ("-2.0", "-3.0", "-0.125"), ("2.00", "3.00", "8"), ("-2.00", "3.00", "-8"), ("2.00", "-3.00", "0.125"), ("-2.00", "-3.00", "-0.125"), ( "3", "-3", either!("0.037037037037037037037037037", "0.0370370370370370370370370370"), ), ("6", "3", "216"), ("0.5", "2", "0.25"), ("6", "13", "13060694016"), // Exact result: 1 / 6^7 ("6", "-7", "0.0000035722450845907636031093"), // ~= 0.8408964152537145 ( "0.5", "0.25", either!("0.8408964159265360661551317741", "0.8408964159265360661551317742"), ), // ~= 0.999999999999999999999999999790814 ( "0.1234567890123456789012345678", "0.0000000000000000000000000001", "0.9999999999999999999999999998", ), // ~= 611.0451043224257 ( "1234.5678", "0.9012", either!("611.04510415448740041442807964", "611.04510415448740041442807964"), ), ( "-2", "0.5", either!("-1.4142135570048917090885260834", "-1.4142135570048917090885260835"), ), // ~= -1.1193003023312942 ( "-2.5", "0.123", either!("-1.1193002994383985239135362086", "-1.1193002994383985239135362086"), ), // ~= 0.0003493091 ( "0.0000000000000000000000000001", "0.1234567890123456789012345678", either!("0.0003533642875741443321850682", "0.0003305188683169079961720764"), ), ]; for &(x, y, expected) in test_cases { let x = Decimal::from_str(x).unwrap(); let y = Decimal::from_str(y).unwrap(); let pow = x.powd(y); assert_eq!(pow.to_string(), expected, "{} ^ {}", x, y); } } #[test] fn test_sqrt() { let test_cases = &[ ("4", "2"), ("3.222", "1.7949930361981909371487724124"), ("199.45", "14.122676800097069416754994263"), ("342.4", "18.504053609952604112132102540"), ("2", "1.414213562373095048801688724209698078569671875376948073176"), ("0.0000000000000000000000000001", "0.0000000000000100000000000000"), ]; for case in test_cases { let a = Decimal::from_str(case.0).unwrap(); let expected = Decimal::from_str(case.1).unwrap(); assert_eq!(expected, a.sqrt().unwrap()); } assert_eq!(Decimal::new(-2, 0).sqrt(), None); } #[cfg(not(feature = "legacy-ops"))] #[test] fn test_exp() { // These are approximations let test_cases = &[ // e^10 ~= 22026.465794806703 ("10", "22026.416157416030662013737698"), // e^11 ~= 59874.14171519778 ("11", "59873.388231055804982198781924"), // e^3 ~= 20.085536923187664 ("3", "20.085536911963143539758560764"), // e^8 ~= 2980.957987041727 ("8", "2980.9578998304103856663509017"), // e^0.1 ~= 1.1051709180756477 ("0.1", "1.1051709166666666666666666667"), // e^2.0 ~= 7.3890560989306495 ("2.0", "7.3890560703259115957528655940"), // e^-2 ~= 0.1353352832366127 ("-2", "0.1353352837605267572029589224"), // e^-1 ~= 0.36787944117144233 ("-1", "0.3678794414773748171422559335"), // e^0.123456789 ~= 1.131401115 ("0.123456789", "1.1314011144241455834073838005"), // e^0.123456789123456789123456789 ~= 1.131401114651912752617990081 ("0.123456789123456789123456789", "1.1314011145638247316063947842"), ]; for &(x, expected) in test_cases { let x = Decimal::from_str(x).unwrap(); let expected = Decimal::from_str(expected).unwrap(); assert_eq!(expected, x.exp()); assert_eq!(Some(expected), x.checked_exp()); } } #[cfg(not(feature = "legacy-ops"))] #[test] fn test_exp_with_tolerance() { let test_cases = &[ // e^0 = 1 ("0", "0.0002", "1"), // e^1 ~= 2.7182539682539682539682539683 ("1", "0.0002", "2.7182539682539682539682539683"), // e^10 ~= 22026.465794806703 ( "10", "0.02", either!("22026.416157416030662013737698", "22026.416157416030662013737699"), ), // e^11 ~= 59874.14171519778 ("11", "0.0002", "59873.388231055804982198781924"), // e^11.7578 ~= 127741.03548949540892948423052 ("11.7578", "0.0002", "127741.03548949540892948423052"), // e^3 ~= 20.085536923187664 ("3", "0.00002", "20.085534430970814899386327955"), // e^8 ~= 2980.957987041727 ("8", "0.0002", "2980.9578998304103856663509017"), // e^0.1 ~= 1.1051709180756477 ("0.1", "0.0002", "1.1051666666666666666666666667"), // e^2.0 ~= 7.3890560989306495 ("2.0", "0.0002", "7.3890460157126823793490460156"), // e^11.7578+ starts to overflow ("11.7579", "0.0002", ""), // e^11.7578+ starts to overflow ("123", "0.0002", ""), // e^-8+ starts to underflow ("-8", "0.0002", "0.0003354626377168530220952633"), // e^-9 continues to converge towards zero ("-9", "0.0002", "0.0001234098417553083895710102"), // e^-11 continues to converge towards zero ("-11", "0.0002", "0.0000167019109748879838728391"), // e^11.7579 has underflowed (by overflowing) ("-11.7579", "0.0002", ""), // e^-1024 has fully underflowed (by overflowing) ("-1024", "0.0002", ""), ]; for &(x, tolerance, expected) in test_cases { let x = Decimal::from_str(x).unwrap(); let tolerance = Decimal::from_str(tolerance).unwrap(); let expected = if expected.is_empty() { None } else { Some(Decimal::from_str(expected).unwrap()) }; if let Some(expected) = expected { assert_eq!(expected, x.exp_with_tolerance(tolerance)); assert_eq!(Some(expected), x.checked_exp_with_tolerance(tolerance)); } else { assert_eq!(None, x.checked_exp_with_tolerance(tolerance)); } } } #[test] #[should_panic(expected = "Exp overflowed")] fn test_exp_expected_panic_from_overflow() { let d = Decimal::from_str("1024").unwrap(); let _ = d.exp(); } #[test] #[should_panic(expected = "Exp underflowed")] fn test_exp_expected_panic_from_underflow() { let d = Decimal::from_str("-1024").unwrap(); let _ = d.exp(); } #[test] fn test_norm_cdf() { let test_cases = &[ ( Decimal::from_str("-0.4").unwrap(), either!( Decimal::from_str("0.3445781286821245037094401704").unwrap(), Decimal::from_str("0.3445781286821245037094401728").unwrap() ), ), ( Decimal::from_str("-0.1").unwrap(), either!( Decimal::from_str("0.4601722899186706579921922696").unwrap(), Decimal::from_str("0.4601722899186706579921922711").unwrap() ), ), ( Decimal::from_str("0.1").unwrap(), Decimal::from_str(either!( "0.5398277100813293420078077304", "0.5398277100813293420078077290" )) .unwrap(), ), ( Decimal::from_str("0.4").unwrap(), either!( Decimal::from_str("0.6554218713178754962905598296").unwrap(), Decimal::from_str("0.6554218713178754962905598272").unwrap() ), ), ( Decimal::from_str("2.0").unwrap(), either!( Decimal::from_str("0.9772497381095865280953380673").unwrap(), Decimal::from_str("0.9772497381095865280953380672").unwrap() ), ), ]; for case in test_cases { assert_eq!(case.1, case.0.norm_cdf()); } } #[test] fn test_norm_pdf() { let test_cases = &[ ( Decimal::from_str("-2.0").unwrap(), Decimal::from_str("0.0539909667221995238993056051").unwrap(), ), ( Decimal::from_str("-0.4").unwrap(), Decimal::from_str("0.3682701404285264134348468378").unwrap(), ), ( Decimal::from_str("-0.1").unwrap(), Decimal::from_str("0.3969525474873078082322691394").unwrap(), ), ( Decimal::from_str("0.1").unwrap(), Decimal::from_str("0.3969525474873078082322691394").unwrap(), ), ( Decimal::from_str("0.4").unwrap(), Decimal::from_str("0.3682701404285264134348468378").unwrap(), ), ( Decimal::from_str("2.0").unwrap(), Decimal::from_str("0.0539909667221995238993056051").unwrap(), ), ]; for case in test_cases { assert_eq!(case.1, case.0.norm_pdf()); } } #[test] fn test_ln() { let test_cases = [ ("1", "0"), // Wolfram Alpha gives -1.46968 ( "0.23", either!("-1.4696759700589416772292300779", "-1.4696759700589416772292300777"), ), // Wolfram Alpha gives 0.693147180559945309417232121458176568075500134360255254120 ("2", "0.6931471805599453094172321218"), // Wolfram Alpha gives 3.218875824868200749201518666452375279051202708537035443825 ( "25", either!("3.2188758248682007492015186670", "3.2188758248682007492015186674"), ), // Wolfram Alpha gives 0.210721022 ( "1.234567890", either!("0.2107210222156525610500017104", "0.2107210222156525610500017106"), ), ]; for (input, expected) in test_cases { let input = Decimal::from_str(input).unwrap(); let expected = Decimal::from_str(expected).unwrap(); assert_eq!(expected, input.ln(), "Failed to calculate ln({})", input); } } #[test] #[cfg(feature = "maths-nopanic")] fn test_invalid_ln_nopanic() { let test_cases = ["0", "-2.0"]; for input in test_cases { let input = Decimal::from_str(input).unwrap(); assert_eq!("0", input.ln().to_string(), "Failed to calculate ln({})", input); } } #[test] #[should_panic(expected = "Unable to calculate ln for zero")] #[cfg(not(feature = "maths-nopanic"))] fn test_invalid_ln_zero_panic() { let _ = Decimal::ZERO.ln(); } #[test] #[should_panic(expected = "Unable to calculate ln for negative numbers")] #[cfg(not(feature = "maths-nopanic"))] fn test_invalid_ln_negative_panic() { let _ = Decimal::NEGATIVE_ONE.ln(); } #[test] fn test_log10() { let test_cases = [ ("1", "0"), // Wolfram Alpha: 0.3010299956639811952137388947 ( "2", either!("0.3010299956639811952137388949", "0.3010299956639811952137388948"), ), // Wolfram Alpha: 0.0915149772 ( "1.234567890", either!("0.0915149771692704475183336230", "0.0915149771692704475183336231"), ), ("10", "1"), ("100", "2"), ("1000", "3"), ("1000.00000000", "3"), ("1000.000000000000000000000", "3"), ("10.000000000000000000000000000", "1"), ("100000000000000.0000000000", "14"), ]; for (input, expected) in test_cases { let input = Decimal::from_str(input).unwrap(); let expected = Decimal::from_str(expected).unwrap(); assert_eq!(expected, input.log10(), "Failed to calculate log10({})", input); } } #[test] #[cfg(feature = "maths-nopanic")] fn test_invalid_log10_nopanic() { let test_cases = ["0", "-2.0"]; for input in test_cases { let input = Decimal::from_str(input).unwrap(); assert_eq!("0", input.log10().to_string(), "Failed to calculate ln({})", input); } } #[test] #[should_panic(expected = "Unable to calculate log10 for zero")] #[cfg(not(feature = "maths-nopanic"))] fn test_invalid_log10_zero_panic() { let _ = Decimal::ZERO.log10(); } #[test] #[should_panic(expected = "Unable to calculate log10 for negative numbers")] #[cfg(not(feature = "maths-nopanic"))] fn test_invalid_log10_negative_panic() { let _ = Decimal::NEGATIVE_ONE.log10(); } #[test] fn test_erf() { let test_cases = &[ ( Decimal::from_str("-2.0").unwrap(), // Wolfram give -0.9953222650189527 Decimal::from_str("-0.9953225170750043399400930073").unwrap(), ), ( Decimal::from_str("-0.4").unwrap(), Decimal::from_str("-0.4283924127205154977961931420").unwrap(), ), ( Decimal::from_str("0.4").unwrap(), Decimal::from_str("0.4283924127205154977961931420").unwrap(), ), ( Decimal::one(), Decimal::from_str("0.8427010463338918630217928957").unwrap(), ), ( Decimal::from_str("2").unwrap(), Decimal::from_str("0.9953225170750043399400930073").unwrap(), ), ]; for case in test_cases { assert_eq!(case.1, case.0.erf()); } } #[test] fn test_checked_sin() { const ACCEPTED_PRECISION: u32 = 10; let test_cases = &[ // Sin(0) ("0", Some("0")), // Sin(PI/2) ("1.5707963267948966192313216916", Some("1")), // Sin(PI) ("3.1415926535897932384626433833", Some("0")), // Sin(3PI/2) ("4.7123889803846898576939650749", Some("-1")), // Sin(2PI) ("6.2831853071795864769252867666", Some("0")), // Sin(1) ~= 0.8414709848078965066525023216302989996225630607983710656727517099 ("1", Some("0.8414709848078965066525023216")), // Sin(2) ~= 0.9092974268256816953960198659117448427022549714478902683789730115 ("2", Some("0.9092974268256816953960198659")), // Sin(4) ~= -0.756802495307928251372639094511829094135912887336472571485416773 ("4", Some("-0.7568024953079282513726390945")), // Sin(6) ~= -0.279415498198925872811555446611894759627994864318204318483351369 ("6", Some("-0.2794154981989258728115554466")), // WA estimate: -0.893653245236708. Legacy ops is closer to f64 accuracy. ( "-79228162514264.337593543950335", Some(either!("-0.893653245236708", "-0.8963358176")), ), ]; for (input, result) in test_cases { let radians = Decimal::from_str(input).unwrap(); let sin = radians.checked_sin(); if let Some(result) = result { assert!(sin.is_some(), "Expected result for sin({})", input); let result = Decimal::from_str(result).unwrap(); assert_approx_eq!(sin.unwrap(), result, ACCEPTED_PRECISION, "sin({})", input); } else { assert!(sin.is_none(), "Unexpected result for sin({})", input); } } } #[test] fn test_checked_cos() { const ACCEPTED_PRECISION: u32 = 10; let test_cases = &[ // Cos(0) ("0", Some("1")), // Cos(PI/2) ("1.5707963267948966192313216916", Some("0")), // Cos(PI) ("3.1415926535897932384626433833", Some("-1")), // Cos(3PI/2) ("4.7123889803846898576939650749", Some("0")), // Cos(2PI) ("6.2831853071795864769252867666", Some("1")), // Cos(1) ~= 0.5403023058681397174009366074429766037323104206179222276700972553 ("1", Some("0.5403023058681397174009366074")), // Cos(2) ~= -0.416146836547142386997568229500762189766000771075544890755149973 ("2", Some("-0.4161468365471423869975682295")), // Cos(4) ~= -0.653643620863611914639168183097750381424133596646218247007010283 ("4", Some("-0.6536436208636119146391681831")), // Cos(6) ~= 0.9601702866503660205456522979229244054519376792110126981292864260 ("6", Some("0.9601702866503660205456522979")), // WA estimate: 0.448758150096352. Legacy ops is closer to f64 accuracy. ( "-79228162514264.337593543950335", Some(either!("0.448758150096352", "0.443375802326")), ), ]; for (input, result) in test_cases { let radians = Decimal::from_str(input).unwrap(); let cos = radians.checked_cos(); if let Some(result) = result { assert!(cos.is_some(), "Expected result for cos({})", input); let result = Decimal::from_str(result).unwrap(); assert_approx_eq!(cos.unwrap(), result, ACCEPTED_PRECISION, "cos({})", input); } else { assert!(cos.is_none(), "Unexpected result for cos({})", input); } } } #[test] fn test_checked_tan() { const ACCEPTED_PRECISION: u32 = 8; let test_cases = &[ // Tan(0) ("0", Some("0")), // Tan(PI/2) ("1.5707963267948966192313216916", None), // Tan(PI) ("3.1415926535897932384626433833", Some("0")), // Tan(3PI/2) ("4.7123889803846898576939650749", None), // Tan(2PI) ("6.2831853071795864769252867666", Some("0")), // Tan(1) ~= 1.5574077246549022305069748074583601730872507723815200383839466056 ("1", Some("1.5574077246549022305069748075")), // Tan(2) ~= -2.185039863261518991643306102313682543432017746227663164562955869 ("2", Some("-2.1850398632615189916433061023")), // Tan(4) ~= 1.1578212823495775831373424182673239231197627673671421300848571893 ("4", Some("1.1578212823495775831373424183")), // Tan(6) ~= -0.291006191384749157053699588868175542831155570912339131608827193 ("6", Some("-0.2910061913847491570536995889")), // WA estimate: -1.99139167733184. Legacy ops is closer to f64 accuracy. ( "-79228162514264.337593543950335", Some(either!("-1.99139167733184", "-2.021616454709")), ), ]; for (input, result) in test_cases { let radians = Decimal::from_str(input).unwrap(); let tan = radians.checked_tan(); if let Some(result) = result { assert!(tan.is_some(), "Expected result for tan({})", input); let result = Decimal::from_str(result).unwrap(); assert_approx_eq!(tan.unwrap(), result, ACCEPTED_PRECISION, "tan({})", input); } else { assert!(tan.is_none(), "Unexpected result for tan({})", input); } } } } // Generated tests #[cfg(not(feature = "legacy-ops"))] mod generated { use rust_decimal::prelude::*; macro_rules! gen_test { ($name:ident, $csv:expr, $method:tt) => { #[test] fn $name() { let path = std::env::current_dir().unwrap(); let mut rdr = csv::Reader::from_reader( std::fs::File::open(format!("{}/tests/generated/{}", path.display(), $csv)).unwrap(), ); let mut row = 0; for result in rdr.records() { let record = result.unwrap(); row += 1; // Extract the data let d1 = record.get(0).unwrap(); let d2 = record.get(1).unwrap(); let result = record.get(2).unwrap(); let error = record.get(3).unwrap(); // Do the calc let d1 = Decimal::from_str(&d1).unwrap(); let d2 = Decimal::from_str(&d2).unwrap(); let expected = Decimal::from_str(&result).unwrap(); match d1.$method(d2) { Some(v) => assert_eq!(expected, v, "Row {}", row), None => assert!(!error.is_empty()), } } } }; } gen_test!(test_add_000_001, "Add_000_001.csv", checked_add); gen_test!(test_add_000_010, "Add_000_010.csv", checked_add); gen_test!(test_add_000_011, "Add_000_011.csv", checked_add); gen_test!(test_add_000_100, "Add_000_100.csv", checked_add); gen_test!(test_add_000_101, "Add_000_101.csv", checked_add); gen_test!(test_add_000_110, "Add_000_110.csv", checked_add); gen_test!(test_add_000_111, "Add_000_111.csv", checked_add); gen_test!(test_add_001_000, "Add_001_000.csv", checked_add); gen_test!(test_add_001_001, "Add_001_001.csv", checked_add); gen_test!(test_add_001_010, "Add_001_010.csv", checked_add); gen_test!(test_add_001_011, "Add_001_011.csv", checked_add); gen_test!(test_add_001_100, "Add_001_100.csv", checked_add); gen_test!(test_add_001_101, "Add_001_101.csv", checked_add); gen_test!(test_add_001_110, "Add_001_110.csv", checked_add); gen_test!(test_add_001_111, "Add_001_111.csv", checked_add); gen_test!(test_add_010_000, "Add_010_000.csv", checked_add); gen_test!(test_add_010_001, "Add_010_001.csv", checked_add); gen_test!(test_add_010_010, "Add_010_010.csv", checked_add); gen_test!(test_add_010_011, "Add_010_011.csv", checked_add); gen_test!(test_add_010_100, "Add_010_100.csv", checked_add); gen_test!(test_add_010_101, "Add_010_101.csv", checked_add); gen_test!(test_add_010_110, "Add_010_110.csv", checked_add); gen_test!(test_add_010_111, "Add_010_111.csv", checked_add); gen_test!(test_add_011_000, "Add_011_000.csv", checked_add); gen_test!(test_add_011_001, "Add_011_001.csv", checked_add); gen_test!(test_add_011_010, "Add_011_010.csv", checked_add); gen_test!(test_add_011_011, "Add_011_011.csv", checked_add); gen_test!(test_add_011_100, "Add_011_100.csv", checked_add); gen_test!(test_add_011_101, "Add_011_101.csv", checked_add); gen_test!(test_add_011_110, "Add_011_110.csv", checked_add); gen_test!(test_add_011_111, "Add_011_111.csv", checked_add); gen_test!(test_add_100_000, "Add_100_000.csv", checked_add); gen_test!(test_add_100_001, "Add_100_001.csv", checked_add); gen_test!(test_add_100_010, "Add_100_010.csv", checked_add); gen_test!(test_add_100_011, "Add_100_011.csv", checked_add); gen_test!(test_add_100_100, "Add_100_100.csv", checked_add); gen_test!(test_add_100_101, "Add_100_101.csv", checked_add); gen_test!(test_add_100_110, "Add_100_110.csv", checked_add); gen_test!(test_add_100_111, "Add_100_111.csv", checked_add); gen_test!(test_add_101_000, "Add_101_000.csv", checked_add); gen_test!(test_add_101_001, "Add_101_001.csv", checked_add); gen_test!(test_add_101_010, "Add_101_010.csv", checked_add); gen_test!(test_add_101_011, "Add_101_011.csv", checked_add); gen_test!(test_add_101_100, "Add_101_100.csv", checked_add); gen_test!(test_add_101_101, "Add_101_101.csv", checked_add); gen_test!(test_add_101_110, "Add_101_110.csv", checked_add); gen_test!(test_add_101_111, "Add_101_111.csv", checked_add); gen_test!(test_add_110_000, "Add_110_000.csv", checked_add); gen_test!(test_add_110_001, "Add_110_001.csv", checked_add); gen_test!(test_add_110_010, "Add_110_010.csv", checked_add); gen_test!(test_add_110_011, "Add_110_011.csv", checked_add); gen_test!(test_add_110_100, "Add_110_100.csv", checked_add); gen_test!(test_add_110_101, "Add_110_101.csv", checked_add); gen_test!(test_add_110_110, "Add_110_110.csv", checked_add); gen_test!(test_add_110_111, "Add_110_111.csv", checked_add); gen_test!(test_add_111_000, "Add_111_000.csv", checked_add); gen_test!(test_add_111_001, "Add_111_001.csv", checked_add); gen_test!(test_add_111_010, "Add_111_010.csv", checked_add); gen_test!(test_add_111_011, "Add_111_011.csv", checked_add); gen_test!(test_add_111_100, "Add_111_100.csv", checked_add); gen_test!(test_add_111_101, "Add_111_101.csv", checked_add); gen_test!(test_add_111_110, "Add_111_110.csv", checked_add); gen_test!(test_add_111_111, "Add_111_111.csv", checked_add); gen_test!(test_div_000_001, "Div_000_001.csv", checked_div); gen_test!(test_div_000_010, "Div_000_010.csv", checked_div); gen_test!(test_div_000_011, "Div_000_011.csv", checked_div); gen_test!(test_div_000_100, "Div_000_100.csv", checked_div); gen_test!(test_div_000_101, "Div_000_101.csv", checked_div); gen_test!(test_div_000_110, "Div_000_110.csv", checked_div); gen_test!(test_div_000_111, "Div_000_111.csv", checked_div); gen_test!(test_div_001_000, "Div_001_000.csv", checked_div); gen_test!(test_div_001_001, "Div_001_001.csv", checked_div); gen_test!(test_div_001_010, "Div_001_010.csv", checked_div); gen_test!(test_div_001_011, "Div_001_011.csv", checked_div); gen_test!(test_div_001_100, "Div_001_100.csv", checked_div); gen_test!(test_div_001_101, "Div_001_101.csv", checked_div); gen_test!(test_div_001_110, "Div_001_110.csv", checked_div); gen_test!(test_div_001_111, "Div_001_111.csv", checked_div); gen_test!(test_div_010_000, "Div_010_000.csv", checked_div); gen_test!(test_div_010_001, "Div_010_001.csv", checked_div); gen_test!(test_div_010_010, "Div_010_010.csv", checked_div); gen_test!(test_div_010_011, "Div_010_011.csv", checked_div); gen_test!(test_div_010_100, "Div_010_100.csv", checked_div); gen_test!(test_div_010_101, "Div_010_101.csv", checked_div); gen_test!(test_div_010_110, "Div_010_110.csv", checked_div); gen_test!(test_div_010_111, "Div_010_111.csv", checked_div); gen_test!(test_div_011_000, "Div_011_000.csv", checked_div); gen_test!(test_div_011_001, "Div_011_001.csv", checked_div); gen_test!(test_div_011_010, "Div_011_010.csv", checked_div); gen_test!(test_div_011_011, "Div_011_011.csv", checked_div); gen_test!(test_div_011_100, "Div_011_100.csv", checked_div); gen_test!(test_div_011_101, "Div_011_101.csv", checked_div); gen_test!(test_div_011_110, "Div_011_110.csv", checked_div); gen_test!(test_div_011_111, "Div_011_111.csv", checked_div); gen_test!(test_div_100_000, "Div_100_000.csv", checked_div); gen_test!(test_div_100_001, "Div_100_001.csv", checked_div); gen_test!(test_div_100_010, "Div_100_010.csv", checked_div); gen_test!(test_div_100_011, "Div_100_011.csv", checked_div); gen_test!(test_div_100_100, "Div_100_100.csv", checked_div); gen_test!(test_div_100_101, "Div_100_101.csv", checked_div); gen_test!(test_div_100_110, "Div_100_110.csv", checked_div); gen_test!(test_div_100_111, "Div_100_111.csv", checked_div); gen_test!(test_div_101_000, "Div_101_000.csv", checked_div); gen_test!(test_div_101_001, "Div_101_001.csv", checked_div); gen_test!(test_div_101_010, "Div_101_010.csv", checked_div); gen_test!(test_div_101_011, "Div_101_011.csv", checked_div); gen_test!(test_div_101_100, "Div_101_100.csv", checked_div); gen_test!(test_div_101_101, "Div_101_101.csv", checked_div); gen_test!(test_div_101_110, "Div_101_110.csv", checked_div); gen_test!(test_div_101_111, "Div_101_111.csv", checked_div); gen_test!(test_div_110_000, "Div_110_000.csv", checked_div); gen_test!(test_div_110_001, "Div_110_001.csv", checked_div); gen_test!(test_div_110_010, "Div_110_010.csv", checked_div); gen_test!(test_div_110_011, "Div_110_011.csv", checked_div); gen_test!(test_div_110_100, "Div_110_100.csv", checked_div); gen_test!(test_div_110_101, "Div_110_101.csv", checked_div); gen_test!(test_div_110_110, "Div_110_110.csv", checked_div); gen_test!(test_div_110_111, "Div_110_111.csv", checked_div); gen_test!(test_div_111_000, "Div_111_000.csv", checked_div); gen_test!(test_div_111_001, "Div_111_001.csv", checked_div); gen_test!(test_div_111_010, "Div_111_010.csv", checked_div); gen_test!(test_div_111_011, "Div_111_011.csv", checked_div); gen_test!(test_div_111_100, "Div_111_100.csv", checked_div); gen_test!(test_div_111_101, "Div_111_101.csv", checked_div); gen_test!(test_div_111_110, "Div_111_110.csv", checked_div); gen_test!(test_div_111_111, "Div_111_111.csv", checked_div); gen_test!(test_mul_000_001, "Mul_000_001.csv", checked_mul); gen_test!(test_mul_000_010, "Mul_000_010.csv", checked_mul); gen_test!(test_mul_000_011, "Mul_000_011.csv", checked_mul); gen_test!(test_mul_000_100, "Mul_000_100.csv", checked_mul); gen_test!(test_mul_000_101, "Mul_000_101.csv", checked_mul); gen_test!(test_mul_000_110, "Mul_000_110.csv", checked_mul); gen_test!(test_mul_000_111, "Mul_000_111.csv", checked_mul); gen_test!(test_mul_001_000, "Mul_001_000.csv", checked_mul); gen_test!(test_mul_001_001, "Mul_001_001.csv", checked_mul); gen_test!(test_mul_001_010, "Mul_001_010.csv", checked_mul); gen_test!(test_mul_001_011, "Mul_001_011.csv", checked_mul); gen_test!(test_mul_001_100, "Mul_001_100.csv", checked_mul); gen_test!(test_mul_001_101, "Mul_001_101.csv", checked_mul); gen_test!(test_mul_001_110, "Mul_001_110.csv", checked_mul); gen_test!(test_mul_001_111, "Mul_001_111.csv", checked_mul); gen_test!(test_mul_010_000, "Mul_010_000.csv", checked_mul); gen_test!(test_mul_010_001, "Mul_010_001.csv", checked_mul); gen_test!(test_mul_010_010, "Mul_010_010.csv", checked_mul); gen_test!(test_mul_010_011, "Mul_010_011.csv", checked_mul); gen_test!(test_mul_010_100, "Mul_010_100.csv", checked_mul); gen_test!(test_mul_010_101, "Mul_010_101.csv", checked_mul); gen_test!(test_mul_010_110, "Mul_010_110.csv", checked_mul); gen_test!(test_mul_010_111, "Mul_010_111.csv", checked_mul); gen_test!(test_mul_011_000, "Mul_011_000.csv", checked_mul); gen_test!(test_mul_011_001, "Mul_011_001.csv", checked_mul); gen_test!(test_mul_011_010, "Mul_011_010.csv", checked_mul); gen_test!(test_mul_011_011, "Mul_011_011.csv", checked_mul); gen_test!(test_mul_011_100, "Mul_011_100.csv", checked_mul); gen_test!(test_mul_011_101, "Mul_011_101.csv", checked_mul); gen_test!(test_mul_011_110, "Mul_011_110.csv", checked_mul); gen_test!(test_mul_011_111, "Mul_011_111.csv", checked_mul); gen_test!(test_mul_100_000, "Mul_100_000.csv", checked_mul); gen_test!(test_mul_100_001, "Mul_100_001.csv", checked_mul); gen_test!(test_mul_100_010, "Mul_100_010.csv", checked_mul); gen_test!(test_mul_100_011, "Mul_100_011.csv", checked_mul); gen_test!(test_mul_100_100, "Mul_100_100.csv", checked_mul); gen_test!(test_mul_100_101, "Mul_100_101.csv", checked_mul); gen_test!(test_mul_100_110, "Mul_100_110.csv", checked_mul); gen_test!(test_mul_100_111, "Mul_100_111.csv", checked_mul); gen_test!(test_mul_101_000, "Mul_101_000.csv", checked_mul); gen_test!(test_mul_101_001, "Mul_101_001.csv", checked_mul); gen_test!(test_mul_101_010, "Mul_101_010.csv", checked_mul); gen_test!(test_mul_101_011, "Mul_101_011.csv", checked_mul); gen_test!(test_mul_101_100, "Mul_101_100.csv", checked_mul); gen_test!(test_mul_101_101, "Mul_101_101.csv", checked_mul); gen_test!(test_mul_101_110, "Mul_101_110.csv", checked_mul); gen_test!(test_mul_101_111, "Mul_101_111.csv", checked_mul); gen_test!(test_mul_110_000, "Mul_110_000.csv", checked_mul); gen_test!(test_mul_110_001, "Mul_110_001.csv", checked_mul); gen_test!(test_mul_110_010, "Mul_110_010.csv", checked_mul); gen_test!(test_mul_110_011, "Mul_110_011.csv", checked_mul); gen_test!(test_mul_110_100, "Mul_110_100.csv", checked_mul); gen_test!(test_mul_110_101, "Mul_110_101.csv", checked_mul); gen_test!(test_mul_110_110, "Mul_110_110.csv", checked_mul); gen_test!(test_mul_110_111, "Mul_110_111.csv", checked_mul); gen_test!(test_mul_111_000, "Mul_111_000.csv", checked_mul); gen_test!(test_mul_111_001, "Mul_111_001.csv", checked_mul); gen_test!(test_mul_111_010, "Mul_111_010.csv", checked_mul); gen_test!(test_mul_111_011, "Mul_111_011.csv", checked_mul); gen_test!(test_mul_111_100, "Mul_111_100.csv", checked_mul); gen_test!(test_mul_111_101, "Mul_111_101.csv", checked_mul); gen_test!(test_mul_111_110, "Mul_111_110.csv", checked_mul); gen_test!(test_mul_111_111, "Mul_111_111.csv", checked_mul); gen_test!(test_rem_000_001, "Rem_000_001.csv", checked_rem); gen_test!(test_rem_000_010, "Rem_000_010.csv", checked_rem); gen_test!(test_rem_000_011, "Rem_000_011.csv", checked_rem); gen_test!(test_rem_000_100, "Rem_000_100.csv", checked_rem); gen_test!(test_rem_000_101, "Rem_000_101.csv", checked_rem); gen_test!(test_rem_000_110, "Rem_000_110.csv", checked_rem); gen_test!(test_rem_000_111, "Rem_000_111.csv", checked_rem); gen_test!(test_rem_001_000, "Rem_001_000.csv", checked_rem); gen_test!(test_rem_001_001, "Rem_001_001.csv", checked_rem); gen_test!(test_rem_001_010, "Rem_001_010.csv", checked_rem); gen_test!(test_rem_001_011, "Rem_001_011.csv", checked_rem); gen_test!(test_rem_001_100, "Rem_001_100.csv", checked_rem); gen_test!(test_rem_001_101, "Rem_001_101.csv", checked_rem); gen_test!(test_rem_001_110, "Rem_001_110.csv", checked_rem); gen_test!(test_rem_001_111, "Rem_001_111.csv", checked_rem); gen_test!(test_rem_010_000, "Rem_010_000.csv", checked_rem); gen_test!(test_rem_010_001, "Rem_010_001.csv", checked_rem); gen_test!(test_rem_010_010, "Rem_010_010.csv", checked_rem); gen_test!(test_rem_010_011, "Rem_010_011.csv", checked_rem); gen_test!(test_rem_010_100, "Rem_010_100.csv", checked_rem); gen_test!(test_rem_010_101, "Rem_010_101.csv", checked_rem); gen_test!(test_rem_010_110, "Rem_010_110.csv", checked_rem); gen_test!(test_rem_010_111, "Rem_010_111.csv", checked_rem); gen_test!(test_rem_011_000, "Rem_011_000.csv", checked_rem); gen_test!(test_rem_011_001, "Rem_011_001.csv", checked_rem); gen_test!(test_rem_011_010, "Rem_011_010.csv", checked_rem); gen_test!(test_rem_011_011, "Rem_011_011.csv", checked_rem); gen_test!(test_rem_011_100, "Rem_011_100.csv", checked_rem); gen_test!(test_rem_011_101, "Rem_011_101.csv", checked_rem); gen_test!(test_rem_011_110, "Rem_011_110.csv", checked_rem); gen_test!(test_rem_011_111, "Rem_011_111.csv", checked_rem); gen_test!(test_rem_100_000, "Rem_100_000.csv", checked_rem); gen_test!(test_rem_100_001, "Rem_100_001.csv", checked_rem); gen_test!(test_rem_100_010, "Rem_100_010.csv", checked_rem); gen_test!(test_rem_100_011, "Rem_100_011.csv", checked_rem); gen_test!(test_rem_100_100, "Rem_100_100.csv", checked_rem); gen_test!(test_rem_100_101, "Rem_100_101.csv", checked_rem); gen_test!(test_rem_100_110, "Rem_100_110.csv", checked_rem); gen_test!(test_rem_100_111, "Rem_100_111.csv", checked_rem); gen_test!(test_rem_101_000, "Rem_101_000.csv", checked_rem); gen_test!(test_rem_101_001, "Rem_101_001.csv", checked_rem); gen_test!(test_rem_101_010, "Rem_101_010.csv", checked_rem); gen_test!(test_rem_101_011, "Rem_101_011.csv", checked_rem); gen_test!(test_rem_101_100, "Rem_101_100.csv", checked_rem); gen_test!(test_rem_101_101, "Rem_101_101.csv", checked_rem); gen_test!(test_rem_101_110, "Rem_101_110.csv", checked_rem); gen_test!(test_rem_101_111, "Rem_101_111.csv", checked_rem); gen_test!(test_rem_110_000, "Rem_110_000.csv", checked_rem); gen_test!(test_rem_110_001, "Rem_110_001.csv", checked_rem); gen_test!(test_rem_110_010, "Rem_110_010.csv", checked_rem); gen_test!(test_rem_110_011, "Rem_110_011.csv", checked_rem); gen_test!(test_rem_110_100, "Rem_110_100.csv", checked_rem); gen_test!(test_rem_110_101, "Rem_110_101.csv", checked_rem); gen_test!(test_rem_110_110, "Rem_110_110.csv", checked_rem); gen_test!(test_rem_110_111, "Rem_110_111.csv", checked_rem); gen_test!(test_rem_111_000, "Rem_111_000.csv", checked_rem); gen_test!(test_rem_111_001, "Rem_111_001.csv", checked_rem); gen_test!(test_rem_111_010, "Rem_111_010.csv", checked_rem); gen_test!(test_rem_111_011, "Rem_111_011.csv", checked_rem); gen_test!(test_rem_111_100, "Rem_111_100.csv", checked_rem); gen_test!(test_rem_111_101, "Rem_111_101.csv", checked_rem); gen_test!(test_rem_111_110, "Rem_111_110.csv", checked_rem); gen_test!(test_rem_111_111, "Rem_111_111.csv", checked_rem); gen_test!(test_sub_000_001, "Sub_000_001.csv", checked_sub); gen_test!(test_sub_000_010, "Sub_000_010.csv", checked_sub); gen_test!(test_sub_000_011, "Sub_000_011.csv", checked_sub); gen_test!(test_sub_000_100, "Sub_000_100.csv", checked_sub); gen_test!(test_sub_000_101, "Sub_000_101.csv", checked_sub); gen_test!(test_sub_000_110, "Sub_000_110.csv", checked_sub); gen_test!(test_sub_000_111, "Sub_000_111.csv", checked_sub); gen_test!(test_sub_001_000, "Sub_001_000.csv", checked_sub); gen_test!(test_sub_001_001, "Sub_001_001.csv", checked_sub); gen_test!(test_sub_001_010, "Sub_001_010.csv", checked_sub); gen_test!(test_sub_001_011, "Sub_001_011.csv", checked_sub); gen_test!(test_sub_001_100, "Sub_001_100.csv", checked_sub); gen_test!(test_sub_001_101, "Sub_001_101.csv", checked_sub); gen_test!(test_sub_001_110, "Sub_001_110.csv", checked_sub); gen_test!(test_sub_001_111, "Sub_001_111.csv", checked_sub); gen_test!(test_sub_010_000, "Sub_010_000.csv", checked_sub); gen_test!(test_sub_010_001, "Sub_010_001.csv", checked_sub); gen_test!(test_sub_010_010, "Sub_010_010.csv", checked_sub); gen_test!(test_sub_010_011, "Sub_010_011.csv", checked_sub); gen_test!(test_sub_010_100, "Sub_010_100.csv", checked_sub); gen_test!(test_sub_010_101, "Sub_010_101.csv", checked_sub); gen_test!(test_sub_010_110, "Sub_010_110.csv", checked_sub); gen_test!(test_sub_010_111, "Sub_010_111.csv", checked_sub); gen_test!(test_sub_011_000, "Sub_011_000.csv", checked_sub); gen_test!(test_sub_011_001, "Sub_011_001.csv", checked_sub); gen_test!(test_sub_011_010, "Sub_011_010.csv", checked_sub); gen_test!(test_sub_011_011, "Sub_011_011.csv", checked_sub); gen_test!(test_sub_011_100, "Sub_011_100.csv", checked_sub); gen_test!(test_sub_011_101, "Sub_011_101.csv", checked_sub); gen_test!(test_sub_011_110, "Sub_011_110.csv", checked_sub); gen_test!(test_sub_011_111, "Sub_011_111.csv", checked_sub); gen_test!(test_sub_100_000, "Sub_100_000.csv", checked_sub); gen_test!(test_sub_100_001, "Sub_100_001.csv", checked_sub); gen_test!(test_sub_100_010, "Sub_100_010.csv", checked_sub); gen_test!(test_sub_100_011, "Sub_100_011.csv", checked_sub); gen_test!(test_sub_100_100, "Sub_100_100.csv", checked_sub); gen_test!(test_sub_100_101, "Sub_100_101.csv", checked_sub); gen_test!(test_sub_100_110, "Sub_100_110.csv", checked_sub); gen_test!(test_sub_100_111, "Sub_100_111.csv", checked_sub); gen_test!(test_sub_101_000, "Sub_101_000.csv", checked_sub); gen_test!(test_sub_101_001, "Sub_101_001.csv", checked_sub); gen_test!(test_sub_101_010, "Sub_101_010.csv", checked_sub); gen_test!(test_sub_101_011, "Sub_101_011.csv", checked_sub); gen_test!(test_sub_101_100, "Sub_101_100.csv", checked_sub); gen_test!(test_sub_101_101, "Sub_101_101.csv", checked_sub); gen_test!(test_sub_101_110, "Sub_101_110.csv", checked_sub); gen_test!(test_sub_101_111, "Sub_101_111.csv", checked_sub); gen_test!(test_sub_110_000, "Sub_110_000.csv", checked_sub); gen_test!(test_sub_110_001, "Sub_110_001.csv", checked_sub); gen_test!(test_sub_110_010, "Sub_110_010.csv", checked_sub); gen_test!(test_sub_110_011, "Sub_110_011.csv", checked_sub); gen_test!(test_sub_110_100, "Sub_110_100.csv", checked_sub); gen_test!(test_sub_110_101, "Sub_110_101.csv", checked_sub); gen_test!(test_sub_110_110, "Sub_110_110.csv", checked_sub); gen_test!(test_sub_110_111, "Sub_110_111.csv", checked_sub); gen_test!(test_sub_111_000, "Sub_111_000.csv", checked_sub); gen_test!(test_sub_111_001, "Sub_111_001.csv", checked_sub); gen_test!(test_sub_111_010, "Sub_111_010.csv", checked_sub); gen_test!(test_sub_111_011, "Sub_111_011.csv", checked_sub); gen_test!(test_sub_111_100, "Sub_111_100.csv", checked_sub); gen_test!(test_sub_111_101, "Sub_111_101.csv", checked_sub); gen_test!(test_sub_111_110, "Sub_111_110.csv", checked_sub); gen_test!(test_sub_111_111, "Sub_111_111.csv", checked_sub); } #[cfg(feature = "rust-fuzz")] mod rust_fuzz { use arbitrary::{Arbitrary, Unstructured}; use super::*; #[test] fn it_can_generate_arbitrary_decimals() { let mut u = Unstructured::new(b"it_can_generate_arbitrary_decimals"); let d = Decimal::arbitrary(&mut u); assert!(d.is_ok()); } } mod issues { use rust_decimal::prelude::*; #[test] fn issue_384_neg_overflow_during_subtract_carry() { // 288230376151711744 let a = Decimal::from_parts(0, 67108864, 0, false, 0); // 714606955844629274884780.85120 let b = Decimal::from_parts(0, 0, 3873892070, false, 3873892070); let c = a.checked_sub(b); assert!(c.is_some()); // 288230376151711744. // - 714606955844629274884780.85120 // = // - 714606667614253123173036.85120 assert_eq!("-714606667614253123173036.85120", c.unwrap().to_string()); } #[test] fn issue_392_overflow_during_remainder() { let a = Decimal::from_str("-79228157791897.854723898738431").unwrap(); let b = Decimal::from_str("184512476.73336922111").unwrap(); let c = a.checked_div(b); assert!(c.is_some()); assert_eq!("-429391.87200000000002327170816", c.unwrap().to_string()) } } rust_decimal-1.17.0/tests/macros.rs000064400000000000000000000023440072674642500154140ustar 00000000000000#[macro_export] macro_rules! assert_approx_eq { ($a:expr, $b:expr, $precision:expr) => {{ let (a, b) = (&$a, &$b); let eps = Decimal::new(1, $precision); let abs = (*a - *b).abs(); assert!( abs <= eps, "assertion failed: `(left ~= right)` \n \ left: `{:?}`,\n \ right: `{:?}`,\n \ expect: `{:?}`,\n \ real: `{:?}`", *a, *b, eps, abs, ); }}; ($a:expr, $b:expr, $precision:expr, $($arg:tt)+) => {{ let (a, b) = (&$a, &$b); let eps = Decimal::new(1, $precision); let abs = (*a - *b).abs(); assert!( abs <= eps, "assertion failed: `(left ~= right)` \n \ left: `{:?}`,\n \ right: `{:?}`,\n \ expect: `{:?}`,\n \ real: `{:?}`: {}", *a, *b, eps, abs, format_args!($($arg)+), ); }}; } #[macro_export] macro_rules! either { ($result:expr, $legacy_result:expr) => { if cfg!(feature = "legacy-ops") { $legacy_result } else { $result } }; }