зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1716518 - Upgrade rust_decimal to v1.14.2.
Differential Revision: https://phabricator.services.mozilla.com/D117846
This commit is contained in:
Родитель
92aeb7b3c6
Коммит
56e1ee0ba7
|
@ -4370,10 +4370,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rust_decimal"
|
||||
version = "1.7.0"
|
||||
version = "1.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95ba36e8c41bf675947e200af432325f332f60a0aea0ef2dc456636c2f6037d7"
|
||||
checksum = "9787e62372fc0c5a0f3af64c392652db72d3ec1cc0cff1becc175d2c11e6fbcc"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"CODE_OF_CONDUCT.md":"64765f10290cfce7191b4208cb21698b708a118568f5016602cccc304846a09a","CONTRIBUTING.md":"471d6281fb5038e17e32d3b4450aacf542a396709605aa170e07d3971d70b9c1","Cargo.toml":"5bcdb31d3230d6592b1940f0730bc6a0a07c05ef245a4a71ae9ff48b83cc5f38","LICENSE":"f8218253704e32441cafea1b9b3bcb2c6a3c51c5553cd8513d179290202bccb2","README.md":"2e6fc38c2289725da3fea1e2429fdc6482484e32b3e11d0216b719d871193fc5","VERSION.md":"172eea9bab41bd1493cd6a4a03a6df5cdfba66a9f02ec79b776fe71ad55d5be8","benches/lib_benches.rs":"39a5a691cd614aee08c0be202d715045dfe1d27e0a998fd983b8cc2ceaca7b55","rustfmt.toml":"f33bda44a494d17c95b7bc1b3dd88c203030b75be766f3a7f9b63ef45d960bb0","src/decimal.rs":"23b00c66f1024c7883f654d492fa6563173b47aa7ad26b4641315883a8278ea4","src/error.rs":"7f546cbfb6b1fdc6bb7bb3d6ef9f1a2462e30beba6f561e1890e7515c9bfb640","src/lib.rs":"104050f8a7d36317da0021dd4b42973e5f6cd928d748f3d0621f100d8d66fa6e","src/postgres.rs":"454630887e43403011dacee0682d163e92aed0071f3258ee616624ec11b82eb6","src/serde_types.rs":"9eadeca56538e69cd909853dd378bffecf2acc98c651ca2eec3192b81044b0a1","tests/decimal_tests.rs":"6c2d5a178a064e4a5e1131ed0d6c14527e9ac819f52379c0225872fa23788bcf"},"package":"95ba36e8c41bf675947e200af432325f332f60a0aea0ef2dc456636c2f6037d7"}
|
||||
{"files":{"CODE_OF_CONDUCT.md":"64765f10290cfce7191b4208cb21698b708a118568f5016602cccc304846a09a","CONTRIBUTING.md":"471d6281fb5038e17e32d3b4450aacf542a396709605aa170e07d3971d70b9c1","Cargo.toml":"06f73d418d4346f1634ad611b064b5bef0ed3214bf4b30a2b9b1b196e8516e44","LICENSE":"f8218253704e32441cafea1b9b3bcb2c6a3c51c5553cd8513d179290202bccb2","Makefile.toml":"071b9b03210ab5978a75476c5ad93ab467cbbf8ae2ef36e4382ed4cd6bbf7cc3","README.md":"2378fe36c615a4c5922a74da0bf2b0d5e97830b2d1ed82155eaef745823f9a3b","VERSION.md":"fe774af618f4e648eb15e6df9a260f4ceca08a7d8c84b65f9230acac5147c264","benches/lib_benches.rs":"1525a2b3424e04da73153a0a57f8ee6e3c7ba93f3d847940ee596be57d469248","rustfmt.toml":"f33bda44a494d17c95b7bc1b3dd88c203030b75be766f3a7f9b63ef45d960bb0","src/constants.rs":"a182285f5e0ee03282f4b142709041bc7125ac99ea97391e6e0fca0f676339f7","src/db.rs":"d6bd647c670a2e95a98483a18312feebfe8196e1adb9abc4f92634b4a3b115df","src/decimal.rs":"5a5e9d711c14fe494ccbe45e2ae73bdd4fd99de221d913e2f0c4b430751d126a","src/error.rs":"35ca9f0e124c218bd684de72bfd7238eaa65b6902a9a9c6e5df5fc0db99cbed2","src/fuzz.rs":"86c07d8d541b9ee92a51275993b686902712f94c30785ba93f38997f3569e700","src/lib.rs":"52e74246198118fa860aa01ba14a39acd5fdff9642900ade5ed80e51a05aeb59","src/maths.rs":"a4e7f435d7a8bcf3bfcdf94e69da37a04afb7f4fa0eca4f7bc0c575ef898be3a","src/ops.rs":"4d426a35f73b0b69cbceee6e01c6eff59b8cc09aab7c885570aea52d8f258f66","src/ops/add.rs":"a85b6214be92a5563e8bb4a936275d8de094507d77384c9184d63583a78b3f55","src/ops/array.rs":"7a5e3b57ecee0bd9e51e8cc43c6e97aced6002fc58d913ec2956ad721e239cfc","src/ops/cmp.rs":"2019326946d413154fdc62b28da43267df7c1e527e6f9e8d23e9ae87fca3ed05","src/ops/common.rs":"6d48ecfa4796a38cb9d4c48f50ffed5d1ee79ba8e168a175615b6fe97194b7c2","src/ops/div.rs":"6b1e90b383293eb51f20f22846002a61f17211f7791860d4e9d6f82ad940fb87","src/ops/legacy.rs":"e0f68a2f26cb40872b6b85d20e092f604e8a3ad86fbbd2829eb3b6e49e960f64","src/ops/mul.rs":"6dd157135ddcc61b669e9883e73cefcc6cfa19ec0569f37ae3ba4bd21b497f35","src/ops/rem.rs":"c81383cf27f76d8d5372f63ae93fd5cbc0b85ced9515ede32430b1cd526af293","src/serde.rs":"f88438ac63d5eb4d78397f70948aba4063d0ab0064266350d606cdb40e374d96","src/str.rs":"eb61689426c1d219005e19fbdde1facbddbcb1f10c03c88dd587ff97596141f1","tests/decimal_tests.rs":"9a29dc7ab20c2fc25ad4cf136223a250ee1a74891a945ea36fe9e452c1cb63dd"},"package":"9787e62372fc0c5a0f3af64c392652db72d3ec1cc0cff1becc175d2c11e6fbcc"}
|
|
@ -13,8 +13,9 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "rust_decimal"
|
||||
version = "1.7.0"
|
||||
version = "1.14.2"
|
||||
authors = ["Paul Mason <paul@form1.co.nz>"]
|
||||
exclude = ["tests/generated/*"]
|
||||
description = "A Decimal Implementation written in pure Rust suitable for financial calculations."
|
||||
documentation = "https://docs.rs/rust_decimal/"
|
||||
readme = "./README.md"
|
||||
|
@ -22,13 +23,26 @@ 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
|
||||
[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 = "0.5"
|
||||
version = "1.0"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.diesel]
|
||||
version = "1.4"
|
||||
|
@ -38,30 +52,40 @@ default-features = false
|
|||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
features = ["i128"]
|
||||
default-features = false
|
||||
|
||||
[dependencies.postgres]
|
||||
version = "0.17"
|
||||
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.5"
|
||||
version = "0.7"
|
||||
optional = true
|
||||
default-features = false
|
||||
[dev-dependencies.bincode]
|
||||
version = "1.3"
|
||||
|
||||
[dev-dependencies.bytes]
|
||||
version = "0.5"
|
||||
version = "1.0"
|
||||
|
||||
[dev-dependencies.csv]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.futures]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.7"
|
||||
|
||||
[dev-dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
|
||||
|
@ -69,14 +93,21 @@ version = "1.0"
|
|||
version = "1.0"
|
||||
|
||||
[dev-dependencies.tokio]
|
||||
version = "0.2"
|
||||
features = ["rt-threaded", "test-util", "macros"]
|
||||
version = "1.0"
|
||||
features = ["rt-multi-thread", "test-util", "macros"]
|
||||
|
||||
[features]
|
||||
db-diesel-postgres = ["diesel"]
|
||||
db-postgres = ["postgres", "bytes", "byteorder"]
|
||||
db-tokio-postgres = ["postgres", "tokio-postgres", "bytes", "byteorder"]
|
||||
default = ["serde"]
|
||||
serde-bincode = ["serde"]
|
||||
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 = []
|
||||
rust-fuzz = ["arbitrary"]
|
||||
serde-arbitrary-precision = ["serde", "serde_json/arbitrary_precision"]
|
||||
serde-bincode = ["serde-str"]
|
||||
serde-float = ["serde"]
|
||||
serde-str = ["serde"]
|
||||
std = ["arrayvec/std"]
|
||||
tokio-pg = ["db-tokio-postgres"]
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
[config]
|
||||
default_to_workspace = false
|
||||
|
||||
[tasks.build]
|
||||
command = "cargo"
|
||||
args = ["build"]
|
||||
|
||||
[tasks.bench]
|
||||
toolchain = "nightly"
|
||||
command = "cargo"
|
||||
args = ["bench", "${@}"]
|
||||
|
||||
[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.format]
|
||||
workspace = true
|
||||
install_crate = "rustfmt"
|
||||
command = "cargo"
|
||||
args = ["fmt", "--", "--emit=files"]
|
||||
|
||||
[tasks.outdated]
|
||||
install_crate = "cargo-outdated"
|
||||
command = "cargo"
|
||||
args = ["outdated", "-R"]
|
||||
|
||||
[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",
|
||||
]
|
||||
|
||||
[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-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"]
|
|
@ -1,15 +1,25 @@
|
|||
# Decimal   [![Build Status]][actions] [![Latest Version]][crates.io]
|
||||
# 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.
|
||||
|
||||
[Documentation](https://docs.rs/rust_decimal/)
|
||||
## Getting started
|
||||
|
||||
To get started, add `rust_decimal` and optionally `rust_decimal_macros` to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
rust_decimal = "1.14"
|
||||
rust_decimal_macros = "1.14"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -17,37 +27,67 @@ Decimal numbers can be created in a few distinct ways. The easiest and most opti
|
|||
|
||||
```rust
|
||||
// Procedural macros need importing directly
|
||||
use rust_decimal_macros::*;
|
||||
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); // 2.02
|
||||
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(); // 2.02
|
||||
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();
|
||||
let my_int: Decimal = 3i32.into();
|
||||
assert_eq!("3", my_int.to_string());
|
||||
|
||||
// Using the raw decimal representation
|
||||
// 3.1415926535897932384626433832
|
||||
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-bincode](#serde-bincode)
|
||||
* [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`
|
||||
|
||||
|
@ -62,23 +102,52 @@ Enables the tokio postgres module allowing for async communication with 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.
|
||||
|
||||
## `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).
|
||||
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-bincode`
|
||||
## `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 it's own will force deserialization to use `deserialize_str`
|
||||
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 regards to storage size.
|
||||
`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`.
|
||||
|
|
|
@ -1,5 +1,194 @@
|
|||
# Version History
|
||||
|
||||
## 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
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
|
||||
extern crate test;
|
||||
|
||||
use bincode::Options as _;
|
||||
use core::str::FromStr;
|
||||
use rust_decimal::Decimal;
|
||||
use std::str::FromStr;
|
||||
|
||||
macro_rules! bench_decimal_op {
|
||||
($name:ident, $op:tt, $y:expr) => {
|
||||
($name:ident, $op:tt, $x:expr, $y:expr) => {
|
||||
#[bench]
|
||||
fn $name(b: &mut ::test::Bencher) {
|
||||
let x = Decimal::from_str("2.01").unwrap();
|
||||
let x = Decimal::from_str($x).unwrap();
|
||||
let y = Decimal::from_str($y).unwrap();
|
||||
b.iter(|| {
|
||||
let result = x $op y;
|
||||
|
@ -41,46 +42,53 @@ macro_rules! bench_fold_op {
|
|||
}
|
||||
|
||||
/* Add */
|
||||
bench_decimal_op!(add_one, +, "1");
|
||||
bench_decimal_op!(add_two, +, "2");
|
||||
bench_decimal_op!(add_one_hundred, +, "100");
|
||||
bench_decimal_op!(add_point_zero_one, +, "0.01");
|
||||
bench_decimal_op!(add_negative_point_five, +, "-0.5");
|
||||
bench_decimal_op!(add_pi, +, "3.1415926535897932384626433832");
|
||||
bench_decimal_op!(add_negative_pi, +, "-3.1415926535897932384626433832");
|
||||
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_one, -, "1");
|
||||
bench_decimal_op!(sub_two, -, "2");
|
||||
bench_decimal_op!(sub_one_hundred, -, "100");
|
||||
bench_decimal_op!(sub_point_zero_one, -, "0.01");
|
||||
bench_decimal_op!(sub_negative_point_five, -, "-0.5");
|
||||
bench_decimal_op!(sub_pi, -, "3.1415926535897932384626433832");
|
||||
bench_decimal_op!(sub_negative_pi, -, "-3.1415926535897932384626433832");
|
||||
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, *, "1");
|
||||
bench_decimal_op!(mul_two, *, "2");
|
||||
bench_decimal_op!(mul_one_hundred, *, "100");
|
||||
bench_decimal_op!(mul_point_zero_one, *, "0.01");
|
||||
bench_decimal_op!(mul_negative_point_five, *, "-0.5");
|
||||
bench_decimal_op!(mul_pi, *, "3.1415926535897932384626433832");
|
||||
bench_decimal_op!(mul_negative_pi, *, "-3.1415926535897932384626433832");
|
||||
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, /, "1");
|
||||
bench_decimal_op!(div_two, /, "2");
|
||||
bench_decimal_op!(div_one_hundred, /, "100");
|
||||
bench_decimal_op!(div_point_zero_one, /, "0.01");
|
||||
bench_decimal_op!(div_negative_point_five, /, "-0.5");
|
||||
bench_decimal_op!(div_pi, /, "3.1415926535897932384626433832");
|
||||
bench_decimal_op!(div_negative_pi, /, "-3.1415926535897932384626433832");
|
||||
|
||||
bench_fold_op!(div_10k, /, Decimal::max_value(), 10_000);
|
||||
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 {
|
||||
|
@ -126,69 +134,218 @@ fn iterator_sum(b: &mut ::test::Bencher) {
|
|||
});
|
||||
}
|
||||
|
||||
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 decimal_from_str(b: &mut test::Bencher) {
|
||||
let samples_strs = &[
|
||||
"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",
|
||||
];
|
||||
fn serialize_bincode(b: &mut test::Bencher) {
|
||||
let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();
|
||||
|
||||
b.iter(|| {
|
||||
for s in samples_strs {
|
||||
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<Vec<u8>> = 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<Decimal> = 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_strs = &[
|
||||
"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",
|
||||
];
|
||||
|
||||
let samples: Vec<Decimal> = test::black_box(samples_strs.iter().map(|x| Decimal::from_str(x).unwrap()).collect());
|
||||
let t = Type::_new("".into(), 0, Kind::Simple, "".into());
|
||||
let mut vec = Vec::<u8>::with_capacity(100);
|
||||
let samples: Vec<Decimal> = 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 {
|
||||
vec.clear();
|
||||
sample.to_sql(&t, &mut vec).unwrap();
|
||||
let result = Decimal::from_sql(&t, &vec).unwrap();
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// 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: u32 = 28;
|
||||
#[cfg(not(feature = "legacy-ops"))]
|
||||
pub const MAX_PRECISION_I32: i32 = 28;
|
||||
// 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;
|
|
@ -1,10 +1,10 @@
|
|||
use num_traits::Zero;
|
||||
|
||||
use crate::Decimal;
|
||||
|
||||
use std::{convert::TryInto, error, fmt, result::*};
|
||||
|
||||
use crate::decimal::{div_by_u32, is_all_zero, mul_by_u32, MAX_PRECISION};
|
||||
use crate::constants::MAX_PRECISION;
|
||||
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 {
|
||||
|
@ -38,13 +38,13 @@ impl Decimal {
|
|||
digits,
|
||||
weight,
|
||||
}: PostgresDecimal<D>,
|
||||
) -> Result<Self, InvalidDecimal> {
|
||||
) -> Self {
|
||||
let mut digits = digits.into_iter().collect::<Vec<_>>();
|
||||
|
||||
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();
|
||||
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 {
|
||||
|
@ -70,8 +70,7 @@ impl Decimal {
|
|||
} else if fract_pow == MAX_PRECISION + 4 {
|
||||
// rounding last digit
|
||||
if digit >= 5000 {
|
||||
result +=
|
||||
Decimal::new(1 as i64, 0) / Decimal::from_i128_with_scale(10i128.pow(MAX_PRECISION), 0);
|
||||
result += Decimal::new(1_i64, 0) / Decimal::from_i128_with_scale(10i128.pow(MAX_PRECISION), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,8 +79,7 @@ impl Decimal {
|
|||
result.set_sign_negative(neg);
|
||||
// Rescale to the postgres value, automatically rounding as needed.
|
||||
result.rescale(scale as u32);
|
||||
|
||||
Ok(result)
|
||||
result
|
||||
}
|
||||
|
||||
fn to_postgres(self) -> PostgresDecimal<Vec<i16>> {
|
||||
|
@ -144,7 +142,6 @@ impl Decimal {
|
|||
#[cfg(feature = "diesel")]
|
||||
mod diesel {
|
||||
use super::*;
|
||||
|
||||
use ::diesel::{
|
||||
deserialize::{self, FromSql},
|
||||
pg::data_types::PgNumeric,
|
||||
|
@ -152,10 +149,8 @@ mod diesel {
|
|||
serialize::{self, Output, ToSql},
|
||||
sql_types::Numeric,
|
||||
};
|
||||
use ::std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
io::Write,
|
||||
};
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use std::io::Write;
|
||||
|
||||
impl<'a> TryFrom<&'a PgNumeric> for Decimal {
|
||||
type Error = Box<dyn error::Error + Send + Sync>;
|
||||
|
@ -180,8 +175,7 @@ mod diesel {
|
|||
weight,
|
||||
scale,
|
||||
digits: digits.iter().copied().map(|v| v.try_into().unwrap()),
|
||||
})
|
||||
.map_err(Box::new)?)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,11 +188,6 @@ mod diesel {
|
|||
}
|
||||
|
||||
impl<'a> From<&'a Decimal> for PgNumeric {
|
||||
// NOTE(clippy): Clippy suggests to replace the `.take_while(|i| i.is_zero())`
|
||||
// with `.take_while(Zero::is_zero)`, but that's a false positive.
|
||||
// The closure gets an `&&i16` due to autoderef `<i16 as Zero>::is_zero(&self) -> bool`
|
||||
// is called. There is no impl for `&i16` that would work with this closure.
|
||||
#[allow(clippy::assign_op_pattern, clippy::redundant_closure)]
|
||||
fn from(decimal: &'a Decimal) -> Self {
|
||||
let PostgresDecimal {
|
||||
neg,
|
||||
|
@ -207,8 +196,6 @@ mod diesel {
|
|||
digits,
|
||||
} = decimal.to_postgres();
|
||||
|
||||
let digits = digits.into_iter().map(|v| v.try_into().unwrap()).collect();
|
||||
|
||||
if neg {
|
||||
PgNumeric::Negative { digits, scale, weight }
|
||||
} else {
|
||||
|
@ -239,7 +226,7 @@ mod diesel {
|
|||
#[cfg(test)]
|
||||
mod pg_tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use core::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_unnecessary_zeroes() {
|
||||
|
@ -476,11 +463,10 @@ mod diesel {
|
|||
#[cfg(feature = "postgres")]
|
||||
mod postgres {
|
||||
use super::*;
|
||||
|
||||
use ::byteorder::{BigEndian, ReadBytesExt};
|
||||
use ::bytes::{BufMut, BytesMut};
|
||||
use ::postgres::types::*;
|
||||
use ::std::io::Cursor;
|
||||
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:
|
||||
|
@ -490,7 +476,7 @@ mod postgres {
|
|||
// u16 sign (0x0000 = positive, 0x4000 = negative, 0xC000 = NaN)
|
||||
// i16 dscale. Number of digits (in base 10) to print after decimal separator
|
||||
//
|
||||
// Psuedo code :
|
||||
// Pseudo code :
|
||||
// const Decimals [
|
||||
// 0.0000000000000000000000000001,
|
||||
// 0.000000000000000000000001,
|
||||
|
@ -556,15 +542,11 @@ mod postgres {
|
|||
weight,
|
||||
scale,
|
||||
digits: groups.into_iter(),
|
||||
})
|
||||
.map_err(Box::new)?)
|
||||
}))
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
match ty {
|
||||
&Type::NUMERIC => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(*ty, Type::NUMERIC)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -603,10 +585,7 @@ mod postgres {
|
|||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
match ty {
|
||||
&Type::NUMERIC => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(*ty, Type::NUMERIC)
|
||||
}
|
||||
|
||||
to_sql_checked!();
|
||||
|
@ -615,10 +594,8 @@ mod postgres {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use ::postgres::{Client, NoTls};
|
||||
|
||||
use std::str::FromStr;
|
||||
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".
|
||||
|
@ -708,8 +685,8 @@ mod postgres {
|
|||
#[tokio::test]
|
||||
#[cfg(feature = "tokio-pg")]
|
||||
async fn async_test_null() {
|
||||
use ::futures::future::FutureExt;
|
||||
use ::tokio_postgres::connect;
|
||||
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());
|
||||
|
@ -748,8 +725,8 @@ mod postgres {
|
|||
#[tokio::test]
|
||||
#[cfg(feature = "tokio-pg")]
|
||||
async fn async_read_numeric_type() {
|
||||
use ::futures::future::FutureExt;
|
||||
use ::tokio_postgres::connect;
|
||||
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());
|
||||
|
@ -786,8 +763,8 @@ mod postgres {
|
|||
#[tokio::test]
|
||||
#[cfg(feature = "tokio-pg")]
|
||||
async fn async_write_numeric_type() {
|
||||
use ::futures::future::FutureExt;
|
||||
use ::tokio_postgres::connect;
|
||||
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());
|
||||
|
@ -829,8 +806,8 @@ mod postgres {
|
|||
#[tokio::test]
|
||||
#[cfg(feature = "tokio-pg")]
|
||||
async fn async_numeric_overflow() {
|
||||
use ::futures::future::FutureExt;
|
||||
use ::tokio_postgres::connect;
|
||||
use futures::future::FutureExt;
|
||||
use tokio_postgres::connect;
|
||||
|
||||
let tests = [(4, 4, "3950.1234")];
|
||||
let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap();
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,31 +1,42 @@
|
|||
use std::{error, fmt};
|
||||
use crate::constants::MAX_PRECISION;
|
||||
use alloc::string::String;
|
||||
use core::fmt;
|
||||
|
||||
/// Error type for the library.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Error {
|
||||
message: String,
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
ErrorString(String),
|
||||
ExceedsMaximumPossibleValue,
|
||||
LessThanMinimumPossibleValue,
|
||||
ScaleExceedsMaximumPrecision(u32),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Instantiate an error with the specified error message.
|
||||
///
|
||||
/// This function is only available within the crate as there should never
|
||||
/// be a need to create this error outside of the library.
|
||||
pub(crate) fn new<S: Into<String>>(message: S) -> Error {
|
||||
Error {
|
||||
message: message.into(),
|
||||
impl<S> From<S> for Error
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
#[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 maximum precision: {} > {}", scale, MAX_PRECISION)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.pad(&self.message)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
use crate::Decimal;
|
||||
|
||||
use arbitrary::{Arbitrary, Result as ArbitraryResult, Unstructured};
|
||||
|
||||
impl Arbitrary<'_> for crate::Decimal {
|
||||
fn arbitrary(u: &mut Unstructured<'_>) -> ArbitraryResult<Self> {
|
||||
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))
|
||||
}
|
||||
}
|
|
@ -9,48 +9,186 @@
|
|||
//! 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.14"
|
||||
//! rust_decimal_macros = "1.14"
|
||||
//! ```
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! Decimal numbers can be created in a few distinct ways, depending
|
||||
//! on the rust compiler version you're targeting.
|
||||
//! 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:
|
||||
//!
|
||||
//! The stable version of rust requires you to create a Decimal number
|
||||
//! using one of it's convenience methods.
|
||||
//! ```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::*;
|
||||
//!
|
||||
//! // Using an integer followed by the decimal points
|
||||
//! let scaled = Decimal::new(202, 2); // 2.02
|
||||
//!
|
||||
//! // From a string representation
|
||||
//! let from_string = Decimal::from_str("2.02").unwrap(); // 2.02
|
||||
//!
|
||||
//! // Using the `Into` trait
|
||||
//! let my_int : Decimal = 3i32.into();
|
||||
//!
|
||||
//! // Using the raw decimal representation
|
||||
//! // 3.1415926535897932384626433832
|
||||
//! let pi = Decimal::from_parts(1102470952, 185874565, 1703060790, false, 28);
|
||||
//! 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.
|
||||
//!
|
||||
//! ## `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`.
|
||||
//!
|
||||
#![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 postgres;
|
||||
mod db;
|
||||
#[cfg(feature = "rust-fuzz")]
|
||||
mod fuzz;
|
||||
#[cfg(feature = "maths")]
|
||||
mod maths;
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde_types;
|
||||
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};
|
||||
pub use std::str::FromStr;
|
||||
}
|
||||
|
||||
#[cfg(feature = "diesel")]
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
||||
/// Shortcut for `core::result::Result<T, rust_decimal::Error>`. Useful to distinguish
|
||||
/// between `rust_decimal` and `std` types.
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
|
|
@ -0,0 +1,522 @@
|
|||
use crate::prelude::*;
|
||||
use num_traits::pow::Pow;
|
||||
|
||||
const TWO: Decimal = Decimal::from_parts_raw(2, 0, 0, 0);
|
||||
const PI: Decimal = Decimal::from_parts_raw(1102470953, 185874565, 1703060790, 1835008);
|
||||
const EXP_TOLERANCE: Decimal = Decimal::from_parts(2, 0, 0, false, 7);
|
||||
|
||||
// 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, e<sup>x</sup>. Stops calculating when it is within
|
||||
/// tolerance of roughly `0.0000002`.
|
||||
fn exp(&self) -> Decimal;
|
||||
|
||||
/// The estimated exponential function, e<sup>x</sup>. Stops calculating when it is within
|
||||
/// tolerance of roughly `0.0000002`. Returns `None` on overflow.
|
||||
fn checked_exp(&self) -> Option<Decimal>;
|
||||
|
||||
/// The estimated exponential function, e<sup>x</sup> 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, e<sup>x</sup> 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<Decimal>;
|
||||
|
||||
/// Raise self to the given integer exponent: x<sup>y</sup>
|
||||
fn powi(&self, exp: i64) -> Decimal;
|
||||
|
||||
/// Raise self to the given integer exponent x<sup>y</sup> returning `None` on overflow.
|
||||
fn checked_powi(&self, exp: i64) -> Option<Decimal>;
|
||||
|
||||
/// Raise self to the given unsigned integer exponent: x<sup>y</sup>
|
||||
fn powu(&self, exp: u64) -> Decimal;
|
||||
|
||||
/// Raise self to the given unsigned integer exponent x<sup>y</sup> returning `None` on overflow.
|
||||
fn checked_powu(&self, exp: u64) -> Option<Decimal>;
|
||||
|
||||
/// Raise self to the given floating point exponent: x<sup>y</sup>
|
||||
fn powf(&self, exp: f64) -> Decimal;
|
||||
|
||||
/// Raise self to the given floating point exponent x<sup>y</sup> returning `None` on overflow.
|
||||
fn checked_powf(&self, exp: f64) -> Option<Decimal>;
|
||||
|
||||
/// Raise self to the given Decimal exponent: x<sup>y</sup>. If `exp` is not whole then the approximation
|
||||
/// e<sup>y*ln(x)</sup> is used.
|
||||
fn powd(&self, exp: Decimal) -> Decimal;
|
||||
|
||||
/// Raise self to the given Decimal exponent x<sup>y</sup> returning `None` on overflow.
|
||||
/// If `exp` is not whole then the approximation e<sup>y*ln(x)</sup> is used.
|
||||
fn checked_powd(&self, exp: Decimal) -> Option<Decimal>;
|
||||
|
||||
/// The square root of a Decimal. Uses a standard Babylonian method.
|
||||
fn sqrt(&self) -> Option<Decimal>;
|
||||
|
||||
/// The natural logarithm for a Decimal. Uses a [fast estimation algorithm](https://en.wikipedia.org/wiki/Natural_logarithm#High_precision)
|
||||
/// This is more accurate on larger numbers and less on numbers less than 1.
|
||||
fn ln(&self) -> Decimal;
|
||||
|
||||
/// 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<Decimal>;
|
||||
}
|
||||
|
||||
impl MathematicalOps for Decimal {
|
||||
fn exp(&self) -> Decimal {
|
||||
self.exp_with_tolerance(EXP_TOLERANCE)
|
||||
}
|
||||
|
||||
fn checked_exp(&self) -> Option<Decimal> {
|
||||
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<Decimal> {
|
||||
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 + 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<Decimal> {
|
||||
// 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<Decimal> {
|
||||
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<Decimal> {
|
||||
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<Decimal> {
|
||||
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;
|
||||
}
|
||||
|
||||
if exp.is_sign_negative() {
|
||||
return self.checked_powi(-(exp.lo() as i64));
|
||||
} else {
|
||||
return 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<Decimal> {
|
||||
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 / 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) / TWO;
|
||||
}
|
||||
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn ln(&self) -> Decimal {
|
||||
const C4: Decimal = Decimal::from_parts_raw(4, 0, 0, 0);
|
||||
const C256: Decimal = Decimal::from_parts_raw(256, 0, 0, 0);
|
||||
const EIGHT_LN2: Decimal = Decimal::from_parts(1406348788, 262764557, 3006046716, false, 28);
|
||||
|
||||
if self.is_sign_positive() {
|
||||
if *self == Decimal::ONE {
|
||||
Decimal::ZERO
|
||||
} else {
|
||||
let rhs = C4 / (self * C256);
|
||||
let arith_geo_mean = arithmetic_geo_mean_of_2(&Decimal::ONE, &rhs);
|
||||
(PI / (arith_geo_mean * TWO)) - EIGHT_LN2
|
||||
}
|
||||
} else {
|
||||
Decimal::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
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()) / 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<Decimal> {
|
||||
let sqrt2pi = Decimal::from_parts_raw(2133383024, 2079885984, 1358845910, 1835008);
|
||||
let factor = -self.checked_powi(2)?;
|
||||
let factor = factor.checked_div(TWO)?;
|
||||
factor.checked_exp()?.checked_div(sqrt2pi)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pow<Decimal> for Decimal {
|
||||
type Output = Decimal;
|
||||
|
||||
fn pow(self, rhs: Decimal) -> Self::Output {
|
||||
MathematicalOps::powd(&self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pow<u64> for Decimal {
|
||||
type Output = Decimal;
|
||||
|
||||
fn pow(self, rhs: u64) -> Self::Output {
|
||||
MathematicalOps::powu(&self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pow<i64> for Decimal {
|
||||
type Output = Decimal;
|
||||
|
||||
fn pow(self, rhs: i64) -> Self::Output {
|
||||
MathematicalOps::powi(&self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pow<f64> for Decimal {
|
||||
type Output = Decimal;
|
||||
|
||||
fn pow(self, rhs: f64) -> Self::Output {
|
||||
MathematicalOps::powf(&self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the convergence of both the arithmetic and geometric mean.
|
||||
/// Used internally.
|
||||
fn arithmetic_geo_mean_of_2(a: &Decimal, b: &Decimal) -> Decimal {
|
||||
const TOLERANCE: Decimal = Decimal::from_parts(5, 0, 0, false, 7);
|
||||
let diff = (a - b).abs();
|
||||
|
||||
if diff < TOLERANCE {
|
||||
*a
|
||||
} else {
|
||||
arithmetic_geo_mean_of_2(&mean_of_2(a, b), &geo_mean_of_2(a, b))
|
||||
}
|
||||
}
|
||||
|
||||
/// The Arithmetic mean. Used internally.
|
||||
fn mean_of_2(a: &Decimal, b: &Decimal) -> Decimal {
|
||||
(a + b) / TWO
|
||||
}
|
||||
|
||||
/// The geometric mean. Used internally.
|
||||
fn geo_mean_of_2(a: &Decimal, b: &Decimal) -> Decimal {
|
||||
// TODO: This can overflow unnecessarily. We should keep this in an internal representation until
|
||||
// absolutely necessary to convert back.
|
||||
(a * b).sqrt().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn 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!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_geo_mean_of_2() {
|
||||
let test_cases = &[
|
||||
(
|
||||
Decimal::from_str("2").unwrap(),
|
||||
Decimal::from_str("2").unwrap(),
|
||||
Decimal::from_str("2").unwrap(),
|
||||
),
|
||||
(
|
||||
Decimal::from_str("4").unwrap(),
|
||||
Decimal::from_str("3").unwrap(),
|
||||
Decimal::from_str("3.4641016151377545870548926830").unwrap(),
|
||||
),
|
||||
(
|
||||
Decimal::from_str("12").unwrap(),
|
||||
Decimal::from_str("3").unwrap(),
|
||||
Decimal::from_str("6.000000000000000000000000000").unwrap(),
|
||||
),
|
||||
];
|
||||
|
||||
for case in test_cases {
|
||||
assert_eq!(case.2, geo_mean_of_2(&case.0, &case.1));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mean_of_2() {
|
||||
let test_cases = &[
|
||||
(
|
||||
Decimal::from_str("2").unwrap(),
|
||||
Decimal::from_str("2").unwrap(),
|
||||
Decimal::from_str("2").unwrap(),
|
||||
),
|
||||
(
|
||||
Decimal::from_str("4").unwrap(),
|
||||
Decimal::from_str("3").unwrap(),
|
||||
Decimal::from_str("3.5").unwrap(),
|
||||
),
|
||||
(
|
||||
Decimal::from_str("12").unwrap(),
|
||||
Decimal::from_str("3").unwrap(),
|
||||
Decimal::from_str("7.5").unwrap(),
|
||||
),
|
||||
];
|
||||
|
||||
for case in test_cases {
|
||||
assert_eq!(case.2, mean_of_2(&case.0, &case.1));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// 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;
|
|
@ -0,0 +1,382 @@
|
|||
use 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<u32> {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
use crate::constants::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
use 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
|
||||
}
|
|
@ -0,0 +1,455 @@
|
|||
use 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<usize> {
|
||||
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<u32> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,658 @@
|
|||
use 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<i32, DivError> {
|
||||
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<i32, DivError> {
|
||||
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
|
||||
}
|
|
@ -0,0 +1,838 @@
|
|||
use crate::{
|
||||
constants::{MAX_PRECISION, 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 {
|
||||
let mut remainder = 0;
|
||||
|
||||
// Division underflowed. We must remove some significant digits over using
|
||||
// an invalid scale.
|
||||
while final_scale > MAX_PRECISION && !is_all_zero("ient) {
|
||||
remainder = div_by_u32(&mut quotient, 10);
|
||||
final_scale -= 1;
|
||||
}
|
||||
if final_scale > MAX_PRECISION {
|
||||
// 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 {
|
||||
final_scale -= MAX_PRECISION;
|
||||
|
||||
// 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;
|
||||
}
|
||||
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 {
|
||||
// We're in an underflow situation
|
||||
// The easiest way to remove precision is to divide off the result
|
||||
while final_scale > MAX_PRECISION && !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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
use crate::constants::{BIG_POWERS_10, MAX_I64_SCALE, MAX_PRECISION, 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 {
|
||||
// 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 + MAX_I64_SCALE {
|
||||
return CalculationResult::Ok(Decimal::ZERO);
|
||||
}
|
||||
|
||||
scale -= MAX_PRECISION + 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;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
use 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();
|
||||
if d2.hi() == 0 {
|
||||
// 64 bit divisor so we adjust accordingly
|
||||
let divisor = d2.low64() << shift;
|
||||
|
||||
// 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 = d2.low64() << shift;
|
||||
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,
|
||||
))
|
||||
}
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
use crate::Decimal;
|
||||
|
||||
use alloc::string::ToString;
|
||||
use core::{fmt, str::FromStr};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use serde::{self, de::Unexpected};
|
||||
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
#[cfg(not(feature = "serde-bincode"))]
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
impl<'de> serde::Deserialize<'de> for Decimal {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Decimal, D::Error>
|
||||
where
|
||||
|
@ -16,7 +14,7 @@ impl<'de> serde::Deserialize<'de> for Decimal {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "serde-bincode", not(feature = "serde-float")))]
|
||||
#[cfg(all(feature = "serde-str", not(feature = "serde-float")))]
|
||||
impl<'de> serde::Deserialize<'de> for Decimal {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Decimal, D::Error>
|
||||
where
|
||||
|
@ -26,7 +24,7 @@ impl<'de> serde::Deserialize<'de> for Decimal {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "serde-bincode", feature = "serde-float"))]
|
||||
#[cfg(all(feature = "serde-str", feature = "serde-float"))]
|
||||
impl<'de> serde::Deserialize<'de> for Decimal {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Decimal, D::Error>
|
||||
where
|
||||
|
@ -36,6 +34,10 @@ impl<'de> serde::Deserialize<'de> for Decimal {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -80,6 +82,90 @@ impl<'de> serde::de::Visitor<'de> for DecimalVisitor {
|
|||
.or_else(|_| Decimal::from_scientific(value))
|
||||
.map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-arbitrary-precision")]
|
||||
fn visit_map<A>(self, map: A) -> Result<Decimal, A::Error>
|
||||
where
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut map = map;
|
||||
let value = map.next_key::<DecimalKey>()?;
|
||||
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<D>(deserializer: D) -> Result<DecimalKey, D::Error>
|
||||
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<E>(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<D>(deserializer: D) -> Result<DecimalFromString, D::Error>
|
||||
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<E>(self, value: &str) -> Result<DecimalFromString, E>
|
||||
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"))]
|
||||
|
@ -88,7 +174,7 @@ impl serde::Serialize for Decimal {
|
|||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
serializer.serialize_str(crate::str::to_str_internal(self, true, None).as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,9 +191,7 @@ impl serde::Serialize for Decimal {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
@ -116,7 +200,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "serde-bincode"))]
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
fn deserialize_valid_decimal() {
|
||||
let data = [
|
||||
("{\"amount\":\"1.234\"}", "1.234"),
|
||||
|
@ -129,7 +213,7 @@ mod test {
|
|||
assert_eq!(
|
||||
true,
|
||||
result.is_ok(),
|
||||
"expected successful deseralization for {}. Error: {:?}",
|
||||
"expected successful deserialization for {}. Error: {:?}",
|
||||
serialized,
|
||||
result.err().unwrap()
|
||||
);
|
||||
|
@ -144,6 +228,14 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[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() {
|
||||
|
@ -172,7 +264,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "serde-bincode", not(feature = "serde-float")))]
|
||||
#[cfg(all(feature = "serde-str", not(feature = "serde-float")))]
|
||||
fn bincode_serialization() {
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
|
@ -183,6 +275,8 @@ mod test {
|
|||
"-3.14159",
|
||||
"1234567890123.4567890",
|
||||
"-1234567890123.4567890",
|
||||
"5233.9008808150288439427720175",
|
||||
"-5233.9008808150288439427720175",
|
||||
];
|
||||
for &raw in data.iter() {
|
||||
let value = Decimal::from_str(raw).unwrap();
|
||||
|
@ -194,7 +288,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "serde-bincode", feature = "serde-float"))]
|
||||
#[cfg(all(feature = "serde-str", feature = "serde-float"))]
|
||||
fn bincode_serialization() {
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
|
@ -215,4 +309,21 @@ mod test {
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,529 @@
|
|||
use crate::{
|
||||
constants::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<usize>,
|
||||
) -> ArrayString<[u8; MAX_STR_BUFFER_SIZE]> {
|
||||
// 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 = match precision {
|
||||
Some(prec) => prec,
|
||||
None => scale,
|
||||
};
|
||||
|
||||
let len = chars.len();
|
||||
let whole_len = len - scale;
|
||||
let mut rep = ArrayString::new();
|
||||
if append_sign && value.is_sign_negative() {
|
||||
rep.push('-');
|
||||
}
|
||||
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.is_empty() {
|
||||
rep.push('0');
|
||||
}
|
||||
|
||||
rep
|
||||
}
|
||||
|
||||
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::<String>();
|
||||
} else {
|
||||
chars.insert(len - 1, '.');
|
||||
rep = chars.iter().rev().collect::<String>();
|
||||
}
|
||||
exponent += (len - 1) as isize;
|
||||
} else {
|
||||
rep = chars.iter().collect::<String>();
|
||||
}
|
||||
|
||||
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<Decimal, crate::Error> {
|
||||
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<Decimal, crate::Error> {
|
||||
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))
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче