Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "ea0803e5dbaea8dd87d7b5d53d3fd366310a6f4f"
},
"path_in_vcs": ""
}
+157
View File
@@ -0,0 +1,157 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bigdecimal"
version = "0.4.10"
dependencies = [
"autocfg",
"libm",
"num-bigint",
"num-integer",
"num-traits",
"paste",
"serde",
"serde_json",
"serde_test",
"siphasher",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "proc-macro2"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_test"
version = "1.0.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29baf0f77ca9ad9c6ed46e1b408b5e0f30b5184bcd66884e7f6d36bd7a65a8a4"
dependencies = [
"serde",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "syn"
version = "2.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2415488199887523e74fd9a5f7be804dfd42d868ae0eca382e3917094d210e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+111
View File
@@ -0,0 +1,111 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2015"
name = "bigdecimal"
version = "0.4.10"
authors = ["Andrew Kubera"]
build = "build.rs"
include = [
"LICENSE-APACHE",
"LICENSE-MIT",
"README.md",
"build.rs",
"src/**/*.rs",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Arbitrary precision decimal numbers"
homepage = "https://github.com/akubera/bigdecimal-rs"
documentation = "https://docs.rs/bigdecimal"
readme = "README.md"
keywords = [
"numerics",
"bignum",
"decimal",
"arbitrary-precision",
]
categories = [
"mathematics",
"science",
"no-std",
]
license = "MIT/Apache-2.0"
repository = "https://github.com/akubera/bigdecimal-rs"
[features]
default = ["std"]
serde-json = [
"serde/derive",
"serde_json",
]
std = [
"num-bigint/std",
"num-integer/std",
"num-traits/std",
]
string-only = []
[lib]
name = "bigdecimal"
path = "src/lib.rs"
bench = false
[dependencies.libm]
version = "0.2.6"
[dependencies.num-bigint]
version = "0.4"
default-features = false
[dependencies.num-integer]
version = "0.1"
default-features = false
[dependencies.num-traits]
version = "0.2"
default-features = false
[dependencies.serde]
version = "1.0"
optional = true
default-features = false
[dependencies.serde_json]
version = "1.0"
features = [
"alloc",
"arbitrary_precision",
]
optional = true
default-features = false
[dev-dependencies.paste]
version = "1"
[dev-dependencies.serde_test]
version = "<1.0.176"
[dev-dependencies.siphasher]
version = "0.3.10"
default-features = false
[build-dependencies.autocfg]
version = "1"
[lints.rust.unexpected_cfgs]
level = "allow"
priority = 0
check-cfg = ["cfg(no_track_caller)"]
+66
View File
@@ -0,0 +1,66 @@
[package]
name = "bigdecimal"
version = "0.4.10"
authors = ["Andrew Kubera"]
description = "Arbitrary precision decimal numbers"
documentation = "https://docs.rs/bigdecimal"
homepage = "https://github.com/akubera/bigdecimal-rs"
repository = "https://github.com/akubera/bigdecimal-rs"
keywords = [
"numerics",
"bignum",
"decimal",
"arbitrary-precision",
]
categories = [ "mathematics", "science", "no-std" ]
license = "MIT/Apache-2.0"
autobenches = false
edition = "2015"
include = [
"LICENSE-APACHE",
"LICENSE-MIT",
"README.md",
"build.rs",
"src/**/*.rs",
]
[lib]
bench = false
[dependencies]
libm = "0.2.6"
num-bigint = { version = "0.4", default-features = false }
num-integer = { version = "0.1", default-features = false }
num-traits = { version = "0.2", default-features = false }
serde = { version = "1.0", optional = true, default-features = false }
# Allow direct parsing of JSON floats, for full arbitrary precision
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc", "arbitrary_precision"]}
[dev-dependencies]
paste = "1"
serde_test = "<1.0.176"
siphasher = { version = "0.3.10", default-features = false }
# The following dev-dependencies are only required for benchmarking
# (use the `benchmark-bigdecimal` script to uncomment these and run benchmarks)
# BENCH: criterion = { version = "0.4", features = [ "html_reports" ] }
# BENCH: oorandom = { version = "11.1.3" }
# BENCH: lazy_static = { version = "1" }
# Only required for property testing - incompatible with older versions of rust
# PROPERTY-TESTS: proptest = "1"
[build-dependencies]
autocfg = "1"
[features]
default = ["std"]
serde-json = ["serde/derive", "serde_json"]
string-only = []
std = ["num-bigint/std", "num-integer/std", "num-traits/std"]
# BENCH: [[bench]]
# BENCH: name = "arithmetic"
# BENCH: harness = false
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(no_track_caller)'] }
+203
View File
@@ -0,0 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 The BigDecimal-rs Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2023 The BigDecimal-rs Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+298
View File
@@ -0,0 +1,298 @@
# bigdecimal-rs
[![crate](https://img.shields.io/crates/v/bigdecimal.svg)](https://crates.io/crates/bigdecimal)
[![Documentation](https://docs.rs/bigdecimal/badge.svg)](https://docs.rs/bigdecimal)
[![minimum rustc 1.43](https://img.shields.io/badge/rustc-1.43+-red.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[![codecov](https://codecov.io/gh/akubera/bigdecimal-rs/branch/feature/circleci/graph/badge.svg?token=YTwyxrxJ3S)](https://codecov.io/gh/akubera/bigdecimal-rs)
[![build status - master](https://gitlab.com/akubera/bigdecimal-rs/badges/master/pipeline.svg?ignore_skipped=true&key_text=status:master&key_width=96)](https://gitlab.com/akubera/bigdecimal-rs/-/pipelines)
[![build status - trunk](https://gitlab.com/akubera/bigdecimal-rs/badges/trunk/pipeline.svg?ignore_skipped=true&key_text=status:trunk&key_width=96)](https://gitlab.com/akubera/bigdecimal-rs/-/pipelines)
Arbitrary-precision decimal numbers implemented in pure Rust.
## Community
Join the conversation on Zulip: https://bigdecimal-rs.zulipchat.com
Please share important stuff like use-cases, issues, benchmarks, and
naming-convention preferences.
This project is currently being re-written, so if performance or flexibility
is lacking, check again soon and it may be fixed.
## Usage
Add bigdecimal as a dependency to your `Cargo.toml` file:
```toml
[dependencies]
bigdecimal = "0.4"
```
Import and use the `BigDecimal` struct to solve your problems:
```rust
use bigdecimal::BigDecimal;
fn main() {
let two = BigDecimal::from(2);
println!("sqrt(2) = {}", two.sqrt().unwrap());
}
```
this code will print
```
sqrt(2) = 1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573
```
### Serialization
If you are passing BigDecimals between systems, be sure to use a serialization format
which explicitly supports decimal numbers and does not require transformations to
floating-point binary numbers, or there will be information loss.
Text formats like JSON should work ok as long as the receiver will also parse
numbers as decimals so complete precision is kept accurate.
Typically JSON-parsing implementations do not do this by default, and need special
configuration.
Binary formats like msgpack may expect/require representing numbers as 64-bit IEEE-754
floating-point, and will likely lose precision by default unless you explicitly format
the decimal as a string, bytes, or some custom structure.
By default, this will serialize the decimal _as a string_.
To use `serde_json` with this crate it is recommended to enable the `serde-json` feature
(note `serde -dash- json` , not `serde_json`) and that will add support for
serializing & deserializing values to BigDecimal.
By default it will parse numbers and strings using normal conventions.
If you want to serialize to a number, rather than a string, you can use the
`serde(with)` annotation as shown in the following example:
```toml
[dependencies]
bigdecimal = { version = "0.4", features = [ "serde-json" ] } # '-' not '_'
```
```rust
use bigdecimal::BigDecimal;
use serde::*;
use serde_json;
#[derive(Debug,Serialize,Deserialize)]
struct MyStruct {
name: String,
// this will be written to json as string
value: BigDecimal,
// this will be written to json as number
#[serde(with = "bigdecimal::serde::json_num")]
number: BigDecimal,
}
fn main() {
let json_src = r#"
{ "name": "foo", "value": 1234567e-3, "number": 3.14159 }
"#;
let my_struct: MyStruct = serde_json::from_str(&json_src).unwrap();
dbg!(my_struct);
// MyStruct { name: "foo", value: BigDecimal("1234.567"), BigDecimal("3.1459") }
println!("{}", serde_json::to_string(&my_struct));
// {"name":"foo","value":"1234.567","number":3.1459}
}
```
If you have suggestions for improving serialization, please bring them
to the Zulip chat.
### Formatting
Until a more sophisticated formatting solution is implemented (currently work in progress),
we are restricted to Rust's `fmt::Display` formatting options.
This is how this crate formats BigDecimals:
- `{}` - Default Display
- Format as "human readable" number
- "Small" fractional numbers (close to zero) are printed in scientific notation
- Number is considered "small" by number of leading zeros exceeding a threshold
- Configurable by the compile-time environment variable:
`RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD`
- Default 5
- Example: `1.23e-3` will print as `0.00123` but `1.23e-10` will be `1.23E-10`
- Trailing zeros will be added to "small" integers, avoiding scientific notation
- May appear to have more precision than they do
- Example: decimal `1e1` would be rendered as `10`
- The threshold for "small" is configured by compile-time environment variable:
`RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD`
- Default 15
- `1e15` => `1000000000000000`
- Large integers (e.g. `1e50000000`) will print in scientific notation, not
a 1 followed by fifty million zeros
- All other numbers are printed in standard decimal notation
- `{:.<PREC>}` - Display with precision
- Format number with exactly `PREC` digits after the decimal place
- Numbers with fractional components will be rounded at precision point, or have
zeros padded to precision point
- Integers will have zeros padded to the precision point
- To prevent unreasonably sized output, a threshold limits the number
of padded zeros
- Greater than the default case, since specific precision was requested
- Configurable by the compile-time environment variable:
`RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING`
- Default 1000
- If digits exceed this threshold, they are printed without decimal-point,
suffixed with scale of the big decimal
- `{:e}` / `{:E}` - Exponential format
- Formats in scientific notation with either `e` or `E` as exponent delimiter
- Precision is kept exactly
- `{:.<PREC>e}` - formats in scientific notation, keeping number
- Number is rounded / zero padded until
- `{:?}` - Debug
- Shows internal representation of BigDecimal
- `123.456` => `BigDecimal(sign=Plus, scale=3, digits=[123456])`
- `-1e10000` => `BigDecimal(sign=Minus, scale=-10000, digits=[1])`
- `{:#?}` - Alternate Debug (used by `dbg!()`)
- Shows simple int+exponent string representation of BigDecimal
- `123.456` => `BigDecimal("123456e-3")`
- `-1e10000` => `BigDecimal("-1e10000")`
There is a [formatting-example](examples/formatting-examples.rs) script in the
`examples/` directory that demonstrates the formatting options and comparison
with Rust's standard floating point Display.
It is recommended you include unit tests in your code to guarantee that future
versions of BigDecimal continue to format numbers the way you expect.
The rules above are not likely to change, but they are probably the only part
of this library that are relatively subjective, and could change behavior
without indication from the compiler.
Also, check for changes to the configuration environment variables, those
may change name until 1.0.
### Compile-Time Configuration
You can set a few default parameters at _compile-time_ via environment variables:
| Environment Variable | Default |
|----------------------------------------------------|------------|
| `RUST_BIGDECIMAL_DEFAULT_PRECISION` | 100 |
| `RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE` | `HalfEven` |
| `RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD` | 5 |
| `RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD` | 15 |
| `RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING` | 1000 |
These allow setting the default [Context] fields globally without incurring a runtime lookup,
or having to pass Context parameters through all calculations.
(If you want runtime control over these fields, you will have to pass Contexts to your functions.)
Examine [build.rs] for how those are converted to constants in the code (if interested).
[Context]: https://docs.rs/bigdecimal/latest/bigdecimal/struct.Context.html
[build.rs]: ./build.rs
#### Default precision
Default precision may be set at compile time with the environment variable `RUST_BIGDECIMAL_DEFAULT_PRECISION`.
The default value of this variable is 100.
This will be used as maximum precision for operations which may produce infinite digits (inverse, sqrt, ...).
Note that other operations, such as multiplication, will preserve all digits;
so multiplying two 70 digit numbers will result in one 140 digit number.
The user will have to manually trim the number of digits after calculations to
reasonable amounts using the `x.with_prec(30)` method.
A new set of methods with explicit precision and rounding modes is being worked
on, but even after those are introduced the default precision will have to be
used as the implicit value.
#### Rounding mode
The default Context uses this value for rounding.
Valid values are the variants of the [RoundingMode] enum.
Defaults to `HalfEven`.
[RoundingMode]: https://docs.rs/bigdecimal/latest/bigdecimal/rounding/enum.RoundingMode.html
#### Exponential Format Threshold
The maximum number of leading zeros after the decimal place before
the formatter uses exponential form (i.e. scientific notation).
There is currently no mechanism to change this during runtime.
If you know of a good solution for number formatting in Rust, please let me know!
#### Example Compile time configuration
Given the program:
```rust
fn main() {
let n = BigDecimal::from(700);
println!("1/{n} = {}", n.inverse());
}
```
Compiling with different environment variables prints different results
```
$ export BIG_DECIMAL_DEFAULT_PRECISION=8
$ cargo run
1/700 = 0.0014285714
$ export RUST_BIGDECIMAL_DEFAULT_PRECISION=5
$ cargo run
1/700 = 0.0014286
$ export RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE=Down
$ cargo run
1/700 = 0.0014285
$ export RUST_BIGDECIMAL_EXPONENTIAL_FORMAT_THRESHOLD=2
$ cargo run
1/700 = 1.4285E-3
```
> [!NOTE]
>
> These are **compile time** environment variables, and the BigDecimal
> library is not configurable at **runtime** via environment variable, or
> any kind of global variables, by default.
>
> This is for flexibility and performance.
## About
This repository contains code originally meant for a bigdecimal module
in the popular [num](https://crates.io/crates/num) crate, but was not
merged due to uncertainty of what the best design for such a crate
should be.
## License
This code is dual-licensed under the permissive
[MIT](https://opensource.org/licenses/MIT) &
[Apache 2.0](https://opensource.org/licenses/Apache-2.0) licenses.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the
Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.
+121
View File
@@ -0,0 +1,121 @@
use std::env;
use std::path::{Path, PathBuf};
// configuration defaults
const DEFAULT_PRECISION: &str = "100";
const DEFAULT_ROUNDING_MODE: &str = "HalfEven";
const FMT_EXPONENTIAL_LOWER_THRESHOLD: &str = "5";
const FMT_EXPONENTIAL_UPPER_THRESHOLD: &str = "15";
const FMT_MAX_INTEGER_PADDING: &str = "1000";
const SERDE_MAX_SCALE: &str = "150000";
fn main() {
let ac = autocfg::new();
// Fn() -> impl Trait
ac.emit_rustc_version(1, 75);
// abs_diff
ac.emit_rustc_version(1, 60);
// slice::fill
ac.emit_rustc_version(1, 50);
// Option::zip
ac.emit_rustc_version(1, 46);
// int::unsigned_abs (1.51)
ac.emit_expression_cfg("0i64.unsigned_abs()", "has_unsigned_abs");
// Remove this comment if enabled proptests
// ::PROPERTY-TESTS:: autocfg::emit("property_tests");
let outdir: PathBuf = std::env::var_os("OUT_DIR").unwrap().into();
write_default_precision_file(&outdir);
write_default_rounding_mode(&outdir);
write_exponential_format_threshold_file(&outdir);
write_max_serde_parsing_scale_limit(&outdir);
}
/// Loads the environment variable string or default
macro_rules! load_env {
($env:ident, $name:literal, $default:ident) => {{
println!("cargo:rerun-if-env-changed={}", $name);
$env::var($name).unwrap_or_else(|_| $default.to_owned())
}};
}
/// Create default_precision.rs, containing definition of constant DEFAULT_PRECISION loaded in src/lib.rs
fn write_default_precision_file(outdir: &Path) {
let env_var = load_env!(env, "RUST_BIGDECIMAL_DEFAULT_PRECISION", DEFAULT_PRECISION);
let rust_file_path = outdir.join("default_precision.rs");
let default_prec: u32 = env_var
.parse::<std::num::NonZeroU32>()
.expect("$RUST_BIGDECIMAL_DEFAULT_PRECISION must be an integer > 0")
.into();
let rust_file_contents = format!("const DEFAULT_PRECISION: u64 = {};", default_prec);
std::fs::write(rust_file_path, rust_file_contents).unwrap();
}
/// Create default_rounding_mode.rs, using value of RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE environment variable
fn write_default_rounding_mode(outdir: &Path) {
let rounding_mode_name = load_env!(env, "RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE", DEFAULT_ROUNDING_MODE);
let rust_file_path = outdir.join("default_rounding_mode.rs");
let rust_file_contents = format!("const DEFAULT_ROUNDING_MODE: RoundingMode = RoundingMode::{};", rounding_mode_name);
std::fs::write(rust_file_path, rust_file_contents).unwrap();
}
/// Create write_default_rounding_mode.rs, containing definition of constant EXPONENTIAL_FORMAT_THRESHOLD loaded in src/impl_fmt.rs
fn write_exponential_format_threshold_file(outdir: &Path) {
let low_value = load_env!(env, "RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD", FMT_EXPONENTIAL_LOWER_THRESHOLD);
let high_value = load_env!(env, "RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD", FMT_EXPONENTIAL_UPPER_THRESHOLD);
let max_padding = load_env!(env, "RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING", FMT_MAX_INTEGER_PADDING);
let low_value: u32 = low_value
.parse::<std::num::NonZeroU32>()
.expect("$RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD must be an integer > 0")
.into();
let high_value: u32 = high_value
.parse::<u32>()
.expect("$RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD must be valid u32");
let max_padding: u32 = max_padding
.parse::<u32>()
.expect("$RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING must be valid u32");
let rust_file_path = outdir.join("exponential_format_threshold.rs");
let rust_file_contents = [
format!("const EXPONENTIAL_FORMAT_LEADING_ZERO_THRESHOLD: usize = {};", low_value),
format!("const EXPONENTIAL_FORMAT_TRAILING_ZERO_THRESHOLD: usize = {};", high_value),
format!("const FMT_MAX_INTEGER_PADDING: usize = {};", max_padding),
];
std::fs::write(rust_file_path, rust_file_contents.join("\n")).unwrap();
}
/// Create write_default_rounding_mode.rs, containing definition of constant EXPONENTIAL_FORMAT_THRESHOLD loaded in src/impl_fmt.rs
fn write_max_serde_parsing_scale_limit(outdir: &Path) {
let scale_limit = load_env!(env, "RUST_BIGDECIMAL_SERDE_SCALE_LIMIT", SERDE_MAX_SCALE);
let scale_limit: u32 = scale_limit
.parse::<u32>()
.or_else(|e| if scale_limit.to_lowercase() == "none" { Ok(0) } else { Err(e) })
.expect("$RUST_BIGDECIMAL_SERDE_SCALE_LIMIT must be an integer");
let rust_file_path = outdir.join("serde_scale_limit.rs");
let rust_file_contents = [
format!("const SERDE_SCALE_LIMIT: i64 = {};", scale_limit),
];
std::fs::write(rust_file_path, rust_file_contents.join("\n")).unwrap();
}
@@ -0,0 +1,153 @@
//! addition routines
//!
use crate::*;
pub(crate) fn add_bigdecimals(
mut a: BigDecimal,
mut b: BigDecimal,
) -> BigDecimal {
if b.is_zero() {
a.extend_scale_to(b.scale);
return a;
}
if a.is_zero() {
b.extend_scale_to(a.scale);
return b;
}
let (a, b) = match a.scale.cmp(&b.scale) {
Ordering::Equal => (a, b),
Ordering::Less => (a.take_and_scale(b.scale), b),
Ordering::Greater => (b.take_and_scale(a.scale), a),
};
add_aligned_bigdecimals(a, b)
}
fn add_aligned_bigdecimals(
mut a: BigDecimal,
mut b: BigDecimal,
) -> BigDecimal {
debug_assert_eq!(a.scale, b.scale);
if a.int_val.bits() >= b.int_val.bits() {
a.int_val += b.int_val;
a
} else {
b.int_val += a.int_val;
b
}
}
pub(crate) fn add_bigdecimal_refs<'a, 'b, Lhs, Rhs>(
lhs: Lhs,
rhs: Rhs,
ctx: Option<&Context>,
) -> BigDecimal
where
Rhs: Into<BigDecimalRef<'a>>,
Lhs: Into<BigDecimalRef<'b>>,
{
use stdlib::cmp::Ordering::*;
let lhs = lhs.into();
let rhs = rhs.into();
if rhs.is_zero() {
let scale_diff = rhs.scale.saturating_sub(lhs.scale).max(0).min(15);
return lhs.to_owned_with_scale(lhs.scale + scale_diff);
}
if lhs.is_zero() {
let scale_diff = lhs.scale.saturating_sub(rhs.scale).max(0).min(15);
return rhs.to_owned_with_scale(rhs.scale + scale_diff);
}
match lhs.scale.cmp(&rhs.scale) {
Equal => {
add_aligned_bigdecimal_ref_ref(lhs, rhs)
}
Greater => {
add_unaligned_bigdecimal_ref_ref(lhs, rhs, ctx)
}
Less => {
add_unaligned_bigdecimal_ref_ref(rhs, lhs, ctx)
}
}
}
pub(crate) fn addassign_bigdecimals(
lhs: &mut BigDecimal,
rhs: BigDecimal,
) {
if rhs.is_zero() {
return;
}
if lhs.is_zero() {
*lhs = rhs;
return;
}
lhs.add_assign(rhs.to_ref());
}
pub(crate) fn addassign_bigdecimal_ref<'a, T: Into<BigDecimalRef<'a>>>(
lhs: &mut BigDecimal,
rhs: T,
) {
// TODO: Replace to_owned() with efficient addition algorithm
let rhs = rhs.into().to_owned();
match lhs.scale.cmp(&rhs.scale) {
Ordering::Less => {
let scaled = lhs.with_scale(rhs.scale);
lhs.int_val = scaled.int_val + &rhs.int_val;
lhs.scale = rhs.scale;
}
Ordering::Greater => {
let scaled = rhs.with_scale(lhs.scale);
lhs.int_val += scaled.int_val;
}
Ordering::Equal => {
lhs.int_val += &rhs.int_val;
}
}
}
/// Add BigDecimal references which have the same scale (integer addition)
fn add_aligned_bigdecimal_ref_ref(
lhs: BigDecimalRef, rhs: BigDecimalRef
) -> BigDecimal {
debug_assert!(!lhs.is_zero() && !rhs.is_zero());
debug_assert_eq!(lhs.scale, rhs.scale);
if lhs.digits.bits() >= rhs.digits.bits() {
lhs.to_owned() + rhs
} else {
rhs.to_owned() + lhs
}
}
fn add_unaligned_bigdecimal_ref_ref(
lhs: BigDecimalRef, rhs: BigDecimalRef, _ctx: Option<&Context>,
) -> BigDecimal {
debug_assert!(!lhs.is_zero() && !rhs.is_zero());
debug_assert!(lhs.scale >= rhs.scale);
let scale_diff = (lhs.scale - rhs.scale) as u64;
let shifted_rhs_digits = rhs.digits * ten_to_the_uint(scale_diff);
let shifted_rhs_int = BigInt::from_biguint(rhs.sign, shifted_rhs_digits);
let shifted_rhs = BigDecimal::new(shifted_rhs_int, lhs.scale);
shifted_rhs + lhs
}
#[cfg(test)]
mod test {
use super::*;
include!("addition.tests.rs");
}
@@ -0,0 +1,52 @@
mod add_bigdecimals {
use super::*;
use paste::paste;
macro_rules! impl_case {
( $name:ident: $a:literal + $b:literal = $c:literal ) => {
#[test]
fn $name() {
let lhs: BigDecimal = $a.parse().unwrap();
let rhs: BigDecimal = $b.parse().unwrap();
let l_plus_r = add_bigdecimals(lhs.clone(), rhs.clone());
let r_plus_l = add_bigdecimals(rhs, lhs);
let expected: BigDecimal = $c.parse().unwrap();
assert_eq!(expected.int_val, l_plus_r.int_val);
assert_eq!(expected.scale, l_plus_r.scale);
assert_eq!(expected.int_val, r_plus_l.int_val);
assert_eq!(expected.scale, r_plus_l.scale);
}
paste! {
#[test]
fn [< $name _refs >]() {
let lhs: BigDecimal = $a.parse().unwrap();
let rhs: BigDecimal = $b.parse().unwrap();
let l_plus_r = add_bigdecimal_refs(&lhs, &rhs, None);
let r_plus_l = add_bigdecimal_refs(&rhs, &lhs, None);
let expected: BigDecimal = $c.parse().unwrap();
assert_eq!(expected.int_val, l_plus_r.int_val);
assert_eq!(expected.scale, l_plus_r.scale);
assert_eq!(expected.int_val, r_plus_l.int_val);
assert_eq!(expected.scale, r_plus_l.scale);
}
}
};
}
impl_case!(case_1d2345_123d45: "1.2345" + "123.45" = "124.6845");
impl_case!(case_123d43e5_1d2345: "123.43e5" + "1.2345" = "12343001.2345");
impl_case!(case_0_0: "0" + "0" = "0");
impl_case!(case_0_0d00: "0" + "0.00" = "0.00");
impl_case!(case_10_0d00: "10" + "0.00" = "10.00");
impl_case!(case_22132e2_0d0000: "22132e2" + "0.0000" = "2213200.0000");
impl_case!(case_n316d79_0en6: "-316.79" + "0e-6" = "-316.790000");
}
+204
View File
@@ -0,0 +1,204 @@
//! Implementation of cube-root algorithm
use crate::*;
use num_bigint::BigUint;
use rounding::NonDigitRoundingData;
use stdlib::num::NonZeroU64;
pub(crate) fn impl_cbrt_int_scale(n: &BigInt, scale: i64, ctx: &Context) -> BigDecimal {
let rounding_data = NonDigitRoundingData {
sign: n.sign(),
mode: ctx.rounding_mode(),
};
impl_cbrt_uint_scale((n.magnitude(), scale).into(), ctx.precision(), rounding_data)
}
/// implementation of cuberoot - always positive
pub(crate) fn impl_cbrt_uint_scale(
n: WithScale<&BigUint>,
precision: NonZeroU64,
// contains sign and rounding mode
rounding_data: NonDigitRoundingData,
) -> BigDecimal {
if n.is_zero() {
let biguint = BigInt::from_biguint(Sign::Plus, n.value.clone());
return BigDecimal::new(biguint, n.scale / 3);
}
// count number of digits in the decimal
let integer_digit_count = count_decimal_digits_uint(n.value);
// extra digits to use for rounding
let extra_rounding_digit_count = 4;
// required number of digits for precision and rounding
let required_precision = precision.get() + extra_rounding_digit_count;
let required_precision = 3 * required_precision;
// number of extra zeros to add to end of integer_digits
let mut exp_shift = required_precision.saturating_sub(integer_digit_count);
// effective scale after multiplying by 10^exp_shift
// (we've added that many trailing zeros after)
let shifted_scale = n.scale + exp_shift as i64;
let (mut new_scale, remainder) = shifted_scale.div_rem(&3);
match remainder.cmp(&0) {
Ordering::Greater => {
new_scale += 1;
exp_shift += (3 - remainder) as u64;
}
Ordering::Less => {
exp_shift += remainder.neg() as u64;
}
Ordering::Equal => {
}
}
// clone-on-write copy of digits
let mut integer_digits = stdlib::borrow::Cow::Borrowed(n.value);
// add required trailing zeros to integer_digits
if exp_shift > 0 {
arithmetic::multiply_by_ten_to_the_uint(
integer_digits.to_mut(), exp_shift
);
}
let result_digits = integer_digits.nth_root(3);
let result_digits_count = count_decimal_digits_uint(&result_digits);
debug_assert!(result_digits_count > precision.get());
let digits_to_trim = result_digits_count - precision.get();
debug_assert_ne!(digits_to_trim, 0);
debug_assert!((result_digits_count as i64 - count_decimal_digits_uint(&integer_digits) as i64 / 3).abs() < 2);
new_scale -= digits_to_trim as i64;
let divisor = ten_to_the_uint(digits_to_trim);
let (mut result_digits, remainder) = result_digits.div_rem(&divisor);
let remainder_digits = remainder.to_radix_le(10);
let insig_digit0;
let trailing_digits;
if remainder_digits.len() < digits_to_trim as usize {
// leading zeros
insig_digit0 = 0;
trailing_digits = remainder_digits.as_slice();
} else {
let (&d, rest) = remainder_digits.split_last().unwrap();
insig_digit0 = d;
trailing_digits = rest;
}
let insig_data = rounding::InsigData::from_digit_and_lazy_trailing_zeros(
rounding_data, insig_digit0, || { trailing_digits.iter().all(Zero::is_zero) }
);
// lowest digit to round
let sig_digit = (&result_digits % 10u8).to_u8().unwrap();
let rounded_digit = insig_data.round_digit(sig_digit);
let rounding_term = rounded_digit - sig_digit;
result_digits += rounding_term;
let result = BigInt::from_biguint(rounding_data.sign, result_digits);
BigDecimal::new(result, new_scale)
}
#[cfg(test)]
mod test {
use super::*;
use stdlib::num::NonZeroU64;
macro_rules! impl_test {
($name:ident; $input:literal => $expected:literal) => {
#[test]
fn $name() {
let n: BigDecimal = $input.parse().unwrap();
let value = n.cbrt();
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(value, expected);
}
};
($name:ident; prec=$prec:literal; round=$round:ident; $input:literal => $expected:literal) => {
#[test]
fn $name() {
let ctx = Context::new(NonZeroU64::new($prec).unwrap(), RoundingMode::$round);
let n: BigDecimal = $input.parse().unwrap();
let value = n.cbrt_with_context(&ctx);
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(value, expected);
}
};
}
mod default {
use super::*;
impl_test!(case_0; "0.00" => "0");
impl_test!(case_1; "1.00" => "1");
impl_test!(case_1d001; "1.001" => "1.000333222283909495175449559955220102010284758197360454054345461242739715702641939155238095670636841");
impl_test!(case_10; "10" => "2.154434690031883721759293566519350495259344942192108582489235506346411106648340800185441503543243276");
impl_test!(case_13409d179789484375; "13409.179789484375" => "23.7575");
impl_test!(case_n59283293e25; "-59283293e25" => "-84006090355.84281237113712383191213626687332139035750444925827809487776780721673264524620270275301685");
impl_test!(case_94213372931en127; "94213372931e-127" => "2.112049945275324414051072540210070583697242797173805198575907094646677475250362108901530353886613160E-39");
}
impl_test!(case_prec15_down_10; prec=15; round=Down; "10" => "2.15443469003188");
impl_test!(case_prec6_up_0d979970546636727; prec=6; round=Up; "0.979970546636727" => "0.993279");
impl_test!(case_1037d495615705321421375_full; "1037.495615705321421375" => "10.123455");
impl_test!(case_1037d495615705321421375_prec7_halfdown; prec=7; round=HalfDown; "1037.495615705321421375" => "10.12345");
impl_test!(case_1037d495615705321421375_prec7_halfeven; prec=7; round=HalfEven; "1037.495615705321421375" => "10.12346");
impl_test!(case_1037d495615705321421375_prec7_halfup; prec=7; round=HalfUp; "1037.495615705321421375" => "10.12346");
impl_test!(case_0d014313506928855520728400001_full; "0.014313506928855520728400001" => "0.242800001");
impl_test!(case_0d014313506928855520728400001_prec6_down; prec=6; round=Down; "0.014313506928855520728400001" => "0.242800");
impl_test!(case_0d014313506928855520728400001_prec6_up; prec=6; round=Up; "0.014313506928855520728400001" => "0.242801");
impl_test!(case_4151902e20_prec16_halfup; prec=16; round=HalfUp; "4151902e20" => "746017527.6855992");
impl_test!(case_4151902e20_prec16_up; prec=16; round=Up; "4151902e20" => "746017527.6855993");
impl_test!(case_4151902e20_prec17_up; prec=17; round=Up; "4151902e20" => "746017527.68559921");
impl_test!(case_4151902e20_prec18_up; prec=18; round=Up; "4151902e20" => "746017527.685599209");
// impl_test!(case_4151902e20_prec18_up; prec=18; round=Up; "4151902e20" => "746017527.685599209");
impl_test!(case_1850846e201_prec14_up; prec=16; round=Up; "1850846e201" => "1.227788123885769e69");
impl_test!(case_6d3797558642427987505823530913e85_prec16_up; prec=160; round=Up; "6.3797558642427987505823530913E+85" => "3995778017e19");
impl_test!(case_88573536600476899341824_prec20_up; prec=20; round=Up; "88573536600476899341824" => "44576024");
impl_test!(case_88573536600476899341824_prec7_up; prec=7; round=Up; "88573536600476899341824" => "4457603e1");
impl_test!(case_833636d150970875_prec5_up; prec=5; round=Up; "833636.150970875" => "94.115000");
impl_test!(case_833636d150970875_prec5_halfup; prec=5; round=HalfUp; "833636.150970875" => "94.115");
impl_test!(case_833636d150970875_prec4_halfup; prec=4; round=HalfUp; "833636.150970875" => "94.12");
impl_test!(case_833636d150970875_prec20_up; prec=20; round=Up; "833636.150970875" => "94.115000");
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::*;
use num_traits::FromPrimitive;
proptest! {
#[test]
fn cbrt_of_cube_is_self(f: f64, prec in 15..50u64) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
let n = BigDecimal::from_f64(f).unwrap().with_prec(prec);
let n_cubed = n.cube();
let x = n_cubed.cbrt();
prop_assert_eq!(x, n);
}
}
}
}
@@ -0,0 +1,242 @@
//! Algorithms for manipulating decimal digits
//!
//! Note: Many bit-optimizations don't apply when doing decimal
//! math, as high-order bits affect low-order decimals
//!
/// Shift u32 right by *n* decimal digits
#[allow(dead_code)]
pub fn dec_shift_right_u32(x: u32, n: usize) -> u32 {
match n {
0 => x,
1 => x / 10,
2 => x / 100,
3 => x / 1000,
4 => x / 10_000,
5 => x / 100_000,
6 => x / 1000_000,
7 => x / 10_000_000,
8 => x / 100_000_000,
9 => x / 1000_000_000,
_ => 0,
}
}
/// Shift u64 right by *n* decimal digits
#[allow(dead_code)]
pub fn dec_shift_right_u64(x: u64, n: usize) -> u64 {
match n {
0 => x,
1 => x / 10,
2 => x / 100,
3 => x / 1000,
4 => x / 10_000,
5 => x / 100_000,
6 => x / 1000_000,
7 => x / 10_000_000,
8 => x / 100_000_000,
9 => x / 1000_000_000,
10 => x / 10_000_000_000,
11 => x / 100_000_000_000,
12 => x / 1000_000_000_000,
13 => x / 10_000_000_000_000,
14 => x / 100_000_000_000_000,
15 => x / 1000_000_000_000_000,
16 => x / 10_000_000_000_000_000,
17 => x / 100_000_000_000_000_000,
18 => x / 1000_000_000_000_000_000,
19 => x / 10_000_000_000_000_000_000,
_ => 0,
}
}
macro_rules! count_digits {
($n:ident : u128) => {
if $n >= 100000000000000000000000000000000000000 {
39
} else if $n >= 10000000000000000000000000000000000000 {
38
} else if $n >= 1000000000000000000000000000000000000 {
37
} else if $n >= 100000000000000000000000000000000000 {
36
} else if $n >= 10000000000000000000000000000000000 {
35
} else if $n >= 1000000000000000000000000000000000 {
34
} else if $n >= 100000000000000000000000000000000 {
33
} else if $n >= 10000000000000000000000000000000 {
32
} else if $n >= 1000000000000000000000000000000 {
31
} else if $n >= 100000000000000000000000000000 {
30
} else if $n >= 10000000000000000000000000000 {
29
} else if $n >= 1000000000000000000000000000 {
28
} else if $n >= 100000000000000000000000000 {
27
} else if $n >= 10000000000000000000000000 {
26
} else if $n >= 1000000000000000000000000 {
25
} else if $n >= 100000000000000000000000 {
24
} else if $n >= 10000000000000000000000 {
23
} else if $n >= 1000000000000000000000 {
22
} else if $n >= 100000000000000000000 {
21
} else {
count_digits!($n:u64)
}
};
($n:ident : u64) => {
if $n >= 10000000000000000000 {
20
} else if $n >= 1000000000000000000 {
19
} else if $n >= 100000000000000000 {
18
} else if $n >= 10000000000000000 {
17
} else if $n >= 1000000000000000 {
16
} else if $n >= 100000000000000 {
15
} else if $n >= 10000000000000 {
14
} else if $n >= 1000000000000 {
13
} else if $n >= 100000000000 {
12
} else if $n >= 10000000000 {
11
} else if $n >= 1000000000 {
10
} else {
count_digits!($n:u32)
}
};
($n:ident : u32) => {
if $n >= 1000000000 {
10
} else if $n >= 100000000 {
9
} else if $n >= 10000000 {
8
} else if $n >= 1000000 {
7
} else if $n >= 100000 {
6
} else {
count_digits!($n:u16)
}
};
($n:ident : u16) => {
if $n >= 100000 {
6
} else if $n >= 10000 {
5
} else if $n >= 1000 {
4
} else {
count_digits!($n:u8)
}
};
($n:ident : u8) => {
if $n >= 100 {
3
} else if $n >= 10 {
2
} else {
1
}
};
}
/// Count digits in u32 (excluding leading-zeros)
pub(crate) fn count_digits_u32(n: u32) -> usize {
count_digits!(n:u32)
}
/// Count digits in u64 (excluding leading-zeros)
pub(crate) fn count_digits_u64(n: u64) -> usize {
if (n >> 32) == 0 {
count_digits_u32(n as u32)
} else {
count_digits!(n:u64)
}
}
/// Count digits in u128 (excluding leading-zeros)
pub(crate) fn count_digits_u128(n: u128) -> usize {
if (n >> 64) == 0 {
count_digits_u64(n as u64)
} else {
count_digits!(n:u128)
}
}
/// Return number of decimal digits in biginteger
pub(crate) fn count_digits_bigint(n: &num_bigint::BigInt) -> u64 {
count_digits_biguint(n.magnitude())
}
/// Return number of significant decimal digits in unsigned big-integer
pub(crate) fn count_digits_biguint(n: &num_bigint::BigUint) -> u64 {
use num_traits::ToPrimitive;
if let Some(n) = n.to_u64() {
return count_digits_u64(n) as u64;
}
let mut digits = (n.bits() as f64 / super::LOG2_10) as u64;
// guess number of digits based on number of bits in UInt
let mut num = super::ten_to_the_uint(digits);
debug_assert!(n * 10u8 >= num);
while n >= &num {
num *= 10u8;
digits += 1;
}
digits
}
/// Return Some(exp) if n == 10^{exp}, otherwise None
pub(crate) fn get_power_of_ten_u64(n: u64) -> Option<u8> {
match n {
0 => Some(0),
10 => Some(1),
100 => Some(2),
1000 => Some(3),
10000 => Some(4),
100000 => Some(5),
1000000 => Some(6),
10000000 => Some(7),
100000000 => Some(8),
1000000000 => Some(9),
10000000000 => Some(10),
n => {
let (q, r) = num_integer::div_rem(n, 10000000000);
if r == 0 {
get_power_of_ten_u64(q).map(|p| p + 10)
} else {
None
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
include!("decimal.tests.rs");
}
@@ -0,0 +1,129 @@
use paste::*;
macro_rules! impl_case {
($n:literal >> $s:literal => $expected:literal) => {
paste! {
#[test]
fn [< case_ $n _ $s >]() {
assert_eq!(TEST_FUNC($n, $s), $expected);
}
}
};
($n:literal => $expected:literal) => {
paste! {
#[test]
fn [< case_ $n >]() {
assert_eq!(TEST_FUNC($n), $expected);
}
}
};
}
mod count_digits_u32 {
use super::*;
const TEST_FUNC: fn(u32) -> usize = count_digits_u32;
impl_case!(0 => 1);
impl_case!(1 => 1);
impl_case!(10 => 2);
impl_case!(999999 => 6);
impl_case!(4294967295 => 10);
}
mod count_digits_u64 {
use super::*;
const TEST_FUNC: fn(u64) -> usize = count_digits_u64;
impl_case!(0 => 1);
impl_case!(1 => 1);
impl_case!(10 => 2);
impl_case!(999999 => 6);
impl_case!(4294967295 => 10);
impl_case!(18446744073709551615 => 20);
}
mod count_digits_uint {
use super::*;
#[allow(non_snake_case)]
fn TEST_FUNC(src: &str) -> u64 {
let n = src.parse().unwrap();
count_digits_biguint(&n)
}
impl_case!("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" => 99);
impl_case!("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" => 100);
impl_case!("10000000000000000000000000000000000000000000000000000000000000000" => 65);
}
mod dec_shift_right_u32 {
use super::*;
const TEST_FUNC: fn(u32, usize) -> u32 = dec_shift_right_u32;
impl_case!(0 >> 0 => 0);
impl_case!(12345 >> 0 => 12345);
impl_case!(12345 >> 1 => 1234);
impl_case!(12345 >> 2 => 123);
impl_case!(12345 >> 6 => 0);
impl_case!(12345 >> 7 => 0);
impl_case!(999999999 >> 1 => 99999999);
impl_case!(999999999 >> 5 => 9999);
impl_case!(999999999 >> 7 => 99);
}
mod dec_shift_right_u64 {
use super::*;
const TEST_FUNC: fn(u64, usize) -> u64 = dec_shift_right_u64;
impl_case!(1234567890123 >> 0 => 1234567890123);
impl_case!(1234567890123 >> 2 => 12345678901);
impl_case!(1234567890123 >> 6 => 1234567);
impl_case!(1234567890123 >> 7 => 123456);
impl_case!(18446744073709551615 >> 1 => 1844674407370955161);
impl_case!(18446744073709551615 >> 5 => 184467440737095);
impl_case!(18446744073709551615 >> 6 => 18446744073709);
impl_case!(18446744073709551615 >> 7 => 1844674407370);
impl_case!(18446744073709551615 >> 9 => 18446744073);
impl_case!(18446744073709551615 >> 10 => 1844674407);
impl_case!(18446744073709551615 >> 19 => 1);
impl_case!(18446744073709551615 >> 20 => 0);
}
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::*;
use proptest::num::f64::*;
proptest! {
#[test]
fn check_dec_shift_right_u32(n: u32) {
let mut x = 1;
for s in 0..12 {
let expected = if x > 0 { n / x } else { 0 };
x = x.checked_mul(10).unwrap_or(0);
let value = dec_shift_right_u32(n, s);
prop_assert_eq!(expected, value);
}
}
#[test]
fn check_dec_shift_right_u64(n: u64) {
let mut x = 1;
for s in 0..22 {
let expected = if x > 0 { n / x } else { 0 };
x = x.checked_mul(10).unwrap_or(0);
let value = dec_shift_right_u64(n, s);
prop_assert_eq!(expected, value);
}
}
}
}
@@ -0,0 +1,354 @@
//! inverse implementation
use crate::*;
use super::exp2;
use arithmetic::decimal::get_power_of_ten_u64;
/// Implementation of inverse: (1/n)
pub(crate) fn impl_inverse_uint_scale(n: &BigUint, scale: i64, ctx: &Context) -> BigDecimal {
if let Some(small_pow_ten) = n.to_u64().and_then(get_power_of_ten_u64) {
// optimized inversion for small power of ten:
// 1/10^{pow - scale} = 10^{scale - pow}
// create bigint with requested precision
let prec = ctx.precision().get();
let inv_int = BigInt::from(10u8).pow(prec as u32 - 1);
// increase inverted scale by requested precision
let inv_scale = small_pow_ten as i64 - scale + prec as i64 - 1;
return BigDecimal::new(inv_int, inv_scale);
}
// use f64 approximation to guess initial inverse
let guess = n.to_f64()
.filter(|f| f.is_normal())
.map(|f| 1.0 / f)
.filter(|&f| f != 0.0 && f.is_finite())
.and_then(BigDecimal::from_f64)
.map(|mut d| { d.scale -= scale; d })
.unwrap_or_else(
// couldn't use floating point, so just approximate with number of bits
|| make_inv_guess(n.bits(), scale));
let max_precision = ctx.precision().get();
let s = BigDecimal::new(BigInt::from_biguint(Sign::Plus, n.clone()), scale);
let two = BigDecimal::from(2);
let next_iteration = move |r: BigDecimal| {
let tmp = &two - &s * &r;
r * tmp
};
// calculate first iteration
let mut running_result = next_iteration(guess);
debug_assert!(!running_result.is_zero(), "Zero detected in inverse calculation of {}e{}", n, -scale);
let mut prev_result = BigDecimal::one();
let mut result = BigDecimal::zero();
// TODO: Prove that we don't need to arbitrarily limit iterations
// and that convergence can be calculated
while prev_result != result {
// store current result to test for convergence
prev_result = result;
// calculate next iteration
running_result = next_iteration(running_result).with_prec(max_precision + 2);
// 'result' has clipped precision, 'running_result' has full precision
result = if running_result.digits() > max_precision {
running_result.with_precision_round(ctx.precision(), ctx.rounding_mode())
} else {
running_result.clone()
};
}
return result;
}
/// guess inverse based on the number of bits in the integer and decimal's scale
fn make_inv_guess(bit_count: u64, scale: i64) -> BigDecimal {
// scale by ln(2)
let magic_factor = stdlib::f64::consts::LN_2;
let bit_count = bit_count as f64;
let initial_guess = magic_factor * exp2(-bit_count);
if initial_guess.is_finite() && initial_guess != 0.0 {
if let Ok(mut result) = BigDecimal::try_from(initial_guess) {
result.scale -= scale;
return result;
}
}
// backup guess for out-of-range integers
let approx_scale = bit_count * stdlib::f64::consts::LOG10_2;
let approx_scale_int = approx_scale.trunc();
let approx_scale_frac = approx_scale - approx_scale_int;
let recip = libm::exp10(-approx_scale_frac);
let mut res = BigDecimal::from_f32((magic_factor * recip) as f32).unwrap();
res.scale += approx_scale_int as i64;
res.scale -= scale;
return res;
}
#[cfg(test)]
mod test_make_inv_guess {
use super::*;
use paste::paste;
macro_rules! impl_case {
( $bin_count:literal, -$scale:literal => $expected:literal ) => {
paste! { impl_case!( [< case_ $bin_count _n $scale >]: $bin_count, -$scale => $expected); }
};
( $bin_count:literal, $scale:literal => $expected:literal ) => {
paste! { impl_case!( [< case_ $bin_count _ $scale >]: $bin_count, $scale => $expected); }
};
( $name:ident: $bin_count:expr, $scale:expr => $expected:literal ) => {
impl_case!($name: $bin_count, $scale, prec=5 => $expected);
};
( $name:ident: $bin_count:expr, $scale:expr, prec=$prec:literal => $expected:literal ) => {
#[test]
fn $name() {
let guess = make_inv_guess($bin_count, $scale);
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(guess.with_prec($prec), expected.with_prec($prec));
}
};
}
impl_case!(0, 0 => "0.69315");
impl_case!(1, 0 => "0.34657");
impl_case!(2, 0 => "0.17329");
impl_case!(2, 1 => "1.7329");
// 1 / (2^3 * 10^5) ~
impl_case!(3, -5 => "8.6643e-07");
// 2^-20
impl_case!(20, 0 => "6.6104e-07");
impl_case!(20, -900 => "6.6104E-907");
impl_case!(20, 800 => "6.6104E+793");
impl_case!(40, 10000 => "6.3041E+9987");
impl_case!(70, -5 => "5.8712e-27");
impl_case!(70, 5 => "5.8712e-17");
impl_case!(70, 50 => "5.8712e+28");
impl_case!(888, -300 => "3.3588E-568");
impl_case!(888, -19 => "3.3588E-287");
impl_case!(888, 0 => "3.3588E-268");
impl_case!(888, 270 => "335.88");
impl_case!(1022, 10 => "1.5423e-298");
impl_case!(1022, 308 => "1.5423");
impl_case!(1038, 316 => "2353.4");
impl_case!(case_31028_n659: 31028, -659 => "3.0347E-10000");
impl_case!(case_31028_0: 31028, 0 => "3.0347E-9341");
impl_case!(case_31028_1: 31028, 1 => "3.0347E-9340");
impl_case!(case_31028_9340: 31028, 9340 => ".30347");
impl_case!(case_31028_10000: 31028, 10000 => "3.0347E+659");
// impl_case!(case_max: u64::MAX, 270 => "335.88");
}
#[cfg(test)]
mod test {
use super::*;
use paste::paste;
use stdlib::num::NonZeroU64;
#[test]
fn test_inverse_35543972957198043e291() {
let v = vec![
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2324389888, 849200558
];
let x = BigInt::new(Sign::Minus, v);
let d = BigDecimal::from(x);
let expected = "-2.813416500187520746852694701086705659180043761702417561798711758892800449936819185796527214192677476E-308".parse::<BigDecimal>().unwrap();
assert_eq!(d.inverse(), expected);
assert_eq!(d.neg().inverse(), expected.neg());
}
macro_rules! impl_case {
($name:ident: $prec:literal, $round:ident => $expected:literal) => {
#[test]
fn $name() {
let n = test_input();
let prec = NonZeroU64::new($prec).unwrap();
let rounding = RoundingMode::$round;
let ctx = Context::new(prec, rounding);
let result = n.inverse_with_context(&ctx);
let expected = $expected.parse::<BigDecimal>().unwrap();
assert_eq!(&result, &expected);
let product = result * &n;
let epsilon = BigDecimal::new(BigInt::one(), $prec - 1);
let diff = (BigDecimal::one() - &product).abs();
assert!(diff < epsilon);
}
};
(prec=$prec:literal, round=$round:ident => $expected:literal) => {
paste! {
#[test]
fn [< case_prec $prec _round_ $round:lower >] () {
let n = test_input();
let prec = NonZeroU64::new($prec).unwrap();
let rounding = RoundingMode::$round;
let ctx = Context::new(prec, rounding);
let result = n.inverse_with_context(&ctx);
let expected = $expected.parse::<BigDecimal>().unwrap();
assert_eq!(&result, &expected);
assert_eq!(&result.scale, &expected.scale);
}
}
};
(prec=$prec:literal, round=$($round:ident),+ => $expected:literal) => {
$( impl_case!(prec=$prec, round=$round => $expected); )*
};
}
mod invert_one {
use super::*;
fn test_input() -> BigDecimal {
1u8.into()
}
impl_case!(prec=1, round=Up,Down => "1");
impl_case!(prec=2, round=Up,Down => "1.0");
impl_case!(prec=7, round=Up,Down => "1.000000");
}
mod invert_n1d00 {
use super::*;
fn test_input() -> BigDecimal {
"-1.00".parse().unwrap()
}
impl_case!(prec=1, round=Up,Down => "-1");
impl_case!(prec=5, round=Up,Down => "-1.0000");
}
mod invert_n1000en8 {
use super::*;
fn test_input() -> BigDecimal {
"1000e-8".parse().unwrap()
}
impl_case!(prec=1, round=Up,Down => "1e5");
impl_case!(prec=5, round=Up,Down => "10000e1");
impl_case!(prec=6, round=Up,Down => "100000");
impl_case!(prec=8, round=Up,Down => "100000.00");
}
mod invert_seven {
use super::*;
fn test_input() -> BigDecimal {
BigDecimal::from(7u8)
}
impl_case!(case_prec10_round_down: 10, Down => "0.1428571428");
impl_case!(case_prec10_round_up: 10, Up => "0.1428571429");
impl_case!(case_prec11_round_ceiling: 11, Ceiling => "0.14285714286");
}
mod invert_ten {
use super::*;
fn test_input() -> BigDecimal {
10u8.into()
}
impl_case!(case_prec1_round_down: 1, Down => "0.1");
impl_case!(case_prec2_round_down: 2, Down => "0.10");
impl_case!(prec=10, round=Up, Down => "0.1000000000");
}
mod invert_n3242342d34324 {
use super::*;
fn test_input() -> BigDecimal {
"-3242342.34324".parse().unwrap()
}
// note: floor ceiling wrong
impl_case!(prec=50, round=Up, Ceiling => "-3.0841900519385698894827476971712670726697831310897E-7");
impl_case!(prec=50, round=Down, Floor => "-3.0841900519385698894827476971712670726697831310896E-7");
}
mod invert_2d8722377233432854650en126 {
use super::*;
fn test_input() -> BigDecimal {
"28722377233432854650456573411382289859440620032075590707304700193738855195818029876568741547799767753181511758371393266031229989006058870578446812747289276920741036671713994469786904880406812933015496296559493964954240161851051500623562557032166800306346000498803201936493334049050141321136859175463065287081665388768669799901545047760009765625e-469"
.parse().unwrap()
}
impl_case!(prec=1, round=Up => "4e125");
impl_case!(prec=5, round=Up => "3.4817e+125");
impl_case!(prec=25, round=Up => "3.481605968311006434080812E+125");
}
#[test]
fn inv_random_number() {
let n = BigDecimal::try_from(0.08121970592310568).unwrap();
let ctx = Context::new(NonZeroU64::new(40).unwrap(), RoundingMode::Down);
let i = n.inverse_with_context(&ctx);
assert_eq!(&i, &"12.31228294456944530942557443718279245563".parse::<BigDecimal>().unwrap());
let product = i * &n;
assert!(BigDecimal::one() - &product < "1e-39".parse::<BigDecimal>().unwrap());
}
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::*;
use num_traits::FromPrimitive;
proptest! {
#[test]
fn inverse_multiplies_to_one(f: f64, prec in 1..100u64) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
prop_assume!(f != 0.0);
let n = BigDecimal::from_f64(f).unwrap();
let ctx = Context::new(NonZeroU64::new(prec).unwrap(), RoundingMode::Up);
let i = n.inverse_with_context(&ctx);
let product = &i * &n;
// accurate to precision minus one (due to rounding)
let epsilon = BigDecimal::new(1.into(), prec as i64 - 1);
let diff_from_one = BigDecimal::one() - &product;
prop_assert!(diff_from_one.abs() < epsilon, "{} >= {}", diff_from_one.abs(), epsilon);
}
}
}
}
+233
View File
@@ -0,0 +1,233 @@
//! arithmetic routines
use crate::*;
use num_traits::CheckedSub;
use num_traits::AsPrimitive;
pub(crate) mod decimal;
pub(crate) mod addition;
pub(crate) mod multiplication;
pub(crate) mod modulo;
pub(crate) mod sqrt;
pub(crate) mod cbrt;
pub(crate) mod inverse;
pub(crate) mod pow;
pub(crate) use self::decimal::{
count_digits_bigint as count_decimal_digits,
count_digits_biguint as count_decimal_digits_uint,
};
#[cfg(not(feature = "std"))]
mod funcs {
// f64::exp2 is only available in std, we have to use an external crate like libm
pub fn exp2(x: f64) -> f64 {
libm::exp2(x)
}
// f64::log10 is only available in std, we have to use an external crate like libm
pub fn log10(x: f64) -> f64 {
libm::log10(x)
}
}
#[cfg(feature = "std")]
mod funcs {
pub fn exp2(x: f64) -> f64 {
x.exp2()
}
pub fn log10(x: f64) -> f64 {
x.log10()
}
}
// rexport all funcs into this module
pub(crate) use self::funcs::*;
/// Return 10^pow
///
/// Try to calculate this with fewest number of allocations
///
pub(crate) fn ten_to_the(pow: u64) -> BigInt {
ten_to_the_uint(pow).into()
}
/// Return 10^{pow} as u64
pub(crate) fn ten_to_the_u64(pow: u8) -> u64 {
debug_assert!(pow < 20);
10u64.pow(pow as u32)
}
/// Return 10^{pow} as output
pub(crate) fn ten_to_the_t<T>(pow: u8) -> T
where
T: From<u8> + num_traits::Pow<u8, Output = T>
{
debug_assert!((pow as f64) < stdlib::mem::size_of::<T>() as f64 * 8.0 / LOG2_10);
T::from(10u8).pow(pow)
}
/// Return 10^pow
pub(crate) fn ten_to_the_uint(pow: u64) -> BigUint {
if pow < 20 {
return BigUint::from(10u64.pow(pow as u32));
}
// linear case of 10^pow = 10^(19 * count + rem)
if pow < 590 {
let ten_to_nineteen = 10u64.pow(19);
// count factors of 19
let (count, rem) = pow.div_rem(&19);
let mut res = BigUint::from(ten_to_nineteen);
for _ in 1..count {
res *= ten_to_nineteen;
}
if rem != 0 {
res *= 10u64.pow(rem as u32);
}
return res;
}
// use recursive algorithm where linear case might be too slow
let (quotient, rem) = pow.div_rem(&16);
let x = ten_to_the_uint(quotient);
let x2 = &x * &x;
let x4 = &x2 * &x2;
let x8 = &x4 * &x4;
let res = &x8 * &x8;
if rem == 0 {
res
} else {
res * 10u64.pow(rem as u32)
}
}
pub(crate) fn multiply_by_ten_to_the_uint<T, P>(n: &mut T, pow: P)
where
T: MulAssign<u64> + MulAssign<BigUint>,
P: ToPrimitive,
{
let pow = pow.to_u64().expect("exponent overflow error");
if pow < 20 {
*n *= 10u64.pow(pow as u32);
} else {
*n *= ten_to_the_uint(pow);
}
}
/// Return difference of two numbers, returning diff as u64
pub(crate) fn diff<T>(a: T, b: T) -> (Ordering, u64)
where
T: ToPrimitive + CheckedSub + stdlib::cmp::Ord,
{
use stdlib::cmp::Ordering::*;
let (ord, diff) = checked_diff(a, b);
(ord, diff.expect("subtraction overflow"))
}
/// Return difference of two numbers. If num doesn't fit in u64, return None
pub(crate) fn checked_diff<T>(a: T, b: T) -> (Ordering, Option<u64>)
where
T: ToPrimitive + CheckedSub + stdlib::cmp::Ord,
{
use stdlib::cmp::Ordering::*;
let _try_subtracting = |x: T, y: T| x.checked_sub(&y).and_then(|diff| diff.to_u64());
match a.cmp(&b) {
Less => (Less, _try_subtracting(b, a)),
Greater => (Greater, _try_subtracting(a, b)),
Equal => (Equal, Some(0)),
}
}
/// Return difference of two numbers, returning diff as usize
#[allow(dead_code)]
pub(crate) fn diff_usize<T>(a: T, b: T) -> (Ordering, usize)
where
T: AsPrimitive<usize> + stdlib::ops::Sub<Output = T> + stdlib::cmp::Ord,
{
use stdlib::cmp::Ordering::*;
match a.cmp(&b) {
Less => (Less, (b - a).as_()),
Greater => (Greater, (a - b).as_()),
Equal => (Equal, 0),
}
}
/// Return absolute difference between two numbers
#[cfg(rustc_1_60)]
#[allow(clippy::incompatible_msrv)]
#[allow(dead_code)]
pub(crate) fn abs_diff(x: i64, y: i64) -> u64 {
x.abs_diff(y)
}
#[cfg(not(rustc_1_60))]
#[allow(dead_code)]
pub(crate) fn abs_diff(x: i64, y: i64) -> u64 {
(x as i128 - y as i128).to_u64().unwrap_or(0)
}
/// Add carry to given number, returning trimmed value and storing overflow back in carry
///
pub(crate) fn add_carry(n: u8, carry: &mut u8) -> u8 {
let s = n + *carry;
if s < 10 {
*carry = 0;
s
} else {
debug_assert!(s < 20);
*carry = 1;
s - 10
}
}
/// If n is greater than 10, split and store overflow in carry
///
/// No action if n is less than 10.
///
/// Carry is not allowed to be 1 if n is two digits
///
pub(crate) fn store_carry(n: u8, carry: &mut u8) -> u8 {
if n < 10 {
n
} else {
debug_assert!(n < 20);
debug_assert_eq!(carry, &0);
*carry = 1;
n - 10
}
}
/// Extend destination vector with values in D, adding carry while carry is not zero
///
/// If carry overflows, it is NOT pushed into the destination vector.
///
pub(crate) fn extend_adding_with_carry<D: Iterator<Item = u8>>(
dest: &mut Vec<u8>,
mut digits: D,
carry: &mut u8,
) {
while *carry != 0 {
match digits.next() {
Some(d) => {
dest.push(add_carry(d, carry))
}
None => {
return;
}
}
}
dest.extend(digits);
}
@@ -0,0 +1,39 @@
use crate::*;
/// optimized calculation of n % 10
#[allow(dead_code)]
pub(crate) fn mod_ten_uint(n: &BigUint) -> u8 {
mod_ten_2p64_le(n.iter_u64_digits())
}
/// optimized calculation of n % 10
pub(crate) fn mod_ten_2p64_le(mut digits: impl Iterator<Item = u64>) -> u8 {
let d0 = digits.next().unwrap_or(0) % 10;
let mut acc: u64 = digits.map(|d| d % 10).sum();
acc *= 6;
acc += d0;
(acc % 10) as u8
}
/// optimized calculation of n % 100
pub(crate) fn mod_100_uint(n: &BigUint) -> u8 {
mod_100_2p64_le(n.iter_u64_digits())
}
/// optimized calculation of n % 100
/// TODO: compare implementations: https://rust.godbolt.org/z/Kcxor1MT5
pub(crate) fn mod_100_2p64_le(mut digits: impl Iterator<Item = u64>) -> u8 {
let mods_2p64 = [16, 56, 96, 36, 76];
let mut acc_v = [ 0, 0, 0, 0, 0];
let d0 = digits.next().unwrap_or(0) % 100;
for (i, d) in digits.enumerate() {
acc_v[i % 5] += d % 100;
}
let mut acc = d0;
for (&a, m) in acc_v.iter().zip(mods_2p64.iter()) {
acc += m * (a % 100);
}
(acc % 100) as u8
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,586 @@
use paste::paste;
mod multiply_scaled_u64_slices_with_prec_into {
use super::*;
macro_rules! impl_case {
($name:ident: $prec:literal => [$($le_d:literal),*] E $exp:literal) => {
impl_case!($name: round=Up; $prec => [$($le_d),*] E $exp);
};
($name:ident: round=$round:ident; $prec:literal => [$($le_d:literal),*] E $exp:literal) => {
impl_case!($name: round=$round; $prec => Plus [$($le_d),*] E $exp);
};
($name:ident: round=$round:ident; $prec:literal => $sign:ident [$($expected:literal),*] E $exp:literal) => {
#[test]
fn $name() {
let a = input_a();
let b = input_b();
let prec = NonZeroU64::new($prec).unwrap();
let expected: &[u64] = &[$($expected),*];
let expected_scale = -$exp;
let rounding = NonDigitRoundingData {
mode: RoundingMode::$round,
sign: Sign::$sign,
};
let mut product = WithScale::default();
multiply_scaled_u64_slices_with_prec_into(
&mut product, a.as_digit_slice(), b.as_digit_slice(), prec, rounding
);
assert_eq!(&product.value.digits[..], &expected[..]);
assert_eq!(product.scale, expected_scale);
}
};
}
mod case_1667660160137446952e158 {
use super::*;
fn input_a() -> WithScale<DigitVec<RADIX_u64, LittleEndian>> {
WithScale {
scale: 34,
value: DigitVec::from_vec(vec![
16175263208544711346,
5279606946643537445,
15341572592040251541,
10541804148642044343,
7522665456306621740,
13427633752471237939,
16469249152156353442,
11787750781150891403,
3583949032441389106,
18383297448742447850,
365519312995810676,
]),
}
}
mod times_2d998213en173 {
use super::*;
fn input_b() -> WithScale<DigitVec<RADIX_u64, LittleEndian>> {
WithScale {
scale: 272,
value: DigitVec::from_vec(vec![
1344009339610453499,
6464650240578252661,
13739370975302393952,
1107949409989368443,
12288971738008765224,
1403
]),
}
}
impl_case!(prec19_roundup: round=Up;
19 => [5000000000000000001] E -15);
impl_case!(prec20_roundup: round=Up;
20 => [13106511852580896769, 2] E -16);
impl_case!(prec100_roundup: round=Up;
100 => [1, 6059390473091416064, 5120748075179647364, 14006871066438625056, 15461642991872345491, 2340] E -96);
impl_case!(neg_prec100_roundceiling: round=Ceiling;
100 => Minus [0, 6059390473091416064, 5120748075179647364, 14006871066438625056, 15461642991872345491, 2340] E -96);
impl_case!(neg_prec100_roundfloor: round=Floor;
100 => Minus [1, 6059390473091416064, 5120748075179647364, 14006871066438625056, 15461642991872345491, 2340] E -96);
}
mod times_5996425554en186 {
use super::*;
fn input_b() -> WithScale<DigitVec<RADIX_u64, LittleEndian>> {
WithScale {
scale: 204,
value: DigitVec::from_vec(
vec![12019064456476625820, 325066880]
),
}
}
impl_case!(prec1_roundup: round=Up; 1 => [1] E 0);
impl_case!(prec1_rounddown: round=Down; 1 => [9] E -1);
impl_case!(prec1_roundfloor: round=Floor; 1 => [9] E -1);
impl_case!(neg_prec1_rounddown: round=Down; 1 => Minus [9] E -1);
impl_case!(neg_prec1_roundfloor: round=Floor; 1 => Minus [1] E 0);
impl_case!(prec10_roundup: round=Floor; 10 => Minus [1000000000] E -9);
impl_case!(prec25_roundup: round=Floor; 25 => Minus [2003764205206896640, 54210] E -24);
impl_case!(prec40_roundup: round=Up;
40 => [13399721896776114178, 7145508105175220139, 29] E -40);
}
mod times_7303919561276644672e10 {
use super::*;
fn input_b() -> WithScale<DigitVec<RADIX_u64, LittleEndian>> {
WithScale {
scale: 7,
value: DigitVec::from_vec(
vec![16568107915372457172, 39594627279977552]
),
}
}
impl_case!(prec1_up: round=Up; 1 => [2] E 205);
impl_case!(prec1_down: round=Down; 1 => [1] E 205);
impl_case!(prec11_up: round=Up; 11 => [12180455666] E 195);
impl_case!(prec25: 25 => [7055331922361162211, 66030] E 181);
impl_case!(prec_30_up: 30 => [571648947000381622, 6603038247] E 176);
impl_case!(prec_31_up: 31 => [5716489470003816211, 66030382470] E 175);
impl_case!(prec_57_up: 57 =>
[3586467374344137641, 14971304827359975165, 357951420621793766] E 149);
impl_case!(prec_75_up: 75 =>
[16970166308972310451, 15246513073646694077, 13267213422529722744, 19404585394121069] E 131);
}
}
mod random_values {
use super::*;
fn input_a() -> WithScale<BigDigitVec> {
let a = vec![
1745623865429447471,
9152528756446785169,
791242510259261833,
8695454801466577396,
8392994416015036890,
2912771882945679798,
8993891992610859856,
8294679885144779824,
8631634693823981953,
366928013,
];
WithScale {
value: BigDigitVec::from_vec(a),
scale: 100,
}
}
fn input_b() -> WithScale<BigDigitVec> {
let b = vec![
518171251856216712,
8211715648977239115,
1585852536764360977,
1552672896608399639,
1044291249247,
];
WithScale {
value: BigDigitVec::from_vec(b),
scale: -5,
}
}
impl_case!(prec100: 100 => [
7841754692876239913,
2960052148898606502,
16409584969160271562,
16913806906493283056,
14019529503395454758,
513 ] E 77);
impl_case!(prec10_rd: round=Down; 10 => [ 1097384700 ] E 167);
impl_case!(prec20_rd: round=Down; 20 => [ 10973847000387492259 ] E 157);
impl_case!(prec20_ru: round=Up; 20 => [ 10973847000387492260 ] E 157);
impl_case!(prec45: 45 => [ 14618765252943857298, 6989353086943465649, 322492 ] E 132);
impl_case!(prec46: 46 => [ 17060444013471711666, 14553298648306001649, 3224923 ] E 131);
}
}
mod multiply_at_product_index {
use super::*;
macro_rules! impl_case {
($name:ident: $idx:literal => $expected:expr) => {
#[test]
fn $name() {
let (a, b) = test_input();
let a = BigDigitSliceP19::from_slice(&a);
let b = BigDigitSliceP19::from_slice(&b);
let mut product = DigitVec::<RADIX_10p19_u64, LittleEndian>::new();
let expected: &[u64] = &$expected;
multiply_at_product_index(&mut product, a, b, $idx);
assert_eq!(product.digits, expected);
}
};
}
mod random_values {
use super::*;
fn test_input() -> (Vec<u64>, Vec<u64>) {
let a = vec![
1745623865429447471,
9152528756446785169,
791242510259261833,
8695454801466577396,
8392994416015036890,
2912771882945679798,
8993891992610859856,
8294679885144779824,
8631634693823981953,
366928013,
];
let b = vec![
518171251856216712,
8211715648977239115,
1585852536764360977,
1552672896608399639,
1044291249247,
];
(a, b)
}
impl_case!(case_1: 1 => [
3259170168870572493,
8530725771486096200,
2353601569174537990,
9915907985447408257,
5505012768436728039,
3337388859546710366,
1787670230149175286,
7565280555772427422,
7309104897441821141,
4201853743352582046,
9605931707432557799,
9324155135517901886,
3179713980940485874,
38
]);
impl_case!(case_3: 3 => [
4519975091742652829,
9915907985447408256,
5505012768436728039,
3337388859546710366,
1787670230149175286,
7565280555772427422,
7309104897441821141,
4201853743352582046,
9605931707432557799,
9324155135517901886,
3179713980940485874,
38
]);
impl_case!(case_9: 9 => [
8171925425020376989,
4201853743352582045,
9605931707432557799,
9324155135517901886,
3179713980940485874,
38
]);
impl_case!(case_12: 12 => [
7983943745001026698,
3179713980940485874,
38
]);
impl_case!(case_20: 20 => []);
}
}
mod multiply_big_int_with_ctx {
use super::*;
macro_rules! impl_case {
(full => $expected:literal) => {
#[test]
fn case_full() {
let (x, y) = test_input();
let product = &x * &y;
let expected: BigInt = $expected.parse().unwrap();
assert_eq!(&expected, &product);
}
};
($prec:literal => $expected:literal E $exp:literal) => {
paste! {
#[test]
fn [< case_prec $prec >] () {
impl_case!(
IMPL;
ctx=Context::default().with_prec($prec);
$expected;
$exp
);
}
}
};
($prec:literal; $mode:ident => $expected:literal E $exp:literal) => {
paste! {
#[test]
fn [< case_prec $prec _ $mode:lower >]() {
impl_case!(
IMPL;
ctx=Context::default().with_rounding_mode(RoundingMode::$mode).with_prec($prec);
$expected;
$exp
);
}
}
};
($prec:literal; $($modes:ident),+ => $expected:literal E $exp:literal) => {
$( impl_case!($prec; $modes => $expected E $exp); )*
};
(IMPL; ctx=$ctx:expr; $expected:literal; $exp:literal) => {
let (x, y) = test_input();
let ctx = $ctx.unwrap();
let product = multiply_big_int_with_ctx(&x, &y, ctx);
let expected: BigInt = $expected.parse().unwrap();
let scale = -$exp;
assert_eq!(&expected, &product.value);
assert_eq!(&scale, &product.scale);
};
}
mod mul_577874872717e492_696712038e285 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "577874872717466911184559916156758198495131305219358809343652669536302387743158062880956367761473177845712351639985492473123959095758451817231985325403278703100241171849679780394380997111752306496891336078616237486336807931580623365870368160556723018500080769313066709500174011587939745841025646291922884710337974681230011250190261422246454991091897190225976651017177719025146752407491035501139104790891684620162820417602920652689929206945524922873810759445524737158705885775589194963686168193817138671875".parse().unwrap();
let y: BigInt = "696712038660547059904752779034165119484624188403362872193018065005292090954487758438349971006124876297418423724096428714200094204277821355374358649621264188134962601815256767310343261823948242351360751280910536781247104655969073243119218479973070589731361603163735999605615737734639873901461504".parse().unwrap();
(x, y)
}
impl_case!(100; Down => "4026123806616905180553157364566020245817156933504926464873554931196880658239704482799527614035787776" E 698);
impl_case!(20; Down => "40261238066169051805" E 778);
}
mod mul_1442862119e445_1049464e105 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "14428621197889752189861398972981082461843826128512116303033203450866726426082853906596666646209600245865382634277766055876367040002473414960534339870779684891851540020597214991380071631960919603045037521801802725511393621544601218214902662462163496096667869677784522965589318809320143842055299565296240744974377213638202218555750884767181679713147685927591605307661717624963762005955819493848581352190387436454778284644628882915640133433043956756591796875".parse().unwrap();
let y: BigInt = "1049464598544894921302784839734515848195965967618699631485860068380390543981052363225912818001120911046454804480".parse().unwrap();
(x, y)
}
impl_case!(20; HalfEven => "15142327152999729642" E 546);
impl_case!(138; HalfDown => "151423271529997296423843423920943952557791563643246327643459830966880329966739641792530818647522102274133154353120322001711481566221946243" E 428);
impl_case!(246; Up,
HalfUp => "151423271529997296423843423920943952557791563643246327643459830966880329966739641792530818647522102274133154353120322001711481566221946242709348400715315872658516242116494741084148721184917688258189092970640993174313848612655419856309890747070313" E 320);
impl_case!(246; Down,
HalfDown,
HalfEven => "151423271529997296423843423920943952557791563643246327643459830966880329966739641792530818647522102274133154353120322001711481566221946242709348400715315872658516242116494741084148721184917688258189092970640993174313848612655419856309890747070312" E 320);
impl_case!(247; Up, Down => "1514232715299972964238434239209439525577915636432463276434598309668803299667396417925308186475221022741331543531203220017114815662219462427093484007153158726585162421164947410841487211849176882581890929706409931743138486126554198563098907470703125" E 319);
impl_case!(248; Up, Down => "15142327152999729642384342392094395255779156364324632764345983096688032996673964179253081864752210227413315435312032200171148156622194624270934840071531587265851624211649474108414872118491768825818909297064099317431384861265541985630989074707031250" E 318);
impl_case!(357; Down => "151423271529997296423843423920943952557791563643246327643459830966880329966739641792530818647522102274133154353120322001711481566221946242709348400715315872658516242116494741084148721184917688258189092970640993174313848612655419856309890747070312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" E 209);
}
mod mul_11552262607819902e131_75e161 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "1155226260781990200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse().unwrap();
let y: BigInt = "7500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse().unwrap();
(x, y)
}
impl_case!(20 => "86641969558649265000" E 290);
impl_case!(18; HalfUp => "866419695586492650" E 292);
impl_case!(17; HalfUp, HalfDown => "86641969558649265" E 293);
impl_case!(16; Up, HalfUp => "8664196955864927" E 294);
impl_case!(16; Down, HalfDown, HalfEven => "8664196955864926" E 294);
impl_case!(15; Up, HalfDown => "866419695586493" E 295);
impl_case!(15; Down => "866419695586492" E 295);
impl_case!( 3; HalfUp => "866" E 307);
}
mod mul_2301346222e107_14293213e315 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "230134622236549555995211986626222699902919552403311068655782487235360869740373750138395520899795192776856789295000000".parse().unwrap();
let y: BigInt = "14293213600958985303333846898378944669728091790434135672846064625014252175269303162751477769546102012233954333273726511104485055758813349716438744379331517132722718015116165694607959258435272025702738339275667597501313358033107443840555382841970678973851997248262639615206455939322314712306500000000000000000000000000000000".parse().unwrap();
(x, y)
}
impl_case!(1 => "3" E 438);
impl_case!(10 => "3289363313" E 429);
impl_case!(20 => "32893633126030082513" E 419);
impl_case!(401 => "32893633126030082513378632456028104243338158146601751534822570641115861940893690474203736021657204584394456560452273117013862292944834269220913901434456366640765512794407601501204644375698772310462659595240672712968083946415944676232524453019368460583076233422452053267394081654933088569072574309446551869537537085955808336137944176420693881753901339121561334586231165213326029020686758979692139589175" E 38);
impl_case!(400; HalfUp => "3289363312603008251337863245602810424333815814660175153482257064111586194089369047420373602165720458439445656045227311701386229294483426922091390143445636664076551279440760150120464437569877231046265959524067271296808394641594467623252445301936846058307623342245205326739408165493308856907257430944655186953753708595580833613794417642069388175390133912156133458623116521332602902068675897969213958918" E 39);
impl_case!(400; HalfDown => "3289363312603008251337863245602810424333815814660175153482257064111586194089369047420373602165720458439445656045227311701386229294483426922091390143445636664076551279440760150120464437569877231046265959524067271296808394641594467623252445301936846058307623342245205326739408165493308856907257430944655186953753708595580833613794417642069388175390133912156133458623116521332602902068675897969213958917" E 39);
}
mod mul_166282988e757_855704757e288 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "1662829881972302555130573804034884736045567192245210098539333943847359516964532887814910330238584574704591142597582120739098300776070255016531228502052315027932433758655554089934478913134076538917555237055311466963257200943418192284685329544412527781851150111516400149208425751400577475249704818681525213682644230996369594895101870162749874014287967075708189893449815333186508386746072349175785274400940465234953370696338639401851764570625527530834473094592285942335138352231629223479010378029739170207319201385183107178796700474330961290205720379495017762115091196226436689486111860406277126830834375106428374156802498831754786978409114271376697671477362897657305451012544150393117399631804227698370258868601667565428070820650330308154707381618209183216094970703125".parse().unwrap();
let y: BigInt = "855704757901450759869356338513283551486583906378815084661323942969666040804246301726741288371813738860776552307977511102966634857016690379580472911872149121752146077285669707976656886280326152572122998172317660237359659093011810926589974967764428889136711853677544671180201059446857211098370670592".parse().unwrap();
(x, y)
}
impl_case!(132 => "142289144158440709930589101331424815642676467582494920860869705909839132157013788030340030454912181312465691007673740386962890625000" E 931);
impl_case!(63 => "142289144158440709930589101331424815642676467582494920860869706" E 1000);
impl_case!(30 => "142289144158440709930589101331" E 1033);
impl_case!(20 => "14228914415844070993" E 1043);
impl_case!(19 => "1422891441584407099" E 1044);
}
// test (2^5000-1) * (2^1500-1)
mod mul_2p5000m1_2p1501m1 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "141246703213942603683520966701614733366889617518454111681368808585711816984270751255808912631671152637335603208431366082764203838069979338335971185726639923431051777851865399011877999645131707069373498212631323752553111215372844035950900535954860733418453405575566736801565587405464699640499050849699472357900905617571376618228216434213181520991556677126498651782204174061830939239176861341383294018240225838692725596147005144243281075275629495339093813198966735633606329691023842454125835888656873133981287240980008838073668221804264432910894030789020219440578198488267339768238872279902157420307247570510423845868872596735891805818727796435753018518086641356012851302546726823009250218328018251907340245449863183265637987862198511046362985461949587281119139907228004385942880953958816554567625296086916885774828934449941362416588675326940332561103664556982622206834474219811081872404929503481991376740379825998791411879802717583885498575115299471743469241117070230398103378615232793710290992656444842895511830355733152020804157920090041811951880456705515468349446182731742327685989277607620709525878318766488368348965015474997864119765441433356928012344111765735336393557879214937004347568208665958717764059293592887514292843557047089164876483116615691886203812997555690171892169733755224469032475078797830901321579940127337210694377283439922280274060798234786740434893458120198341101033812506720046609891160700284002100980452964039788704335302619337597862052192280371481132164147186514169090917191909375".parse().unwrap();
let y: BigInt = "35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150406795437517399294548941469959754171038918004700847889956485329097264486802711583462946536682184340138629451355458264946342525383619389314960644665052551751442335509249173361130355796109709885580674313954210217657847432626760733004753275317192133674703563372783297041993227052663333668509952000175053355529058880434182538386715523683713208549375".parse().unwrap();
(x, y)
}
impl_case!(100 => "4954180389441794407302644533158844937036173071799942385207702899881096950794929284226174201155309760" E 1857);
impl_case!(70 => "4954180389441794407302644533158844937036173071799942385207702899881097" E 1887);
impl_case!(20 => "49541803894417944073" E 1937);
}
mod multiply_313313e185_140912e85 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "31331330514777647459696918012218766637269396231379435058341584170846149718531941093035596483272466942484919002494751588025494203950111183556196762802239021663296916615390846043521157975900649".parse().unwrap();
let y: BigInt = "1409125393389843319552855599302577071349036214812589000980540875883362915766473073232671889".parse().unwrap();
(x, y)
}
impl_case!(full => "44149773437063254678149469396251230458443452710019771114377331920312228495036605502543146558201981056772851870606187717471634519393139631393769297684773531284154562671396651882745113413784696354015721073630190690162770887707923095632780007819514677121000367593109419444597479155961");
impl_case!(100 => "4414977343706325467814946939625123045844345271001977111437733192031222849503660550254314655820198106" E 181);
impl_case!(50 => "44149773437063254678149469396251230458443452710020" E 231);
impl_case!(21 => "441497734370632546781" E 260);
}
mod mul_354436071780069e151_282138326095801e70 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "3544360717800690051921825754840028412915557760215486673759084471433756826848643934952495657908167389640789167410480517347773985344060322134315971203024120535921247142".parse().unwrap();
let y: BigInt = "2821383260958014531084804730393168953719437088977599878666724657220634716408631037763".parse().unwrap();
(x, y)
}
impl_case!(200; Up => "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999979587838286624448431722853801267006" E 50);
impl_case!(200; Down => "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999979587838286624448431722853801267005" E 50);
impl_case!(166; Up => "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998" E 84);
impl_case!(166; Down => "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997" E 84);
impl_case!(165; Up => "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" E 86);
impl_case!(165; Down => "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" E 85);
impl_case!(42; Up => "100000000000000000000000000000000000000000" E 209);
impl_case!(22; Up => "1000000000000000000000" E 229);
impl_case!(21; Up => "100000000000000000000" E 230);
impl_case!(20; Up => "10000000000000000000" E 231);
impl_case!(19; Up => "1000000000000000000" E 232);
impl_case!(12; Up => "100000000000" E 239);
impl_case!(12; Down => "999999999999" E 238);
impl_case!(2; Up => "10" E 249);
impl_case!(2; Down => "99" E 248);
impl_case!(1; Up, HalfUp => "1" E 250);
impl_case!(1; Down => "9" E 249);
}
mod mul_3721418e448_3844674e161 {
use super::*;
fn test_input() -> (BigInt, BigInt) {
let x: BigInt = "37214180020990129688144525415958680174157975522481928636541900907279612101437934903263895850798267657684166647521832553912867028735643169656904261854440137332351057065885513562123737325309196016024433700918100990040278606068777262035069231336055867161206731423272839919925319426271071135470308154818218588125994382305796706103307807638138902394400413759405500315832312273461261134673346351894542208780520017757692703697092184711436857469379901885986328125".parse().unwrap();
let y: BigInt = "384467472657945478705050171808793258461219095507172038645145218810507057013081321321499154743364560076819504633305258370960191863566109907922325021911311899034388627258125176177530017308946900981633538093921103514946225947524282186623225866254314533377160053778776276453031176006567736506050524284200740234201681989688527171382167496044598504125969423204217194885709731987591265467004291805188733436497695584275662479958826872492302565650006868261814480548258209741016215533270514872064920715433587381471128142127411378792338183702243894864240468654283770230621234327560844588972472126092496338279872812038026604640358131717471991285825660709098949913655676881776223568791337070502357370402925535826378792284995488461006043177936586374698890722356736660003662109375".parse().unwrap();
(x, y)
}
impl_case!(1220 => "14307641739707883586679338985784075376978599376076587159440407707473484422913380008679767889422355347578618773517358618682873816807001368824338399142290429730889921795132627329720311739245335457279882660403134178519487192193894827422017154794231967246548713182248254712192749435152248607256549045476459086684404679865181612177150422506682419296142751964295148026282623865246096746974909857474439409542650897431239188074724043848310873968922165900982577988822068956986831441597090874147542405989083220921853122209746471544669432201826149806457043393719329728030519620309220466905001009022724582301547918511296250822446396192324003728354373460047809727056243017571910340832631321081615757824717334420837535928747492488405758229871707378866111943107064677309884245984942614408304014573934636656429274910312463230088178109721849404398091245972145748205556337972593506762958354058055515482983023700007919157035627908661458407216568244912244912595573815702329952766665565695461070495291089051793343278543261429000685884437880347246913152237805580594025316169462092558252280687656044981784943452157349160827416439182889565322019088971898689733394885908687909675808992617947650750136428902947738350803774665109813213348388671875" E 0);
}
}
mod test_multiply_quad_spread_into {
use super::*;
macro_rules! impl_case {
( wrapping: $($toks:tt)* ) => {
impl_case!(multiply_quad_spread_into_wrapping; $($toks)*);
};
(
$func:ident;
$s:literal @ $n:literal,
[ $a:literal, $b:literal, $y:literal, $z:literal] => $expected:expr
) => {
paste!{
#[test]
fn [< case_ $n _ $a _ $b _ $y _ $z >]() {
let mut result = vec![0; $s];
$func(&mut result, $n, $a, $b, $y, $z);
let expected = &$expected;
assert_eq!(expected, result.as_slice());
}
}
};
( $($toks:tt)* ) => {
impl_case!(multiply_quad_spread_into; $($toks)*);
};
}
impl_case!(
8 @ 2,
[2559712337, 684026673, 1163340730, 1823138616]
=> [0u32, 0, 3001179060, 4203670869, 1059648540, 580714756, 0, 0]);
impl_case!(
6 @ 1,
[4294967295, 4294967295, 4294967295, 4294967295]
=> [0u32, 2, 0, 4294967292, 4294967295, 1]);
impl_case!(
wrapping: 8 @ 6,
[2559712337, 684026673, 1163340730, 1823138616]
=> [1059648540u32, 580714756, 0, 0, 0, 0, 3001179060, 4203670869]);
}
#[cfg(all(test, property_tests))]
mod props {
use super::*;
use proptest::{self as pt};
use proptest::prelude::*;
use num_traits::FromPrimitive;
fn random_f64() -> pt::num::f64::Any {
use proptest::num::f64::*;
NORMAL | SUBNORMAL | ZERO | NEGATIVE
}
fn rounding_modes() -> impl Strategy<Value = RoundingMode> {
prop_oneof![
Just(RoundingMode::Down),
Just(RoundingMode::Up),
Just(RoundingMode::HalfEven),
]
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(10_000))]
#[test]
fn test_multiply_f64_decimals_with_context(
a in random_f64(), b in random_f64(), prec in 1..6000u64, mode in rounding_modes()
) {
let prec = NonZeroU64::new(prec).unwrap();
let a: BigDecimal = BigDecimal::from_f64(a).unwrap();
let b: BigDecimal = BigDecimal::from_f64(b).unwrap();
let a_times_b = &a * &b;
let a_times_b_rounded = a_times_b.with_precision_round(prec, mode);
let mut dest = BigDecimal::default();
let ctx = Context::new(prec, mode);
super::multiply_decimals_with_context(&mut dest, &a, &b, &ctx);
prop_assert_eq!(a_times_b_rounded, dest)
}
}
}
+340
View File
@@ -0,0 +1,340 @@
//! pow implementation
use stdlib::num::NonZeroU64;
use crate::*;
use super::log10;
use super::multiplication::{
multiply_decimals_with_context,
multiply_slices_with_prec_into_p19_z,
};
use bigdigit::digitvec::{DigitVec, DigitSlice};
use bigdigit::radix::{RadixType, RadixPowerOfTen, RADIX_u64, RADIX_10p19_u64};
use bigdigit::endian::{Endianness, LittleEndian};
use arithmetic::multiplication::mul_scaled_slices_truncating_into;
/// Compute bd**exp using exponentiation by squaring algorithm, while maintaining the
/// precision specified in ctx (the number of digits would otherwise explode).
///
/// Algorithm comes from https://en.wikipedia.org/wiki/Exponentiation_by_squaring
pub(crate) fn impl_powi_with_context<'a>(
bd: impl Into<BigDecimalRef<'a>>,
exp: i64,
ctx: &Context,
) -> BigDecimal {
powi_with_context(bd.into(), exp, ctx)
}
/// Calculate BigDecimalRef `bd` to power of signed integer, using context
/// to set precision
fn powi_with_context(
bd: BigDecimalRef,
exp: i64,
ctx: &Context,
) -> BigDecimal {
match exp.cmp(&0) {
Ordering::Equal => BigDecimal::one(),
Ordering::Greater => pow_u64_with_context(bd, exp as u64, ctx),
Ordering::Less => {
if exp == -1 {
return bd.inverse_with_context(&ctx);
}
// create truncating "double wide" context for inverting
let inv_ctx = Context::new_truncating(
(ctx.precision().get() * 2)
.max((bd.digits.bits() as f64 * LOG10_2 * 2.0).ceil() as u64)
.max(38) // always at least 2 u64 'big-digits' wide
);
let inverted_bd = bd.inverse_with_context(&inv_ctx);
// requested context when taking the power of inverted value
pow_u64_with_context(inverted_bd.to_ref(), unsigned_abs(exp), &ctx)
}
}
}
/// Calculate BigDecimalRef `bd` to power of positive integer, using context
/// to set precision
///
/// The case where exp==0 (therefore result is 1) must be handled
/// before calling this pow impl
fn pow_u64_with_context(
bd: BigDecimalRef,
exp: u64,
ctx: &Context,
) -> BigDecimal {
type R = RADIX_10p19_u64;
debug_assert_ne!(exp, 0);
if exp == 1 {
return ctx.round_decimal_ref(bd);
}
// bd^exp guaranteed to fit within precision: use roundless pow
if (bd.digits.bits() as f64 * exp as f64) < (ctx.precision().get() as f64 * LOG2_10) {
return pow_u64_no_context(bd, exp);
}
let mut tmp = Vec::new();
let bd_as_base10p19 = DigitVec::from_biguint_using_tmp(bd.digits, &mut tmp);
debug_assert_eq!(tmp.len(), 0);
let mut prec = RunningPrecision::new(
bd_as_base10p19.as_digit_slice(),
NonZeroU64::new(exp).unwrap(),
ctx.precision(),
);
// Count the number of multiplications we're going to perform, one per "1" binary digit
// in exp, and the number of times we can divide exp by 2.
let mut n = exp;
// factor product into bdⁿ => bdˣ·bdʸ where x is largest
// power of two which fits into 'n'
let final_x_pow = 1 << (63 - n.leading_zeros() as u64);
let final_y_pow = n - final_x_pow;
// if final_y_pow == 0, then 'y' digits is never used and
// we will use x*x as final multiplication, otherwise
// final product is x*y
let final_x_times_y = final_y_pow != 0;
let mut y_vec = Vec::new();
if final_x_times_y {
y_vec.reserve(bd_as_base10p19.len());
y_vec.push(1);
} else {
// no final multiplication by y, so decrease number of squarings
// by one so we do (x/2 * x/2) rather than (x * 1)
n >>= 1;
}
let mut digits_x = WithScale {
value: bd_as_base10p19,
scale: 0,
};
let mut digits_y = WithScale {
value: DigitVec::from_vec(y_vec),
scale: 0,
};
// temporary storage for results of multiplications
let mut prod = WithScale {
value: DigitVec::from_vec(tmp),
scale: 0,
};
// tracks if skipped insignificant digits are zero for final rounding
let mut trailing_zeros = true;
while n > 1 {
if n % 2 == 1 {
// 'prod' is now product y * x, swap with 'y'
let skipped_bigdigit_count = mul_scaled_slices_truncating_into(
&mut prod,
digits_y.as_digit_slice(),
digits_x.as_digit_slice(),
prec.next(),
);
trailing_zeros = trailing_zeros
&& {
let skipped = skipped_bigdigit_count.saturating_sub(digits_y.value.len() - 1);
digits_x.value.least_n_are_zero(skipped)
}
&& {
let skipped = skipped_bigdigit_count.saturating_sub(digits_x.value.len() - 1);
digits_y.value.least_n_are_zero(skipped)
};
// now 'digits_y <- prod = digits_y * digits_x'
stdlib::mem::swap(&mut prod, &mut digits_y);
}
// TODO: optimized algorithm for squaring a scaled digitslice to requested precision
let skipped_bigdigits = mul_scaled_slices_truncating_into(
&mut prod,
digits_x.as_digit_slice(),
digits_x.as_digit_slice(),
prec.next(),
);
// detect if truncated digits were non-zero
trailing_zeros = trailing_zeros
&& {
let skipped = skipped_bigdigits.saturating_sub(digits_x.value.len() - 1);
digits_x.value.least_n_are_zero(skipped)
};
// digits_x <- prod = digits_x * digits_x
stdlib::mem::swap(&mut prod, &mut digits_x);
// shift lowest bit out of multiplication counter
n >>= 1;
}
let sign = if exp % 2 == 0 {
Sign::Plus
} else {
bd.sign()
};
let rounding = crate::rounding::NonDigitRoundingData {
mode: ctx.rounding_mode(),
sign: sign,
};
prod.value.clear();
prod.scale = 0;
let x_slice = digits_x.value.as_digit_slice();
let y_slice = if final_x_times_y {
digits_y.value.as_digit_slice()
} else {
// raised to a power-of-two: y-slice was never touched so
// we reuse x-slice here for final multiplication
debug_assert_eq!(digits_y.value.digits.capacity(), 0);
digits_y.scale = digits_x.scale;
x_slice
};
multiply_slices_with_prec_into_p19_z(
&mut prod,
x_slice,
y_slice,
ctx.precision(),
rounding,
trailing_zeros,
);
let mut scale = bd.scale * exp as i64 + prod.scale;
scale += (digits_x.scale + digits_y.scale) * R::DIGITS as i64;
let int_val = BigInt::from_biguint(sign, prod.value.into_biguint());
BigDecimal::new(int_val, scale)
}
/// Simple implementation of exponentiation-by-squaring,
/// with no precision/rounding involved
fn pow_u64_no_context(bd: BigDecimalRef, exp: u64) -> BigDecimal {
debug_assert_ne!(exp, 0);
if exp == 1 {
return bd.to_owned();
}
let mut x = bd.digits.clone();
let mut y: BigUint = 1u8.into();
let mut n = exp;
while n > 1 {
if n % 2 == 1 {
y *= &x;
}
x = x.pow(2u8);
n >>= 1;
}
// final product
let p = x * y;
let sign = if exp % 2 == 0 {
Sign::Plus
} else {
bd.sign()
};
let scale = bd.scale * exp as i64;
let int_val = BigInt::from_biguint(sign, p);
BigDecimal::new(int_val, scale)
}
#[cfg(not(has_unsigned_abs))]
fn unsigned_abs(n: i64) -> u64 {
if n != i64::MIN {
n.abs() as u64
} else {
(i64::MIN as i128).abs() as u64
}
}
#[cfg(has_unsigned_abs)]
#[allow(clippy::incompatible_msrv)]
fn unsigned_abs(n: i64) -> u64 {
n.unsigned_abs()
}
/// Struct housing the 'margin' information for calculating the required
/// precision while doing sequential multiplications for pow
///
/// Currently uses a naive scheme: calculating the widest required
/// margin, and multiplying the number of multiplications by that width.
/// Then we linearly decrease the margin so we end up near the requested
/// precision by the time we get to the final product.
///
struct RunningPrecision {
/// Minimum precision
min: u64,
/// Current margin
margin: u64,
/// number of digits to decrease each time `next()` is called
margin_per_mul: u64,
}
impl RunningPrecision {
/// Create from requiring 'prec' digits of precision of digits^exp
fn new<'a, R: RadixPowerOfTen, E: Endianness>(
digits: DigitSlice<'a, R, E>,
exp: NonZeroU64,
prec: NonZeroU64,
) -> Self {
// number of big-digits required to fit requested precision, plus a few
// extra for guaranteed rounding digits
let prec_bigdigit_count = R::divceil_digit_count(prec.get() as usize + 3) as u64;
// length of 'digits' in big digits (floating-point)
let digit_count_f = digits.count_decimal_digits() as f64 ;
let count_squarings = 63 - exp.get().leading_zeros();
let count_non_squarings = exp.get().count_ones() - 1;
// total number of multiplications
let muls = (count_non_squarings + count_squarings) as u64;
// aⁿ => aˣ·aʸ, n = x+y
let max_partial_pow = {
let x = 1 << count_squarings;
let y = exp.get() - x;
(x / 2).max(y)
};
// number of digits of multiplicand in the final product
let max_width_digits_f = digit_count_f * max_partial_pow as f64;
// length in digits of kmaximum sum of digits
let diag_sum_digit_len = 2.0 * log10(R::max().to_f64().unwrap()) + log10(max_width_digits_f);
let diag_sum_bigdigit_len = R::divceil_digit_count(diag_sum_digit_len.ceil() as usize) as u64;
let margin_per_mul = diag_sum_bigdigit_len + 1;
Self {
min: prec_bigdigit_count,
margin: (muls + 1) * margin_per_mul,
margin_per_mul: margin_per_mul,
}
}
/// update margin and return precision
fn next(&mut self) -> u64 {
self.margin = self.margin.saturating_sub(self.margin_per_mul);
self.margin + self.min
}
}
#[cfg(test)]
mod test {
use super::*;
include!("pow.tests.rs");
}
@@ -0,0 +1,266 @@
use paste::paste;
#[allow(non_snake_case)]
mod powi_with_context {
use super::*;
macro_rules! impl_cases {
( pow= - $pow:literal : $( prec=$prec:literal, round=$($round:ident),+ => $expected:literal ; )+ ) => {
$( paste!{ impl_cases!([<pow_n$pow>]; -$pow; $($round),*; $prec; $expected); } )*
};
( pow=$pow:literal : $( prec=$prec:literal, round=$($round:ident),+ => $expected:literal ; )+ ) => {
$( paste!{ impl_cases!([<pow_$pow>]; $pow; $($round),*; $prec; $expected); } )*
};
($name:ident; $pow:literal; $round:ident; $prec:literal; $expected:literal) => {
paste!{
#[test]
fn [< $name _prec $prec _round_ $round >]() {
let n = test_input();
let prec = $prec;
let exp = $pow;
let ctx = Context::default()
.with_rounding_mode(RoundingMode::$round)
.with_prec(prec).unwrap();
let value = n.powi_with_context(exp, &ctx);
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(value, expected);
assert_eq!(value.scale, expected.scale);
}
}
};
($name:ident; $pow:literal; $($round:ident),+; $prec:literal; $expected:literal) => {
$( impl_cases!($name; $pow; $round; $prec; $expected); )*
};
}
mod case_999en3 {
use super::*;
fn test_input() -> BigDecimal {
"0.999".parse().unwrap()
}
impl_cases!(
pow=100 :
prec=1, round=Up => "1";
prec=1, round=Down => "0.9";
prec=2, round=Up => "0.91";
prec=2, round=Down => "0.90";
prec=10, round=Up => "0.9047921472";
);
impl_cases!(
pow=1001 :
prec=30, round=Up => "0.367327729346193080582179333082";
prec=38, round=Up => "0.36732772934619308058217933308124088263";
);
impl_cases!(
pow=30_000_000 :
prec=1, round=Up => "5E-13036";
prec=1, round=Down => "4E-13036";
prec=2, round=Down => "4.4E-13036";
prec=20, round=Up => "4.4338344072941502620E-13036";
);
}
mod case_1d5319977452724413736 {
use super::*;
fn test_input() -> BigDecimal {
"1.5319977452724413736".parse().unwrap()
}
impl_cases!(
pow=1:
prec=5, round=Up => "1.5320";
prec=5, round=Down => "1.5319";
);
impl_cases!(
pow=580:
prec=5, round=Up => "2.8166E+107";
prec=5, round=Down => "2.8165E+107";
);
impl_cases!(
pow=-580:
prec=5, round=Up => "3.5505E-108";
prec=5, round=Down => "3.5504E-108";
);
}
mod case_neg1040582726326750d5484 {
use super::*;
fn test_input() -> BigDecimal {
"-1040582726326750.5484".parse().unwrap()
}
impl_cases!(
pow=4:
prec=19, round=Up, Ceiling => "1.172482715963826257E+60";
prec=19, round=Down, HalfEven => "1.172482715963826256E+60";
);
impl_cases!(
pow=-51:
prec=19, round=Up, Floor => "-1.314900138431188004E-766";
prec=19, round=Down, Ceiling => "-1.314900138431188003E-766";
);
}
mod case_4d135846964 {
use super::*;
fn test_input() -> BigDecimal {
"4.135846964236207374549487108400332686443027446631549764347102855558635985583587468810645966176878292".parse().unwrap()
}
impl_cases!(
pow=52 :
prec=30, round=Up => "1.15173335866675718975392626426e32";
prec=52, round=Up => "115173335866675718975392626425044.8429283056873556580";
prec=100, round=Up => "115173335866675718975392626425044.8429283056873556579339935191839667843520503303080335324003379361749";
prec=100, round=Down => "115173335866675718975392626425044.8429283056873556579339935191839667843520503303080335324003379361748";
prec=220, round=Up => "115173335866675718975392626425044.8429283056873556579339935191839667843520503303080335324003379361748091953445157717419294900287105759027634531967229789408468617796286936526237706376914237296824445112814900146493371906508";
);
impl_cases!(
pow=400 :
prec=30, round=Down => "4.22458642351596686588868351991e246";
prec=50, round=Up => "4.2245864235159668658886835199155003081243046945638e246";
prec=100, round=Down => "4.224586423515966865888683519915500308124304694563763586647543454119991635745909994893495905909587954e246";
);
impl_cases!(
pow=527 :
prec=1, round=Up => "9e324";
prec=1, round=HalfUp => "9e324";
prec=15, round=Down => "8.50101303706824E+324";
prec=19, round=Down => "8.501013037068242474E+324";
prec=20, round=Down => "8.5010130370682424747E+324";
);
impl_cases!(
pow=550 :
prec=1, round=Up => "2e339";
prec=5, round=Down => "1.2895e339";
prec=15, round=Down => "1.28959480113192E+339";
);
}
mod case_23994 {
use super::*;
fn test_input() -> BigDecimal {
"23994".parse().unwrap()
}
impl_cases!(
pow=6 :
prec=1, round=Up => "2e26";
prec=6, round=Down => "1.90816E+26";
prec=6, round=HalfDown => "1.90817E+26";
prec=15, round=Down => "1.90816500635331E+26";
prec=30, round=Up => "190816500635331516320302656";
);
impl_cases!(
pow=20 :
prec=2, round=Down => "3.9E+87";
prec=2, round=HalfEven => "4.0E+87";
prec=6, round=Down => "3.99993E+87";
prec=15, round=Down => "3.99993644008739E+87";
prec=30, round=Down => "3.99993644008739657595647874758E+87";
);
}
}
#[cfg(not(feature = "std"))]
macro_rules! println {
( $( $x:expr ),* ) => {}
}
// Test that the 2 numbers are the same, assuming precision in ctx.
fn compare(bd: BigDecimal, bd_expected: BigDecimal, ctx: &Context) {
let bd_expected_round = bd_expected.with_precision_round(ctx.precision(), ctx.rounding_mode());
println!("100d 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
println!("exp {}", bd_expected);
println!("val {}", bd);
println!("exprd {}", bd_expected_round);
assert_eq!(bd, bd_expected_round);
}
macro_rules! impl_case {
($name:ident: $start:expr, $exp:literal => $expected:literal) => {
#[test]
fn $name() {
let start = BigDecimal::from($start);
let exp = $exp;
let expected = $expected;
let ctx = Context::default();
println!("Compute {}**{}", start, exp);
let bd = start.powi_with_context(exp, &ctx);
let bd_expected = BigDecimal::from_str(expected).unwrap();
compare(bd, bd_expected, &ctx);
}
};
}
mod pow_known {
use super::*;
// Wolfram Alpha can get us to these precise values with a bit of log trickery, e.g.:
// 2**3000000000 = 10**log_10(2**3000000000) = 10**(3000000000 * log_10(2))
impl_case!(case_2_3000: 2, 3000 => "1.230231922161117176931558813276752514640713895736833715766118029160058800614672948775360067838593459582429649254051804908512884180898236823e903");
impl_case!(case_2_2048: 2, 2048 => "3.231700607131100730071487668866995196044410266971548403213034542752465513886789089319720141152291346368871796092189801949411955915049092109e616");
impl_case!(case_2_2001: 2, 2001 => "2.296261390548509048465666402355363968044635404177390400955285473651532522784740627713318972633012539836891929277974925546894237921726110662e602");
impl_case!(case_2_3000000000: 2, 3000000000 => "9.8162042336235053508313854078782835648991393286913072670026492205522618203568834202759669215027003865712110468405800021098042607617495e903089986");
// This works as 2 can be exactly inverted with only 1 digit (0.5).
impl_case!(case_0_5_30000000: BigDecimal::from(2).inverse(), 30000000 => "1.34921314623699835510360889355448887159595110457423959780496317685705095413905406464421931122265203166201415504288117880522818881981650e-9030900");
impl_case!(case_0_5_minus3000000000: BigDecimal::from(2).inverse(), -3000000000 => "9.8162042336235053508313854078782835648991393286913072670026492205522618203568834202759669215027003865712110468405800021098042607617495e903089986");
impl_case!(case_2_minus30000000: 2, -30000000 => "1.34921314623699835510360889355448887159595110457423959780496317685705095413905406464421931122265203166201415504288117880522818881981650e-9030900");
// This tests that the inverse operation carries enough digits to keep the precision.
impl_case!(case_3_minus30000000: 3, -30000000 => "2.2824965348198962029744520058679746159742143842721452620663907608967745444344346503448190515521985159162206416095535917875712100566195e-14313638");
}
macro_rules! impl_case {
($name:ident: $start:expr, $exp:expr) => {
#[test]
fn $name() {
let start = BigDecimal::from_str($start).unwrap();
let exp = $exp.into();
let ctx = Context::new(NonZeroU64::new(50).unwrap(), RoundingMode::HalfEven);
let ctx_large = Context::new(NonZeroU64::new(500).unwrap(), RoundingMode::HalfEven);
println!("Compute {}**{}", start, exp);
let bd = start.powi_with_context(exp, &ctx);
let bd_expected = start.powi_with_context(exp, &ctx_large);
compare(bd, bd_expected, &ctx);
}
};
}
mod pow_misc {
use super::*;
// Test a few more misc values, checking that contexts with 50 or 500 precision
// get the same number, after scaling down the 500 precision result to 50.
impl_case!(case_misc_1: "-1.87421916986125215986", 3000912415i64);
impl_case!(case_misc_2: "230231922161117176931558813276752514640713895736833715766118029160058800614672948775360067838593459582429649254051804908512884180898236823e903", 1000151);
impl_case!(case_misc_3: "9.4215159218712961e-123", u32::MAX);
impl_case!(case_misc_4: "213", 3);
impl_case!(case_misc_5: "230231922161117176931558813276752514640713895736833715766118029160058800614672948775360067838593459582429649254051804908512884180898236823e903", -1000151);
impl_case!(case_misc_6: "9.4215159218712961e-123", i32::MIN);
// This test case fails without some extra margin (the number ends with 8.489 and gets rounded to 9 instead of 8)
impl_case!(case_misc_7: "-946773878.6364634037017822265625", 4294967295i64);
}
+36
View File
@@ -0,0 +1,36 @@
//! square root implementation
use crate::*;
pub(crate) fn impl_sqrt(n: &BigUint, scale: i64, ctx: &Context) -> BigDecimal {
// Calculate the number of digits and the difference compared to the scale
let num_digits = count_decimal_digits_uint(n);
let scale_diff = BigInt::from(num_digits) - scale;
// Calculate the number of wanted digits and the exponent we need to raise the original value to
// We want twice as many digits as the precision because sqrt halves the number of digits
// We add an extra one for rounding purposes
let prec = ctx.precision().get();
let extra_rounding_digit_count = 5;
let wanted_digits = 2 * (prec + extra_rounding_digit_count);
let exponent = wanted_digits.saturating_sub(num_digits) + u64::from(scale_diff.is_odd());
let sqrt_digits = (n * ten_to_the_uint(exponent)).sqrt();
// Calculate the scale of the result
let result_scale_digits = 2 * (2 * prec - scale_diff) - 1;
let result_scale_decimal: BigDecimal = BigDecimal::new(result_scale_digits, 0) / 4.0;
let mut result_scale = result_scale_decimal.with_scale_round(0, RoundingMode::HalfEven).int_val;
// Round the value so it has the correct precision requested
result_scale += count_decimal_digits_uint(&sqrt_digits).saturating_sub(prec);
let unrounded_result = BigDecimal::new(sqrt_digits.into(), result_scale.to_i64().unwrap());
unrounded_result.with_precision_round(ctx.precision(), ctx.rounding_mode())
}
#[cfg(test)]
mod test {
use super::*;
include!("sqrt.tests.rs");
}
@@ -0,0 +1,129 @@
macro_rules! impl_case {
($name:ident; $input:literal => $expected:literal) => {
#[test]
fn $name() {
let n: BigDecimal = $input.parse().unwrap();
let value = n.sqrt().unwrap();
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(value, expected);
assert_eq!(value.scale, expected.scale);
}
};
($name:ident; prec=$prec:literal; round=$round:ident; $input:literal => $expected:literal) => {
#[test]
fn $name() {
let ctx = Context::default()
.with_prec($prec).unwrap()
.with_rounding_mode(RoundingMode::$round);
let n: BigDecimal = $input.parse().unwrap();
let value = n.sqrt_with_context(&ctx).unwrap();
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(value, expected);
assert_eq!(value.scale, expected.scale);
}
};
}
impl_case!(case_0d000; "0.000" => "0.000");
impl_case!(case_1en232; "1e-232" => "1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-116");
impl_case!(case_1d00; "1.00" => "1.00");
impl_case!(case_1d001; "1.001" => "1.000499875062460964823258287700109753027590031219780479551442971840836093890879944856933288426795152");
impl_case!(case_100d0; "100" => "10.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
impl_case!(case_49; "49" => "7.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
impl_case!(case_d25; ".25" => ".5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
impl_case!(case_0d0152399025; "0.0152399025" => ".1234500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
impl_case!(case_0d00400; "0.00400" => "0.06324555320336758663997787088865437067439110278650433653715009705585188877278476442688496216758600590");
impl_case!(case_0d1; "0.1" => "0.3162277660168379331998893544432718533719555139325216826857504852792594438639238221344248108379300295");
impl_case!(case_152399025; "152399025" => "12345.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
impl_case!(case_2; "2" => "1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573");
impl_case!(case_125348; "125348" => "354.0451948551201563108487193176101314241016013304294520812832530590100407318465590778759640828114535");
impl_case!(case_121d000242000121; "121.000242000121000000" => "11.00001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
impl_case!(case_0d01234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901; "0.01234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901" => "0.1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
impl_case!(case_2e70; "2e70" => "141421356237309504880168872420969807.8569671875376948073176679737990732478462107038850387534327641573");
impl_case!(case_8d9793115997963468544185161590576171875en11; "8.9793115997963468544185161590576171875e-11" => "0.000009475922962855041517561783740144225422359796851494316346796373337470068631250135521161989831460407155");
impl_case!(case_18446744073709551616d1099511; "18446744073709551616.1099511" => "4294967296.000000000012799992691725492477397918722952224079252026972356303360555051219312462698703293");
impl_case!(case_3d1415926; "3.141592653589793115997963468544185161590576171875" => "1.772453850905515992751519103139248439290428205003682302442979619028063165921408635567477284443197875");
impl_case!(case_0d71777001; "0.7177700109762963922745342343167413624881759290454997218753321040760896053150388903350654937434826216803814031987652326749140535150336357405672040727695124057298138872112244784753994931999476811850580200000000000000000000000000000000" => "0.8472130847527653667042980517799020703921106560594525833177762276594388966885185567535692987624493813");
impl_case!(case_0d110889ddd444; "0.1108890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000444" => "0.3330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000667");
impl_case!(case_3e170; "3e170" => "17320508075688772935274463415058723669428052538103806280558069794519330169088000370811.46186757248576");
impl_case!(case_9e199; "9e199" => "9486832980505137995996680633298155601158665417975650480572514558377783315917714664032744325137900886");
impl_case!(case_7e200; "7e200" => "2645751311064590590501615753639260425710259183082450180368334459201068823230283627760392886474543611e1");
impl_case!(case_777e204; "777e204" => "2.787471972953270789531596912111625325974789615194854615319795902911796043681078997362635440358922503E+103");
impl_case!(case_777e600; "7e600" => "2.645751311064590590501615753639260425710259183082450180368334459201068823230283627760392886474543611E+300");
impl_case!(case_2e900; "2e900" => "1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573E+450");
impl_case!(case_7e999; "7e999" => "8.366600265340755479781720257851874893928153692986721998111915430804187725943170098308147119649515362E+499");
impl_case!(case_74908163946345982392040522594123773796e999; "74908163946345982392040522594123773796e999" => "2.736935584670307552030924971360722787091742391079630976117950955395149091570790266754718322365663909E+518");
impl_case!(case_20e1024; "20e1024" => "4.472135954999579392818347337462552470881236719223051448541794490821041851275609798828828816757564550E512");
impl_case!(case_3en1025; "3e-1025" => "5.477225575051661134569697828008021339527446949979832542268944497324932771227227338008584361638706258E-513");
impl_case!(case_3242053850483855en13_prec11_round_down; prec=11; round=Down; "324.2053850483855" => "18.005704236");
impl_case!(case_3242053850483855en13_prec11_round_up; prec=11; round=Up; "324.2053850483855" => "18.005704237");
impl_case!(case_3242053850483855en13_prec31_round_up; prec=31; round=Up; "324.2053850483855" => "18.00570423639090823994825477228");
impl_case!(case_5d085019992340351en10_prec25_round_down; prec=25; round=Down; "5.085019992340351e-10" => "0.00002254998889653906459324292");
impl_case!(case_3025d13579652399025_prec3_round_up; prec=3; round=Up; "3025.13579652399025" => "55.1");
impl_case!(case_3025d13579652399025_prec9_round_down; prec=9; round=Down; "3025.13579652399025" => "55.0012345");
impl_case!(case_3025d13579652399025_prec9_round_up; prec=9; round=Up; "3025.13579652399025" => "55.0012345");
impl_case!(case_3025d13579652399025_prec8_round_halfdown; prec=8; round=HalfDown; "3025.13579652399025" => "55.001234");
impl_case!(case_3025d13579652399025_prec8_round_halfeven; prec=8; round=HalfEven; "3025.13579652399025" => "55.001234");
impl_case!(case_3025d13579652399025_prec8_round_halfup; prec=8; round=HalfUp; "3025.13579652399025" => "55.001235");
#[test]
fn test_sqrt_rounding() {
let vals = vec![
// sqrt(1.21) = 1.1, [Ceiling, Up] should round up
("1.21", "2", "1", "1", "1", "1", "1", "2"),
// sqrt(2.25) = 1.5, [Ceiling, HalfEven, HalfUp, Up] should round up
("2.25", "2", "1", "1", "1", "2", "2", "2"),
// sqrt(6.25) = 2.5, [Ceiling, HalfUp, Up] should round up
("6.25", "3", "2", "2", "2", "2", "3", "3"),
// sqrt(8.41) = 2.9, [Ceiling, HalfDown, HalfEven, HalfUp, Up] should round up
("8.41", "3", "2", "2", "3", "3", "3", "3"),
];
for &(val, ceiling, down, floor, half_down, half_even, half_up, up) in vals.iter() {
let val = BigDecimal::from_str(val).unwrap();
let ceiling = BigDecimal::from_str(ceiling).unwrap();
let down = BigDecimal::from_str(down).unwrap();
let floor = BigDecimal::from_str(floor).unwrap();
let half_down = BigDecimal::from_str(half_down).unwrap();
let half_even = BigDecimal::from_str(half_even).unwrap();
let half_up = BigDecimal::from_str(half_up).unwrap();
let up = BigDecimal::from_str(up).unwrap();
let ctx = Context::default().with_prec(1).unwrap();
assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Ceiling)).unwrap(), ceiling);
assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Down)).unwrap(), down);
assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Floor)).unwrap(), floor);
assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::HalfDown)).unwrap(), half_down);
assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::HalfEven)).unwrap(), half_even);
assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::HalfUp)).unwrap(), half_up);
assert_eq!(val.sqrt_with_context(&ctx.with_rounding_mode(RoundingMode::Up)).unwrap(), up);
}
}
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::*;
use num_traits::FromPrimitive;
proptest! {
#[test]
fn sqrt_of_square_is_self(f: f64, prec in 15..50u64) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
let n = BigDecimal::from_f64(f.abs()).unwrap().with_prec(prec);
let n_squared = n.square();
let x = n_squared.sqrt().unwrap();
prop_assert_eq!(x, n);
}
}
}
@@ -0,0 +1,503 @@
//! Splitting and shifting bigdigits for alignment
//!
#![allow(dead_code)]
use crate::*;
use super::radix::RadixPowerOfTen;
use super::endian::LittleEndian;
use crate::arithmetic::diff_usize;
type BigDigitVec<R> = super::digitvec::DigitVec<R, LittleEndian>;
/// The cases resulting from applying (lhs += rhs * offset)
///
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum AddAssignSliceAlignment {
/// [rrrrrrrr] | [rrr] | [rrrrrrr]
/// [lllllll] | [lllllll] | [lllll]
RightOverlap {
count: usize,
},
/// [rrrrr]
/// [lllllll]
/// ^count ^0
RightDisjoint {
count: usize,
},
/// Left starts with the more significant digits
/// [rrrrrrr] | [rrrrrrr]
/// [llllllll] | [lll]
LeftOverlap {
count: usize,
},
/// Disjoint, left more significant
/// [rrrrrrr]
/// [lllll]
/// ^count ^0
LeftDisjoint {
count: usize,
},
}
impl AddAssignSliceAlignment {
/// Determine relative alignment of digit-arrays of given length
/// and scale (exponent of the least significant digit)
pub fn from_lengths_and_scales(lhs: WithScale<usize>, rhs: WithScale<usize>) -> Self {
use cmp::Ordering::*;
match diff_usize(rhs.scale, lhs.scale) {
(Equal, _) => {
Self::RightOverlap {
count: 0,
}
}
(Less, count) => {
if count < lhs.value {
Self::RightOverlap {
count,
}
} else {
Self::RightDisjoint {
count,
}
}
}
(Greater, count) => {
if count < rhs.value {
Self::LeftOverlap {
count,
}
} else {
Self::LeftDisjoint {
count,
}
}
}
}
}
/// Determine relative alignment of digit-arrays of given length
/// and number of integer bigdigits
pub fn from_lengths_and_icount(lhs: WithIntCount<usize>, rhs: WithIntCount<usize>) -> Self {
// position of least-significant digits
Self::from_lengths_and_scales(
WithScale {
scale: lhs.value as i64 - lhs.count as i64,
value: lhs.value,
},
WithScale {
scale: rhs.value as i64 - rhs.count as i64,
value: rhs.value,
},
)
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct BigDigitSplitterIter<R, I>
where
R: RadixPowerOfTen,
I: Iterator<Item = R::Base>,
{
shift: ShiftState<R>,
digits: I,
}
impl<R, I> BigDigitSplitterIter<R, I>
where
R: RadixPowerOfTen,
I: Iterator<Item = R::Base>,
{
/// Wrap iterator with on shifting
pub fn new(iter: I) -> Self {
Self::from_shifter_and_iter(ShiftState::Zero, iter)
}
/// Wrap iterator, shifting by shift-state
pub fn from_shifter_and_iter(shifter: ShiftState<R>, iter: I) -> Self {
Self {
shift: shifter,
digits: iter,
}
}
/// Return the internal 'mask' value
pub fn mask(&self) -> R::Base {
match self.shift {
ShiftState::Zero => Zero::zero(),
ShiftState::Shifted {
mask:
BigDigitSplitter {
mask,
..
},
..
} => mask,
}
}
/// Returns the value of the 'shift' with one subtracted
///
/// Useful for adding nines to first result of `from_slice_starting_bottom`
///
pub fn shift_minus_one(&self) -> R::Base {
match self {
BigDigitSplitterIter {
shift: ShiftState::Zero,
..
} => R::max(),
BigDigitSplitterIter {
shift:
ShiftState::Shifted {
mask,
..
},
..
} => mask.shift - One::one(),
}
}
/// Copy all remaining digits into destination vector
pub fn extend_vector(self, dest: &mut BigDigitVec<R>) {
if let Self {
shift: ShiftState::Zero,
digits,
..
} = self
{
dest.digits.extend(digits);
} else {
dest.digits.extend(self);
}
}
/// Copy all remaining digits into destination vector, adding carry
///
/// Note: carry is not zeroed after being pushed into the vector
///
pub(crate) fn extend_vector_adding_carry(
mut self,
mut carry: R::Base,
dest: &mut BigDigitVec<R>,
) {
while !carry.is_zero() {
if let Some(mut next_digit) = self.next() {
R::addassign_carry(&mut next_digit, &mut carry);
dest.push_significant_digit(next_digit);
} else {
dest.push_significant_digit(carry);
return;
}
}
self.extend_vector(dest)
}
/// Extend vector with digits in self, adding carry and subtracting borrow
pub(crate) fn extend_vector_with_carry_borrow(
mut self,
carry: &mut R::Base,
borrow: &mut R::Base,
dest: &mut BigDigitVec<R>,
) {
use num_traits::WrappingSub;
if borrow.is_zero() && carry.is_zero() {
return self.extend_vector(dest);
}
// the two cancel
if carry == borrow {
*borrow = Zero::zero();
*carry = Zero::zero();
return self.extend_vector(dest);
}
match self.next() {
Some(digit) => {
let d = R::sub_with_carry_borrow(digit, R::Base::zero(), carry, borrow);
dest.push_significant_digit(d);
// TODO: is there a cost to recursion?
self.extend_vector_with_carry_borrow(borrow, carry, dest);
}
None if carry == borrow => {}
None if carry > borrow => {
dest.push_significant_digit(carry.wrapping_sub(borrow));
*carry = Zero::zero();
*borrow = Zero::zero();
}
None => {
unreachable!("borrow underflow");
}
}
}
/// skip n digits
pub fn advance_by_n(&mut self, n: usize) {
// naive loop implementation
for _ in 0..n {
if self.next().is_none() {
break;
}
}
}
/// called when there's further digits to iterate
fn on_next_digit(&mut self, digit: R::Base) -> R::Base {
if let ShiftState::Shifted {
ref mut prev,
ref mask,
} = self.shift
{
let (hi, mut lo) = mask.split_and_shift(digit);
lo += *prev;
*prev = hi;
lo
} else {
digit
}
}
/// called when internal iterator is exhausted, returning final
/// shifted digits
fn on_last_digit(&mut self) -> Option<R::Base> {
match self.shift {
ShiftState::Shifted {
ref mut prev,
..
} if !prev.is_zero() => {
let result = *prev;
*prev = Zero::zero();
Some(result)
}
_ => None,
}
}
}
impl<R, I> Iterator for BigDigitSplitterIter<R, I>
where
R: RadixPowerOfTen,
I: Iterator<Item = R::Base>,
{
type Item = R::Base;
fn next(&mut self) -> Option<Self::Item> {
self.digits
.next()
.map(|digit| self.on_next_digit(digit))
.or_else(|| self.on_last_digit())
}
}
type CopiedDigitSlice<'a, R> =
stdlib::iter::Copied<stdlib::slice::Iter<'a, <R as super::radix::RadixType>::Base>>;
pub(crate) type BigDigitSliceSplitterIter<'a, R> = BigDigitSplitterIter<R, CopiedDigitSlice<'a, R>>;
impl<'a, R: RadixPowerOfTen> BigDigitSliceSplitterIter<'a, R> {
/// Return the complimentary size to 'n'
fn comp_n(n: usize) -> usize {
debug_assert!(n < R::DIGITS);
if n == 0 {
0
} else {
R::DIGITS - n
}
}
/// Build without shifting digits
pub fn from_slice(slice: &'a [R::Base]) -> Self {
Self {
shift: ShiftState::Zero,
digits: slice.iter().copied(),
}
}
/// Stream will behave as if adding n zeros to beginning of digits
///
/// ```ignore
/// [987654321, ...] : n=3 => [654321000, xxxxxx987, ...]
/// ```
///
pub(crate) fn from_slice_shifting_left(slice: &'a [R::Base], n: usize) -> Self {
Self::from_slice_starting_bottom(slice, Self::comp_n(n))
}
/// Stream will behave as if removing first n-digits from the slice
///
/// ```ignore
/// [987654321, ...] : n=3 => [xxx987654, ...]
/// ```
///
/// This is the second digit of `from_slice_starting_bottom`
///
pub(crate) fn from_slice_shifting_right(slice: &'a [R::Base], n: usize) -> Self {
Self::from_slice_starting_top(slice, Self::comp_n(n))
}
/// First digit will be the bottom n digits shifted to top
///
/// ```ignore
/// [987654321, ...] : n=3 => [321000000, xxx987654, ...]
/// ```
///
pub(crate) fn from_slice_starting_bottom(slice: &'a [R::Base], n: usize) -> Self {
Self::from_shifter_and_iter(ShiftState::starting_with_bottom(n), slice.iter().copied())
}
/// First digit will be the highest n-digits shifted to bottom
///
/// ```ignore
/// [987654321, ...] : n=3 => [xxxxxx987, ...]
/// ```
///
/// This is the second digit of `from_slice_shifting_left`
/// The bottom digits will be lost.
///
pub(crate) fn from_slice_starting_top(slice: &'a [R::Base], n: usize) -> Self {
let mut digits = slice.iter().copied();
let shifter = ShiftState::starting_with_top(n, &mut digits);
Self::from_shifter_and_iter(shifter, digits)
}
/// True if iterator has no more values
pub fn is_exhausted(&self) -> bool {
match self.shift {
ShiftState::Zero => self.digits.len() == 0,
_ => self.peek_next().is_none(),
}
}
/// Return the next bigdigit (if available) without advancing the internal value
pub fn peek_next(&self) -> Option<R::Base> {
self.clone().next()
}
}
/// Wrap shift-and-mask type with special-case for zero shift
///
#[derive(Copy, Clone, Debug)]
pub(crate) enum ShiftState<R: RadixPowerOfTen> {
Zero,
Shifted {
mask: BigDigitSplitter<R>,
prev: R::Base,
},
}
impl<R: RadixPowerOfTen> ShiftState<R> {
/// Start with the lower n digits of the first bigdigit, shifted up
///
/// ```ignore
/// Self::starting_with_top([987654321, ...], 3) => [32100000, xxx987654, ...]
/// ```
/// 987654321, 3 =>
///
fn starting_with_bottom(n: usize) -> Self {
if n == 0 {
Self::Zero
} else {
Self::Shifted {
mask: BigDigitSplitter::mask_low(n as u8),
prev: R::Base::zero(),
}
}
}
/// Start with high n digits of the first bigdigit
///
/// ```ignore
/// Self::starting_with_top([987654321, ...], 3) => [xxxxxx987, ...]
/// ```
///
fn starting_with_top<I: Iterator<Item = R::Base>>(n: usize, digits: &mut I) -> Self {
if n == 0 {
Self::Zero
} else {
let mask = BigDigitSplitter::mask_high(n);
let first_digit = digits.next().map(|d| d / mask.mask).unwrap_or(Zero::zero());
Self::Shifted {
mask: mask,
prev: first_digit,
}
}
}
}
/// Splits a bigdigit into pieces, used when aligning
#[derive(Copy, Clone, Debug)]
pub(crate) struct BigDigitSplitter<R: RadixPowerOfTen> {
/// dividend to split a bigdigit into high and low digits
mask: R::Base,
/// multiplication shifts high bigdigit to correct place after split
shift: R::Base,
}
impl<R: RadixPowerOfTen> BigDigitSplitter<R> {
/// Build such that (X % mask) 'keeps' n digits of X
pub fn mask_low(n: u8) -> Self {
use crate::arithmetic::ten_to_the_t;
debug_assert!((n as usize) < R::DIGITS);
let mask = ten_to_the_t(n);
let shift = ten_to_the_t(R::DIGITS as u8 - n);
Self {
shift,
mask,
}
}
/// Build such that (X / mask) 'keeps' n digits of X
pub fn mask_high(n: usize) -> Self {
Self::mask_low((R::DIGITS - n) as u8)
}
/// Split bigdigit into high and low digits
///
/// BigDigitSplitter::mask_high(3).split(987654321) => (987000000, 000654321)
/// BigDigitSplitter::mask_low(3).split(987654321) => (987654000, 000000321)
///
pub fn split(&self, n: R::Base) -> (R::Base, R::Base) {
let lo = n % self.mask;
(n - lo, lo)
}
/// Split and shift such that the n "high" bits are on low
/// side of digit and low bits are at the high end
///
/// BigDigitSplitter::mask_high(3).split_and_shift(987654321) => (000000987, 654321000)
/// BigDigitSplitter::mask_low(3).split_and_shift(987654321) => (000987654, 321000000)
///
pub fn split_and_shift(&self, n: R::Base) -> (R::Base, R::Base) {
let (hi, lo) = self.div_rem(n);
(hi, lo * self.shift)
}
pub fn div_rem(&self, n: R::Base) -> (R::Base, R::Base) {
n.div_rem(&self.mask)
}
pub fn div(&self, n: R::Base) -> R::Base {
n / self.mask
}
}
/// Wrap a container of bigidigts with a count of how
/// many are integer
///
/// i.e. The digits are aligned with zero
///
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct WithIntCount<T> {
/// number of big-digits in value to treat as integer
count: i32,
/// Slice of bigdigits
value: T,
}
#[cfg(test)]
#[allow(clippy::zero_prefixed_literal)]
mod test {
use super::*;
include!("alignment.tests.rs");
}
@@ -0,0 +1,191 @@
use bigdigit::radix::RADIX_10p9_u32;
use bigdigit::radix::RADIX_10p19_u64;
#[test]
fn split_987654321_low_3() {
let (hi, lo) = BigDigitSplitter::<RADIX_10p9_u32>::mask_low(3).split(987654321);
assert_eq!(hi, 987654000);
assert_eq!(lo, 000000321)
}
#[test]
fn split_987654321_high_3() {
let (hi, lo) = BigDigitSplitter::<RADIX_10p9_u32>::mask_high(3).split(987654321);
assert_eq!(hi, 987000000);
assert_eq!(lo, 000654321)
}
#[test]
fn split_and_shift_987654321_high_3() {
let (hi, lo) = BigDigitSplitter::<RADIX_10p9_u32>::mask_high(3).split_and_shift(987654321);
assert_eq!(hi, 000000987);
assert_eq!(lo, 654321000)
}
#[test]
fn split_and_shift_987654321_low_3() {
let (hi, lo) = BigDigitSplitter::<RADIX_10p9_u32>::mask_low(3).split_and_shift(987654321);
assert_eq!(hi, 000987654);
assert_eq!(lo, 321000000)
}
mod from_lengths_and_scales {
use super::*;
use paste::*;
macro_rules! impl_test {
(($x:literal, -$y:literal) += $($t:tt)*) => {
paste! { impl_test!([< case_ $x _n$y >]; ($x, -$y); $($t)*); }
};
(($x:literal, $y:literal) += $($t:tt)*) => {
paste! { impl_test!([< case_ $x _$y >]; ($x, $y); $($t)*); }
};
($name:ident; ($x:literal, $y:literal); ($a:literal, -$b:literal) => $e:expr ) => {
paste! { impl_test!([< $name _ $a _n $b >]; ($x, $y); ($a, -$b); $e); }
};
($name:ident; ($x:literal, $y:literal); ($a:literal, $b:literal) => $e:expr ) => {
paste! { impl_test!([< $name _ $a _ $b >]; ($x, $y); ($a, $b); $e); }
};
($name:ident; ($x:literal, $y:literal); ($a:literal, $b:literal); $expected:expr) => {
#[test]
fn $name() {
use self::AddAssignSliceAlignment::*;
let alignment = AddAssignSliceAlignment::from_lengths_and_scales(
WithScale { value: $x, scale: $y },
WithScale { value: $a, scale: $b },
);
assert_eq!(alignment, $expected);
}
};
}
// ll.llllllll
// r.rrrrrrr
impl_test!((10, 8) += (8, 7) => RightOverlap { count: 1 });
// lllllll.
// rrrrr0.
impl_test!((7, 0) += (5, -1) => RightOverlap { count: 1 });
// lll.
// rrrrrr.rrr
impl_test!((3, 0) += (9, 3) => LeftOverlap { count: 3 });
// lll.l
// .000000rrr
impl_test!((4, 1) += (3, 9) => LeftDisjoint { count: 8 });
// 0.00000llll
// rrrr.rr
impl_test!((4, 9) += (6, 2) => RightDisjoint{ count: 7 });
}
mod from_lengths_and_icount {
use super::*;
use paste::*;
macro_rules! impl_test {
(($x:literal, -$y:literal) += $($t:tt)*) => {
paste! {
impl_test!([< case_ $x _n$y >]; ($x, -$y); $($t)*);
}
};
(($x:literal, $y:literal) += $($t:tt)*) => {
paste! {
impl_test!([< case_ $x _$y >]; ($x, $y); $($t)*);
}
};
($name:ident; ($x:literal, $y:literal); ($a:literal, -$b:literal) => $e:expr ) => {
paste! {
impl_test!([< $name _ $a _n $b >]; ($x, $y); ($a, -$b); $e);
}
};
($name:ident; ($x:literal, $y:literal); ($a:literal, $b:literal) => $e:expr ) => {
paste! {
impl_test!([< $name _ $a _ $b >]; ($x, $y); ($a, $b); $e);
}
};
($name:ident; ($x:literal, $y:literal); ($a:literal, $b:literal); $expected:expr) => {
#[test]
fn $name() {
use self::AddAssignSliceAlignment::*;
let alignment = AddAssignSliceAlignment::from_lengths_and_icount(
WithIntCount { value: $x, count: $y },
WithIntCount { value: $a, count: $b },
);
assert_eq!(alignment, $expected);
}
};
}
// ll.llllllll
// r.rrrrrrr
impl_test!((10, 2) += (8, 1) => RightOverlap { count: 1 });
// ll.lllllll
// .rrrrrrr
impl_test!((9, 2) += (7, 0) => RightOverlap { count: 0 });
// ll.ll
// rrrrr.rr
impl_test!((4, 2) += (7, 5) => RightOverlap { count: 0 });
// lllll000.
// .00rrrrrrr
impl_test!((5, 8) += (7, -2) => LeftDisjoint { count: 12 });
// l0.
// .0rrrrrrr
impl_test!((1, 2) += (7, -1) => LeftDisjoint { count: 9 });
// ll.lllllllll
// .0rrrrrrr
impl_test!((11, 2) += (7, -1) => RightOverlap { count: 1 });
// ll.llllllll
// .0rrrrrrr
impl_test!((10, 2) += (7, -1) => RightOverlap { count: 0 });
// ll.lllllll
// .0rrrrrrr
impl_test!((9, 2) += (7, -1) => LeftOverlap { count: 1 });
// ll.ll
// .0rrrrrrr
impl_test!((4, 2) += (7, -1) => LeftOverlap { count: 6 });
// ll.l
// .0rrrrrrr
impl_test!((3, 2) += (7, -1) => LeftDisjoint { count: 7 });
// ll.
// .0rrrrrrr
impl_test!((2, 2) += (7, -1) => LeftDisjoint { count: 8 });
// rrrrr000.
// .00lllllll
impl_test!((7, -2) += (5, 8) => RightDisjoint { count: 12 });
// lllll.ll
// r.rrrr
impl_test!((7, 5) += (5, 1) => LeftOverlap { count: 2 });
// lllll.l
// r.rrrr
impl_test!((6, 5) += (5, 1) => LeftOverlap { count: 3 });
// lllll.
// r.rrrr
impl_test!((5, 5) += (5, 1) => LeftOverlap { count: 4 });
// llll0.
// r.rrrr
impl_test!((4, 5) += (5, 1) => LeftDisjoint { count: 5 });
// l.llll
// rrr0000.
impl_test!((5, 1) += (3, 7) => RightDisjoint { count: 8 });
}
+957
View File
@@ -0,0 +1,957 @@
//! Digit vectors (and slices) of arbitrary radix and endianness
use crate::*;
use stdlib::marker::PhantomData;
use stdlib::num::NonZeroU64;
use super::radix::*;
use super::endian::*;
use crate::rounding::NonDigitRoundingData;
/// Vector of integers, interpreted as bigdigits in an integer
///
/// Value of the integer is defined by the radix and endianness
/// type parameters.
///
#[derive(Clone, Default)]
pub(crate) struct DigitVec<R: RadixType, E: Endianness> {
pub digits: Vec<R::Base>,
_radix: PhantomData<R>,
_endian: PhantomData<E>,
}
#[allow(dead_code)]
impl<R: RadixType, E: Endianness> DigitVec<R, E> {
/// Create new vector
pub fn new() -> Self {
Self::from_vec(Vec::new())
}
/// Create new vector with capacity
pub fn with_capacity(n: usize) -> Self {
Self::from_vec(Vec::with_capacity(n))
}
/// construct from vector of digits
pub fn from_vec(v: Vec<R::Base>) -> Self {
debug_assert!(R::validate_digits(v.iter()), "{:?}", v);
Self {
digits: v,
_radix: PhantomData {},
_endian: PhantomData {},
}
}
/// allocate with n bigdigits and fill with zeros
pub fn from_zero_count(n: usize) -> Self {
Self::from_vec(vec![Zero::zero(); n])
}
/// Number of bigdigits in the vector
pub fn len(&self) -> usize {
self.digits.len()
}
/// Remove all bigdigits
pub fn clear(&mut self) {
self.digits.clear()
}
/// Resize inner vector, filling new values with zero
pub fn resize(&mut self, n: usize) {
self.digits.resize(n, Zero::zero())
}
/// Shrink inner vectory to new size
pub fn truncate(&mut self, n: usize) {
self.digits.truncate(n);
}
/// construct digitvec with u128
pub(crate) fn from_u128(n: u128) -> Self {
let mut v = Self::new();
v.fill_with_u128(n);
v
}
/// create vector of bigdigits from u128
pub(crate) fn fill_with_u128(&mut self, n: u128) {
E::fill_vec_with_u128::<R>(&mut self.digits, n);
}
/// Remove 'n' insignificant from the vector
///
/// BigEndian vectors truncate. LittleEndian vectors shift and truncate, and
/// remove significant zeros to reduce potential copy space
///
/// If 'n' is larger than the vector, the vector is cleared.
///
pub fn remove_insignificant_digits(&mut self, n: usize) {
if n == 0 {
return;
}
E::remove_insignificant_digits(&mut self.digits, n)
}
/// Borrow inner vectory as immutable digit-slice
pub fn as_digit_slice(&self) -> DigitSlice<'_, R, E> {
DigitSlice::from_slice(&self.digits[..])
}
/// Return slice skipping first n bigdigits
pub fn as_digit_slice_at(&self, n: usize) -> DigitSlice<'_, R, E> {
DigitSlice::from_slice(&self.digits[n..])
}
/// Borrow inner vectory as mutable digit-slice
pub fn as_digit_slice_mut(&mut self) -> DigitSliceMut<'_, R, E> {
DigitSliceMut::from_slice(&mut self.digits[..])
}
/// Split slice at 'pos' least-significant digits
pub fn split_le_at(
&self,
pos: usize
) -> (DigitSlice<'_, R, E>, DigitSlice<'_, R, E>) {
let (lo, hi) = E::split_least_significant(&self.digits[..], pos);
(DigitSlice::from_slice(lo), DigitSlice::from_slice(hi))
}
/// Split slice at 'pos' least-significant digits
pub fn split_le_at_mut(
&mut self,
pos: usize,
) -> (DigitSliceMut<'_, R, E>, DigitSliceMut<'_, R, E>) {
let (lo, hi) = E::split_least_significant_mut(&mut self.digits[..], pos);
(DigitSliceMut::from_slice(lo), DigitSliceMut::from_slice(hi))
}
/// Return the number of significant zeros in vector
pub fn count_significant_zeros(&self) -> usize {
E::count_significant_zeros(self.digits.as_slice())
}
/// true if empty or all zeros
pub fn is_all_zeros(&self) -> bool {
self.digits.iter().all(|&d| d.is_zero())
}
/// Return self, ignoring any significant zeros
pub fn least_n_are_zero(&self, n: usize) -> bool {
self.iter_le().take(n).all(Zero::is_zero)
}
/// Convert to inner vector
pub fn into_vec(self) -> Vec<R::Base> {
self.digits
}
/// Add bigdigit into this vector, starting from index of least significance
///
/// Any "overflow" is pushed to most significant end of the vector
///
pub fn add_value(&mut self, n: R::Base) {
self.add_value_at(0, n);
}
/// Add bigdigit into this vector (idexing from least-significant digit)
///
/// TODO: Should vector resize if index is larger than size of vector?
///
pub fn add_value_at(&mut self, idx: usize, n: R::Base) {
debug_assert!(idx <= self.digits.len());
if n.is_zero() {
return;
}
let overflow = self.as_digit_slice_mut().add_value_at(idx, n);
if !overflow.is_zero() {
self.push_significant_digit(overflow);
}
}
/// Add bigdigit to the significant end of the vec
pub fn push_significant_digit(&mut self, n: R::Base) {
E::push_significant_digit(&mut self.digits, n);
}
/// remove significant zeros
pub fn remove_leading_zeros(&mut self) {
E::strip_significant_zeros(&mut self.digits)
}
#[cfg(rustc_1_75)]
pub fn iter_le(&self) -> impl LeBigDigitIterator<'_, &R::Base> {
E::iter_slice(&self.digits[..])
}
#[cfg(not(rustc_1_75))]
pub fn iter_le(&self) -> LittleEndianBigDigitIter<'_, &R::Base> {
E::iter_slice(&self.digits[..])
}
#[cfg(rustc_1_75)]
pub fn iter_le_mut(&mut self) -> impl LeBigDigitIterator<'_, &mut R::Base> {
E::iter_slice_mut(&mut self.digits[..])
}
#[cfg(not(rustc_1_75))]
pub fn iter_le_mut(&mut self) -> LittleEndianBigDigitIter<'_, &mut R::Base> {
E::iter_slice_mut(&mut self.digits[..])
}
}
impl<R: RadixPowerOfTen, E: Endianness> DigitVec<R, E> {
pub fn count_decimal_digits(&self) -> usize {
self.as_digit_slice().count_decimal_digits()
}
}
#[allow(dead_code)]
impl<R: RadixType> DigitVec<R, LittleEndian> {
/// allocate with n bigdigits and fill with zeros
pub fn remove_significant_digits(&mut self) {
if let Some(idx) = self.digits.iter().rposition(|d| !d.is_zero()) {
self.digits.truncate(idx);
}
}
}
#[allow(dead_code)]
impl<R: RadixType> DigitVec<R, BigEndian> {
pub fn remove_significant_digits(&mut self) {
if let Some(idx) = self.digits.iter().position(|d| !d.is_zero()) {
self.digits.copy_within(idx.., 0);
self.digits.truncate(self.len() - idx);
}
}
}
#[allow(dead_code)]
impl<E: Endianness> DigitVec<RADIX_u64, E> {
/// multiply this vector by 'n'
fn mulassign_u64(&mut self, n: u64) {
let mut carry = 0u64;
for digit in self.iter_le_mut() {
RADIX_u64::mulassign_add_carry(digit, n, &mut carry);
}
if !carry.is_zero() {
E::push_significant_digit(&mut self.digits, carry);
}
}
}
#[allow(dead_code)]
impl DigitVec<RADIX_u64, LittleEndian> {
/// Convert to signed big integer
pub fn into_bigint(self, sign: Sign) -> BigInt {
BigInt::from_biguint(sign, self.into())
}
/// Construct vector from iterator of base-10^{19} bigdigits
#[allow(dead_code)]
pub fn from_10p19_digits<I: Iterator<Item = u64>>(mut digits: I) -> Self {
type R2p64 = RADIX_u64;
type R10p19 = RADIX_10p19_u64;
let mut v = vec![digits.next().unwrap_or(0)];
if let Some(d) = digits.next() {
let mut carry = 0;
R2p64::carrying_mul_add_inplace(
d, R10p19::RADIX as u64, &mut v[0], &mut carry
);
if carry != 0 {
v.push(carry);
}
}
let mut d = match digits.next() {
Some(d) => d,
None => {
return Self::from_vec(v);
}
};
let mut shifter = BigUint::from(R10p19::RADIX * R10p19::RADIX);
loop {
v.push(0);
if d != 0 {
let mut carry = 0;
let mut dest_digits = v.iter_mut();
let mut shifter_digits = shifter.iter_u64_digits();
loop {
match (dest_digits.next(), shifter_digits.next()) {
(Some(p), Some(s)) => {
R2p64::carrying_mul_add_inplace(d, s, p, &mut carry);
}
(None, Some(mut s)) => {
loop {
let (hi, lo) = R2p64::fused_mul_add(s, d, carry);
v.push(lo);
carry = hi;
s = match shifter_digits.next() {
None => break,
Some(x) => x,
};
}
break;
}
(Some(p), None) if carry != 0 => {
R2p64::addassign_carry(p, &mut carry);
}
_ => {
break;
}
}
}
if !carry.is_zero() {
v.push(carry);
}
}
d = match digits.next() {
Some(d) => d,
None => {
let zero_idx = v.iter().rposition(|&d| d != 0).unwrap_or(0);
v.truncate(zero_idx + 1);
return Self::from_vec(v);
}
};
shifter *= R10p19::RADIX as u64;
}
}
}
impl From<&num_bigint::BigUint> for DigitVec<RADIX_u64, LittleEndian> {
fn from(n: &num_bigint::BigUint) -> Self {
Self::from_vec(n.iter_u64_digits().collect())
}
}
impl From<DigitVec<RADIX_u64, LittleEndian>> for num_bigint::BigUint {
fn from(v: DigitVec<RADIX_u64, LittleEndian>) -> Self {
// TODO: Can we do this conversion in place?
let mut digits = Vec::with_capacity(v.len() * 2);
for d in v.digits.into_iter() {
digits.push(d as u32);
digits.push((d >> 32) as u32);
}
Self::new(digits)
}
}
impl From<&DigitVec<RADIX_u64, LittleEndian>> for num_bigint::BigUint {
fn from(v: &DigitVec<RADIX_u64, LittleEndian>) -> Self {
let mut digits = Vec::with_capacity(v.len() * 2);
for &d in v.digits.iter() {
digits.push(d as u32);
digits.push((d >> 32) as u32);
}
Self::new(digits)
}
}
impl DigitVec<RADIX_10p19_u64, LittleEndian> {
/// Convert a num biguint into DigitVec, using tmp as scratchpad
pub(crate) fn from_biguint_using_tmp(
n: &num_bigint::BigUint,
tmp: &mut Vec<u64>,
) -> Self {
tmp.clear();
tmp.extend(n.iter_u64_digits());
let result = Self::from_2p64le_vec(tmp);
// clear tmp so any residual digits are not accessible
tmp.clear();
result
}
/// remove the bottom 'n' digits in the vector, returning the highest
pub fn shift_n_digits_returning_high(&mut self, n: usize) -> u8 {
use bigdigit::alignment::BigDigitSplitter;
type Splitter = BigDigitSplitter<RADIX_10p19_u64>;
if n == 0 {
return 0;
}
let (bd_count, d_count) = n.div_rem(&19);
if d_count == 0 {
// insig is top digit on previous bigdigit
let ret = self.digits[bd_count - 1] / (RADIX_10p19_u64::RADIX as u64 / 10);
self.digits.copy_within(bd_count.., 0);
self.digits.truncate(self.len() - bd_count);
return ret as u8;
}
let mask = Splitter::mask_low(d_count as u8);
let (d0, insig) = mask.div_rem(self.digits[bd_count]);
let ret = mask.div(insig * 10) as u8;
let mut prev = d0;
let mut j = 0;
loop {
if let Some(&d) = self.digits.get(bd_count + 1 + j) {
let (hi, lo) = mask.split_and_shift(d);
self.digits[j] = lo + prev;
prev = hi;
j += 1;
} else {
if prev != 0 {
self.digits[j] = prev;
j += 1;
}
self.digits.truncate(j);
return ret;
}
}
}
/// Convert a base-2^64 DigitVec to 10^19 DigitVec
fn from_2p64le_vec(src: &mut Vec<u64>) -> Self {
type R = RADIX_10p19_u64;
let mut result: Vec<u64>;
match src.split_last() {
None => {
return Self::default();
}
Some((&top_digit, &[])) => {
let result = if top_digit < R::RADIX as u64 {
vec![top_digit]
} else {
let (hi, lo) = top_digit.div_rem(&(R::RADIX as u64));
vec![lo, hi]
};
return Self::from_vec(result);
}
Some((&top_digit, digits)) => {
let bit_count = (64 * digits.len()) + (64 - top_digit.leading_zeros() as usize);
let base2p64_bigdigit_count = (bit_count as f64) / (LOG2_10 * R::DIGITS as f64);
result = Vec::with_capacity(base2p64_bigdigit_count.ceil() as usize);
}
}
while let Some(pos) = src.iter().rposition(|&d| d != 0) {
src.truncate(pos + 1);
let rem: u64 = src.iter_mut().rev().fold(0, |acc, d| {
R::rotating_div_u64_radix(acc, d)
});
result.push(rem);
}
Self::from_vec(result)
}
/// Convert to a num BigInt with given sign
pub fn into_bigint(self, sign: Sign) -> BigInt {
let uint = self.into_biguint();
BigInt::from_biguint(sign, uint)
}
/// Convert to BigUint
pub fn into_biguint(self) -> BigUint {
use num_integer::div_rem;
let radix = <RADIX_10p19_u64 as RadixType>::RADIX;
let mut digits = self.digits.into_iter();
let d0 = digits.next().unwrap_or(0);
let mut result = BigUint::from(d0);
let n = match digits.next() {
None => {
return result;
}
Some(n) => n,
};
let mut scale = BigUint::from(radix);
result += n * &scale;
for digit in digits {
scale *= radix;
match digit {
0 => {}
1 => {
result += &scale;
}
n => {
result += n * &scale;
}
}
}
return result;
}
}
impl From<DigitVec<RADIX_u32, LittleEndian>> for num_bigint::BigUint {
fn from(v: DigitVec<RADIX_u32, LittleEndian>) -> Self {
Self::new(v.digits)
}
}
impl From<DigitVec<RADIX_10p19_u64, LittleEndian>> for num_bigint::BigUint {
fn from(v: DigitVec<RADIX_10p19_u64, LittleEndian>) -> Self {
type R = RADIX_10p19_u64;
let radix = R::RADIX as u64;
match v.digits.as_slice() {
&[] => {
Self::zero()
}
&[d] => {
Self::from(d)
}
&[d0, d1] => {
let mut result = Self::from(d1);
result *= radix;
result += d0;
return result;
}
_ => {
let mut shifter = Self::one();
let mut digits = v.digits.iter().rev();
let mut result: Self = digits.next().copied().unwrap().into();
for &d in digits {
shifter *= radix;
result *= &shifter;
result += d;
}
result
}
}
}
}
impl From<DigitSlice<'_, RADIX_u64, LittleEndian>> for DigitVec<RADIX_10p19_u64, LittleEndian> {
fn from(v: DigitSlice<'_, RADIX_u64, LittleEndian>) -> Self {
let mut src = Vec::from(v.digits);
Self::from_2p64le_vec(&mut src)
}
}
impl From<DigitVec<RADIX_10p19_u64, LittleEndian>> for DigitVec<RADIX_u64, LittleEndian> {
fn from(mut src: DigitVec<RADIX_10p19_u64, LittleEndian>) -> Self {
type R2p64 = RADIX_u64;
let radix = RADIX_10p19_u64::RADIX as u64;
match src.digits.len() {
0 | 1 => {
Self::from_vec(src.digits)
}
2 => {
let (hi, lo) = R2p64::expanding_mul(src.digits[1], radix);
let (sum, overflow) = src.digits[0].overflowing_add(lo);
src.digits[0] = sum;
src.digits[1] = hi + u64::from(overflow);
if src.digits[1] == 0 {
src.digits.truncate(1);
}
Self::from_vec(src.digits)
}
_ => {
let mut result = vec![0; src.len()];
result[0] = src.digits[0];
let mut scaler = BigInt::from(radix);
let mut base10_digits = src.digits.iter().skip(1);
let mut base10_digit = base10_digits.next().copied().unwrap_or(0);
loop {
for (i, base2_digit) in scaler.iter_u64_digits().enumerate() {
let (hi, lo) = R2p64::expanding_mul(base10_digit, base2_digit);
let (sum, overflow) = result[i].overflowing_add(lo);
result[i] = sum;
let mut j = i + 1;
let (sum, overflow) = result[j].overflowing_add(hi + u64::from(overflow));
result[j] = sum;
let mut carry = u64::from(overflow);
while carry != 0 {
j += 1;
let (sum, overflow) = result[j].overflowing_add(carry);
result[j] = sum;
carry = u64::from(overflow);
}
}
match base10_digits.next() {
None => break,
Some(&d) => base10_digit = d,
}
scaler *= radix;
}
Self::from_vec(result)
}
}
}
}
/// Convert BigUint to base-10 digits
impl From<&num_bigint::BigUint> for DigitVec<RADIX_10_u8, LittleEndian> {
fn from(n: &num_bigint::BigUint) -> Self {
Self::from_vec(n.to_radix_le(10))
}
}
#[cfg(test)]
mod test_from_biguint_using_tmp {
use super::*;
use crate::bigdigit::radix::RADIX_10p19_u64;
macro_rules! impl_case {
($name:ident: $input:literal => $result:expr) => {
#[test]
fn $name() {
let n: BigUint = $input.parse().unwrap();
let mut tmp = Vec::new();
let vec = DigitVec::from_biguint_using_tmp(&n, &mut tmp);
let expected: &[u64] = &$result;
assert_eq!(vec.digits.as_slice(), expected);
}
};
}
impl_case!(test_zero: "0" => []);
impl_case!(test_3888089293362626678: "3888089293362626678" => [3888089293362626678]);
impl_case!(test_10000000000000000000: "10000000000000000000" => [0, 1]);
impl_case!(test_141905914:
"1419059141115374799211309048234647259918822773497033524702964376392264024748829821875106774"
=> [
4748829821875106774,
2470296437639226402,
2599188227734970335,
4799211309048234647,
141905914111537,
]);
}
/// Vector of base-10 digits
#[allow(dead_code)]
impl DigitVec<RADIX_10_u8, LittleEndian> {
/// splits digits into `prec` significant digits, returning the lowest significant digit,
/// highest insignificant digit, and the remaining insignificant digits in little endian order
///
pub fn get_rounding_digits_at_prec(
&self,
prec: NonZeroU64,
) -> (u8, u8, DigitSlice<'_, RADIX_10_u8, LittleEndian>) {
let trimmed = self.digits.len().saturating_sub(prec.get() as usize);
if trimmed == 0 {
return (0, 0, DigitSlice::from_slice(&[]));
}
let (insig_digits, sig_digits) = self.digits.split_at(trimmed);
debug_assert_eq!(trimmed, insig_digits.len());
let (insig_digit, trailing_digits) = insig_digits.split_last().unwrap_or((&0, &[]));
(sig_digits[0], *insig_digit, DigitSlice::from_slice(trailing_digits))
}
/// Round the digits in this vec, returning slice of the digits
///
/// Note: this changes the value of 'self', and should be considered as
/// just a buffer of bytes after rounding in place.
///
pub fn round_at_prec_inplace(
&mut self,
prec: NonZeroU64,
rounding: NonDigitRoundingData,
) -> (DigitSlice<'_, RADIX_10_u8, LittleEndian>, usize) {
// count number of insignificant digits to remove
let mut trimmed = self.digits.len().saturating_sub(prec.get() as usize);
if trimmed == 0 {
return (DigitSlice::from_slice(&self.digits), 0);
}
let (insig_digits, sig_digits) = self.digits.split_at_mut(trimmed);
debug_assert_eq!(trimmed, insig_digits.len());
let (&insig_digit, insig_digits) = insig_digits.split_last().unwrap_or((&0, &[]));
let trailing_zeros = insig_digits.iter().all(|&d| d == 0);
let round = rounding.round_pair((sig_digits[0], insig_digit), trailing_zeros);
if round != 10 {
sig_digits[0] = round;
} else {
match sig_digits.iter().position(|&d| d != 9) {
Some(idx) => {
sig_digits[idx] += 1;
fill_slice_with_zero(&mut sig_digits[..idx]);
}
None => {
fill_slice_with_zero(sig_digits);
*sig_digits.last_mut().unwrap() = 1;
trimmed += 1;
}
}
}
debug_assert_eq!(prec.get() as usize, sig_digits.len());
return (DigitSlice::from_slice(sig_digits), trimmed);
}
}
#[cfg(rustc_1_50)]
#[allow(clippy::incompatible_msrv)]
#[allow(dead_code)]
fn fill_slice_with_zero<D: Zero + Clone>(s: &mut [D]) {
s.fill(Zero::zero());
}
#[cfg(not(rustc_1_50))]
#[allow(dead_code)]
fn fill_slice_with_zero<D: Zero + Clone>(s: &mut [D]) {
for r in s.iter_mut() {
*r = Zero::zero();
}
}
/// Immutable slice of digits
///
/// Operations on the bigdigit values are defined by the
/// radix and endianness traits.
///
#[derive(Clone, Copy)]
pub(crate) struct DigitSlice<'a, R: RadixType, E: Endianness> {
pub digits: &'a [R::Base],
_radix: PhantomData<R>,
_endian: PhantomData<E>,
}
#[allow(dead_code)]
impl<'a, R: RadixType, E: Endianness> DigitSlice<'a, R, E> {
/// Wrap slice of numbers as a slice of big-digits with given radix
/// and endianness
///
/// This does no validation, so the digits may be outside the bounds
/// of the radix and may have leading significant zeros.
///
pub fn from_slice(d: &'a [R::Base]) -> Self {
Self {
digits: d,
_radix: PhantomData {},
_endian: PhantomData {},
}
}
/// Wrap slice of numbers, ignoring significant zeros
pub fn from_sig_slice(d: &'a [R::Base]) -> Self {
let (nonzero, _) = E::split_significant_zeros(d);
Self::from_slice(nonzero)
}
/// Number of bigdigits in slice
pub fn len(&self) -> usize {
self.digits.len()
}
/// Return self, ignoring any significant zeros
pub fn without_leading_zeros(&self) -> Self {
let (digits, _) = E::split_significant_zeros(self.digits);
Self::from_slice(digits)
}
/// Split splice into 'pos' least-significant bigdigits, and remaining
pub fn split_le_at(&'a self, pos: usize) -> (Self, Self) {
let (lo, hi) = E::split_least_significant(self.digits, pos);
(Self::from_slice(lo), Self::from_slice(hi))
}
/// Return the number of significant zeros
pub fn count_significant_zeros(&self) -> usize {
E::count_significant_zeros(self.digits)
}
/// true if empty or all zeros
pub fn is_all_zeros(&self) -> bool {
self.digits.iter().all(|&d| d.is_zero())
}
/// Return self, ignoring any significant zeros
pub fn least_n_are_zero(&self, n: usize) -> bool {
self.iter_le().take(n).all(Zero::is_zero)
}
#[cfg(rustc_1_75)]
pub fn iter_le(&self) -> impl LeBigDigitIterator<'_, &R::Base> {
E::iter_slice(self.digits)
}
#[cfg(not(rustc_1_75))]
pub fn iter_le(&self) -> LittleEndianBigDigitIter<'_, &R::Base> {
E::iter_slice(self.digits)
}
}
#[allow(dead_code)]
impl<R: RadixType> DigitSlice<'_, R, LittleEndian> {
/// Return subslice of digits with the 'n' least significant bigdigits removed
pub fn trim_insignificant(&self, n: usize) -> Self {
Self::from_slice(&self.digits[n..])
}
pub fn find_least_significant_nonzero(&self) -> Option<usize> {
self.digits.iter().position(|&d| !d.is_zero())
}
}
#[allow(dead_code)]
impl<'a, E: Endianness> From<&'a DigitVec<RADIX_u64, E>> for DigitSlice<'a, RADIX_u64, E> {
fn from(v: &'a DigitVec<RADIX_u64, E>) -> Self {
v.as_digit_slice()
}
}
impl<'a, R: RadixPowerOfTen, E: Endianness> DigitSlice<'a, R, E> {
pub fn count_decimal_digits(&self) -> usize {
use crate::arithmetic::decimal::count_digits_u64;
let (top_digit, trailing) = E::split_most_significant_digit(self.digits);
R::DIGITS * trailing.len()
+ count_digits_u64(top_digit.to_u64().unwrap())
}
}
impl DigitSlice<'_, RADIX_10_u8, LittleEndian> {
/// fill digitvec with value contained in this digit-slice
#[allow(dead_code)]
pub fn fill_vec_u64(&self, dest: &mut DigitVec<RADIX_u64, LittleEndian>) {
let n = num_bigint::BigUint::from_radix_le(self.digits, 10).unwrap();
*dest = (&n).into();
}
}
/// Mutable slice of bigdigit values
pub(crate) struct DigitSliceMut<'a, R: RadixType, E: Endianness> {
pub digits: &'a mut [R::Base],
_radix: PhantomData<R>,
_endian: PhantomData<E>,
}
#[allow(dead_code)]
impl<'a, R: RadixType, E: Endianness> DigitSliceMut<'a, R, E> {
/// Construct from mutable slice of numbers
pub fn from_slice(v: &'a mut [R::Base]) -> Self {
Self {
digits: v,
_radix: PhantomData {},
_endian: PhantomData {},
}
}
/// Number of bigdigits in slice
pub fn len(&self) -> usize {
self.digits.len()
}
/// From digitvec, offset from the true index (independent of endianness)
pub fn from_vec_offset(v: &'a mut DigitVec<R, E>, offset: usize) -> Self {
Self::from_slice(&mut v.digits[offset..])
}
/// Cast to immutable slice
pub fn as_digit_slice(&'a self) -> DigitSlice<'a, R, E> {
DigitSlice::from_slice(self.digits)
}
/// Split, returning 'pos' little-endian
pub fn split_le_at(&'a self, pos: usize) -> (DigitSlice<'a, R, E>, DigitSlice<'a, R, E>) {
let (lo, hi) = E::split_least_significant(&self.digits[..], pos);
(DigitSlice::from_slice(lo), DigitSlice::from_slice(hi))
}
/// Split, returning 'pos' little-endian
pub fn split_le_at_mut(&'a mut self, pos: usize) -> (Self, Self) {
let (lo, hi) = E::split_least_significant_mut(self.digits, pos);
(Self::from_slice(lo), Self::from_slice(hi))
}
/// Add bigdigit 'n' into this slice, returning overflow
pub fn add_value_at(&mut self, idx: usize, mut n: R::Base) -> R::Base {
if n.is_zero() {
return n;
}
E::addassign_carry_into_slice_at::<R>(self.digits, &mut n, idx);
n
}
/// Add bigdigit into vector, storing overflow back in c
pub fn addassign_carry(&mut self, c: &mut R::Base) {
E::addassign_carry_into_slice_at::<R>(self.digits, c, 0);
}
#[cfg(rustc_1_75)]
pub fn iter_le(&self) -> impl LeBigDigitIterator<'_, &R::Base> {
E::iter_slice(self.digits)
}
#[cfg(not(rustc_1_75))]
pub fn iter_le(&self) -> LittleEndianBigDigitIter<'_, &R::Base> {
E::iter_slice(self.digits)
}
#[cfg(rustc_1_75)]
pub fn iter_le_mut(&mut self) -> impl LeBigDigitIterator<'_, &mut R::Base> {
E::iter_slice_mut(self.digits)
}
#[cfg(not(rustc_1_75))]
pub fn iter_le_mut(&mut self) -> LittleEndianBigDigitIter<'_, &mut R::Base> {
E::iter_slice_mut(self.digits)
}
}
#[allow(dead_code)]
impl<R: RadixPowerOfTen, E: Endianness> DigitSliceMut<'_, R, E> {
pub fn count_decimal_digits(&self) -> usize {
self.as_digit_slice().count_decimal_digits()
}
}
impl<'a, R: RadixType, E: Endianness> From<&'a mut Vec<R::Base>> for DigitSliceMut<'a, R, E> {
fn from(digits: &'a mut Vec<R::Base>) -> Self {
Self::from_slice(&mut digits[..])
}
}
impl<R: RadixType, E: Endianness> fmt::Debug for DigitVec<R, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DigitVec({}, {:?})", E::NAME, self.digits)
}
}
impl<R: RadixType, E: Endianness> fmt::Debug for DigitSlice<'_, R, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DigitSlice({}, {:?})", E::NAME, self.digits)
}
}
impl<R: RadixType, E: Endianness> fmt::Debug for DigitSliceMut<'_, R, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DigitSliceMut({}, {:?})", E::NAME, self.digits)
}
}
/// Add method to easily transform scaled DigitVec to scaled DigitSlice
impl<R: RadixType, E: Endianness> crate::WithScale<DigitVec<R, E>> {
pub fn as_digit_slice(&self) -> crate::WithScale<DigitSlice<'_, R, E>> {
WithScale {
scale: self.scale,
value: self.value.as_digit_slice(),
}
}
}
+461
View File
@@ -0,0 +1,461 @@
//! Structs and traits for generic operations on big or little endian ints
use crate::*;
use crate::stdlib;
use crate::stdlib::fmt;
use crate::stdlib::Vec;
#[cfg(not(rustc_1_75))]
use stdlib::Box;
use num_traits::{Zero, PrimInt};
use super::radix::RadixType;
/// Trait to allow generic parameterization of significant digit ordering
#[allow(dead_code)]
pub(crate) trait Endianness: Copy + Clone + Default + fmt::Debug {
/// Name to use for debugging
const NAME: &'static str;
/// Iterate over digits in vec from least to most significance
#[cfg(rustc_1_75)]
fn into_iter<'a, D: 'a>(digits: Vec<D>) -> impl LeBigDigitIterator<'a, D>;
/// Iterate in slice from least to most significance
#[cfg(rustc_1_75)]
fn iter_slice<D>(digits: &[D]) -> impl LeBigDigitIterator<'_, &D>;
/// Iterate over mut digits in slice from least to most significance
#[cfg(rustc_1_75)]
fn iter_slice_mut<D>(digits: &mut [D]) -> impl LeBigDigitIterator<'_, &mut D>;
#[cfg(not(rustc_1_75))]
fn into_iter<'a, D: 'a>(digits: Vec<D>) -> LittleEndianBigDigitIter<'a, D>;
#[cfg(not(rustc_1_75))]
fn iter_slice<D>(digits: &[D]) -> LittleEndianBigDigitIter<'_, &D>;
#[cfg(not(rustc_1_75))]
fn iter_slice_mut<D>(digits: &mut [D]) -> LittleEndianBigDigitIter<'_, &mut D>;
#[cfg(rustc_1_75)]
fn addassign_carry_into_slice_at<R: RadixType>(
digits: &mut [R::Base],
carry: &mut R::Base,
idx: usize,
) {
for dest in Self::iter_slice_mut(digits).skip(idx) {
R::addassign_carry(dest, carry);
if carry.is_zero() {
return;
}
}
}
#[cfg(not(rustc_1_75))]
fn addassign_carry_into_slice_at<R: RadixType>(
digits: &mut [R::Base],
carry: &mut R::Base,
idx: usize,
);
/// Place given digit at the most-significant end of the vecor
fn push_significant_digit<D>(digits: &mut Vec<D>, d: D);
/// Split slice into most-significant digit and 'the rest'
///
/// If slice is empty zero and empty-slice is returned
fn split_most_significant_digit<D: Zero + PrimInt>(digits: &[D]) -> (D, &[D]);
/// Split significant zeros from digits returning pair (digits, zeros)
fn split_significant_zeros<D: Zero>(digits: &[D]) -> (&[D], &[D]);
/// Split slice into 'count' low-significant digits, and remaining
/// significant digits
fn split_least_significant<D>(digits: &[D], count: usize) -> (&[D], &[D]);
/// Split mutable slice into 'count' low-significant digits, and
/// remaining significant digits
fn split_least_significant_mut<D>(digits: &mut [D], count: usize) -> (&mut [D], &mut [D]);
/// Remove any zeros at the location of highest significance, if all zeros
/// the vector will be cleared
fn strip_significant_zeros<D: Copy + Zero>(digits: &mut Vec<D>);
/// return number of consecutive zeros starting at significant
fn count_significant_zeros<D: Zero>(digits: &[D]) -> usize {
Self::iter_slice(digits).rev().position(|d| !d.is_zero()).unwrap_or(digits.len())
}
/// Remove 'n' insignificant digits from the vector, and MAY remove leading significant zeros
fn remove_insignificant_digits<D: Zero + Copy>(digits: &mut Vec<D>, n: usize);
/// Correctly order a slice of little endian digits
///
/// Reverses for BigEndian, no-op for LittleEndian
fn reorder_le_digits<D: Copy>(digits: &mut [D]);
/// Reorder vector of big-endian digits to this endianness
///
/// Removes trailing zeros
fn reorder_be_vec<D: Zero + Copy>(digits: &mut Vec<D>);
/// Consider digits in slice past 'idx' to be little-endian digits
/// of higher significance than those below; move them to correct
/// position in the slice
///
/// This will be a no-op for LittleEndian, and a rotate and reverse for BigEndian
///
fn rotate_trailing_le_digits_at<D: Copy>(digits: &mut [D], idx: usize);
/// Extract digits in correct order from bigiuint
fn bigint_to_digits(n: &num_bigint::BigUint) -> Vec<u8>;
/// Build BigUint from base-10 digits in slice
fn biguint_from_digits(n: &[u8]) -> Option<num_bigint::BigUint>;
/// Split u128 and store in vector
fn fill_vec_with_u128<R: RadixType>(vec: &mut Vec<R::Base>, n: u128);
}
/// Empty struct indicating most-significant bigdigit first
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct BigEndian {}
/// Empty struct indicating least-significant bigdigit first
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct LittleEndian {}
impl Endianness for BigEndian {
const NAME: &'static str = "BE";
#[cfg(rustc_1_75)]
fn into_iter<'a, D: 'a>(digits: Vec<D>) -> impl LeBigDigitIterator<'a, D> {
digits.into_iter().rev()
}
#[cfg(rustc_1_75)]
fn iter_slice<D>(digits: &[D]) -> impl LeBigDigitIterator<'_, &D> {
digits.iter().rev()
}
#[cfg(rustc_1_75)]
fn iter_slice_mut<D>(digits: &mut [D]) -> impl LeBigDigitIterator<'_, &mut D> {
digits.iter_mut().rev()
}
#[cfg(not(rustc_1_75))]
fn into_iter<'a, D: 'a>(digits: Vec<D>) -> LittleEndianBigDigitIter<'a, D> {
LittleEndianBigDigitIter {
digits: Box::new(digits.into_iter().rev()),
}
}
#[cfg(not(rustc_1_75))]
fn iter_slice<D>(digits: &[D]) -> LittleEndianBigDigitIter<'_, &D> {
LittleEndianBigDigitIter {
digits: Box::new(digits.iter().rev()),
}
}
#[cfg(not(rustc_1_75))]
fn iter_slice_mut<D>(digits: &mut [D]) -> LittleEndianBigDigitIter<'_, &mut D> {
LittleEndianBigDigitIter {
digits: Box::new(digits.iter_mut().rev()),
}
}
#[cfg(not(rustc_1_75))]
fn addassign_carry_into_slice_at<R: RadixType>(
digits: &mut [R::Base],
carry: &mut R::Base,
idx: usize,
) {
for dest in digits.iter_mut().rev().skip(idx) {
R::addassign_carry(dest, carry);
if carry.is_zero() {
return;
}
}
}
fn push_significant_digit<D>(digits: &mut Vec<D>, d: D) {
digits.insert(0, d);
}
fn split_least_significant<D>(digits: &[D], count: usize) -> (&[D], &[D]) {
let (hi, lo) = digits.split_at(digits.len() - count);
(lo, hi)
}
fn split_least_significant_mut<D>(digits: &mut [D], count: usize) -> (&mut [D], &mut [D]) {
let (hi, lo) = digits.split_at_mut(digits.len() - count);
(lo, hi)
}
fn split_most_significant_digit<D: Copy + Zero>(digits: &[D]) -> (D, &[D]) {
digits.split_first().map(|(&d, r)| (d, r)).unwrap_or((Zero::zero(), &[]))
}
fn strip_significant_zeros<D: Copy + Zero>(digits: &mut Vec<D>) {
if let Some(idx) = digits.iter().position(|d| !d.is_zero()) {
digits.copy_within(idx.., 0);
digits.truncate(digits.len() - idx);
} else {
digits.clear();
}
}
fn split_significant_zeros<D: Zero>(digits: &[D]) -> (&[D], &[D]) {
if let Some(idx) = digits.iter().position(|d| !d.is_zero()) {
let (sig_zeros, digits) = digits.split_at(idx);
(digits, sig_zeros)
} else {
(&[], digits)
}
}
fn remove_insignificant_digits<D: Zero + Copy>(digits: &mut Vec<D>, n: usize) {
// TODO: Does truncate need - 1?
digits.truncate(digits.len() - n);
}
fn reorder_le_digits<D: Copy>(digits: &mut [D]) {
digits.reverse()
}
fn reorder_be_vec<D: Zero + Copy>(digits: &mut Vec<D>) {
match digits.iter().position(|&d| !d.is_zero()) {
Some(0) => {}
Some(idx) => {
digits.copy_within(idx.., 0);
digits.truncate(digits.len() - idx);
}
None => digits.clear(),
}
}
fn rotate_trailing_le_digits_at<D: Copy>(digits: &mut [D], idx: usize) {
Self::reorder_le_digits(&mut digits[idx..]);
digits.rotate_left(idx);
}
fn bigint_to_digits(n: &num_bigint::BigUint) -> Vec<u8> {
n.to_radix_be(10)
}
fn biguint_from_digits(n: &[u8]) -> Option<num_bigint::BigUint> {
num_bigint::BigUint::from_radix_be(n, 10)
}
fn fill_vec_with_u128<R: RadixType>(vec: &mut Vec<R::Base>, n: u128) {
LittleEndian::fill_vec_with_u128::<R>(vec, n);
vec.reverse();
}
}
impl Endianness for LittleEndian {
const NAME: &'static str = "LE";
#[cfg(rustc_1_75)]
fn into_iter<'a, D: 'a>(digits: Vec<D>) -> impl LeBigDigitIterator<'a, D> {
digits.into_iter()
}
#[cfg(rustc_1_75)]
fn iter_slice<D>(digits: &[D]) -> impl LeBigDigitIterator<'_, &D> {
digits.iter()
}
#[cfg(rustc_1_75)]
fn iter_slice_mut<D>(digits: &mut [D]) -> impl LeBigDigitIterator<'_, &mut D> {
digits.iter_mut()
}
#[cfg(not(rustc_1_75))]
fn into_iter<'a, D: 'a>(digits: Vec<D>) -> LittleEndianBigDigitIter<'a, D> {
LittleEndianBigDigitIter {
digits: Box::new(digits.into_iter()),
}
}
#[cfg(not(rustc_1_75))]
fn iter_slice<D>(digits: &[D]) -> LittleEndianBigDigitIter<'_, &D> {
LittleEndianBigDigitIter {
digits: Box::new(digits.iter()),
}
}
#[cfg(not(rustc_1_75))]
fn iter_slice_mut<D>(digits: &mut [D]) -> LittleEndianBigDigitIter<'_, &mut D> {
LittleEndianBigDigitIter {
digits: Box::new(digits.into_iter()),
}
}
#[cfg(not(rustc_1_75))]
fn addassign_carry_into_slice_at<R: RadixType>(
digits: &mut [R::Base],
carry: &mut R::Base,
idx: usize,
) {
for dest in digits.iter_mut().skip(idx) {
R::addassign_carry(dest, carry);
if carry.is_zero() {
return;
}
}
}
fn push_significant_digit<D>(digits: &mut Vec<D>, d: D) {
digits.push(d);
}
fn split_least_significant<D>(digits: &[D], count: usize) -> (&[D], &[D]) {
digits.split_at(count)
}
fn split_least_significant_mut<D>(digits: &mut [D], count: usize) -> (&mut [D], &mut [D]) {
digits.split_at_mut(count)
}
fn split_most_significant_digit<D: Copy + Zero>(digits: &[D]) -> (D, &[D]) {
digits.split_last().map(|(&d, r)| (d, r)).unwrap_or((Zero::zero(), &[]))
}
fn strip_significant_zeros<D: Zero>(digits: &mut Vec<D>) {
if let Some(idx) = digits.iter().rposition(|d| !d.is_zero()) {
digits.truncate(idx + 1);
} else {
digits.clear();
}
}
fn split_significant_zeros<D: Zero>(digits: &[D]) -> (&[D], &[D]) {
if let Some(idx) = digits.iter().rposition(|d| !d.is_zero()) {
let (digits, sig_zeros) = digits.split_at(idx);
(digits, sig_zeros)
} else {
(&[], digits)
}
}
fn remove_insignificant_digits<D: Zero + Copy>(digits: &mut Vec<D>, n: usize) {
let last_nonzero_idx = digits.iter().rposition(|&d| !d.is_zero());
match (last_nonzero_idx, n > digits.len()) {
(Some(idx), false) => {
digits.copy_within(n..=idx, 0);
digits.truncate(idx - n + 1);
}
_ => {
digits.clear();
}
}
}
fn reorder_be_vec<D: Zero + Copy>(digits: &mut Vec<D>) {
match digits.iter().position(|&d| !d.is_zero()) {
None => digits.clear(),
Some(idx) => {
digits.copy_within(idx.., 0);
digits.truncate(digits.len() - idx);
digits.reverse();
}
}
}
#[allow(unused_variables)]
fn reorder_le_digits<D: Copy>(digits: &mut [D]) {
//no-op
}
#[allow(unused_variables)]
fn rotate_trailing_le_digits_at<D: Copy>(digits: &mut [D], idx: usize) {
//no-op
}
fn bigint_to_digits(n: &num_bigint::BigUint) -> Vec<u8> {
n.to_radix_le(10)
}
fn biguint_from_digits(n: &[u8]) -> Option<num_bigint::BigUint> {
num_bigint::BigUint::from_radix_le(n, 10)
}
fn fill_vec_with_u128<R: RadixType>(vec: &mut Vec<R::Base>, mut n: u128) {
let base = R::RADIX.to_u128().unwrap();
vec.clear();
while !n.is_zero() {
let (hi, lo) = num_integer::div_rem(n, base);
let lo = R::Base::from_u128(lo).unwrap();
vec.push(lo);
n = hi;
}
}
}
/// Abstraction over fixed-size little-endian bigdigit iterators
///
/// Can be applied to slice, vecs, and num_bigint::{U32Digits, U64Digits}
/// allowing "easy" access to the digits.
pub(crate) trait LeBigDigitIterator<'a, D>
: Iterator<Item = D>
+ ExactSizeIterator
+ DoubleEndedIterator
{
}
/// Thin wrapper around boxed big-digit-iterator trait object
///
/// Used to implement generic endian iterators for versions of Rust before
/// Return Position Impl Trait In Trait (RPITIT) were implemented.
///
#[cfg(not(rustc_1_75))]
pub(crate) struct LittleEndianBigDigitIter<'a, D> {
digits: Box<dyn LeBigDigitIterator<'a, D> + 'a>,
}
#[cfg(not(rustc_1_75))]
impl<D> Iterator for LittleEndianBigDigitIter<'_, D> {
type Item = D;
fn next(&mut self) -> Option<Self::Item> {
self.digits.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.digits.size_hint()
}
}
#[cfg(not(rustc_1_75))]
impl<D> DoubleEndedIterator for LittleEndianBigDigitIter<'_, D> {
fn next_back(&mut self) -> Option<Self::Item> {
self.digits.next_back()
}
}
#[cfg(not(rustc_1_75))]
impl<D> ExactSizeIterator for LittleEndianBigDigitIter<'_, D> {
fn len(&self) -> usize {
self.digits.len()
}
}
impl<'a> LeBigDigitIterator<'a, u64> for num_bigint::U64Digits<'a> {}
impl<'a> LeBigDigitIterator<'a, u32> for num_bigint::U32Digits<'a> {}
impl<'a, D> LeBigDigitIterator<'a, &'a D> for stdlib::slice::Iter<'a, D> {}
impl<'a, D> LeBigDigitIterator<'a, &'a D> for stdlib::iter::Rev<stdlib::slice::Iter<'a, D>> {}
impl<'a, D> LeBigDigitIterator<'a, &'a mut D> for stdlib::slice::IterMut<'a, D> {}
impl<'a, D> LeBigDigitIterator<'a, &'a mut D> for stdlib::iter::Rev<stdlib::slice::IterMut<'a, D>> {}
impl<D> LeBigDigitIterator<'_, D> for stdlib::vec::IntoIter<D> {}
impl<D> LeBigDigitIterator<'_, D> for stdlib::iter::Rev<stdlib::vec::IntoIter<D>> {}
+15
View File
@@ -0,0 +1,15 @@
//! BigDigit types and routines
//!
//! Constants and structures defining the "BigDigit" components of BigDecimal.
//!
//! BigDigit is generic over Radix types, allowing the properties of
//! a bigdigit to be specified in the radix, and the the overall behavior
//! of the bigdigit (i.e. add_with_carry) as methods on the BigDigit.
//!
//! NOTE: These are distinct from the BigDigits in num_bigint.
//!
pub mod alignment;
pub mod digitvec;
pub mod endian;
pub mod radix;
+407
View File
@@ -0,0 +1,407 @@
//! Radix definitions
//!
//! Empty structs used to make generic algorithms over kind of radix
//!
#![allow(dead_code)]
#![allow(non_camel_case_types)]
use crate::*;
use crate::stdlib::fmt;
use num_traits::{WrappingAdd, WrappingSub, AsPrimitive};
/// All the information needed to specify a bigdecimal's radix, and methods operating on integers
pub trait RadixType: Copy + Clone + Default + fmt::Debug {
/// the inner type of values
type Base
: 'static
+ Copy
+ fmt::Debug
+ num_integer::Integer
+ num_traits::PrimInt
+ num_traits::FromPrimitive
+ num_traits::AsPrimitive<Self::BaseDouble>
+ num_traits::Zero
+ num_traits::One
+ num_traits::WrappingSub
+ num_traits::Pow<u8, Output = Self::Base>
+ AddAssign
+ From<bool>
+ From<u8>;
/// double wide unsigned type (capable of storing product of two BigDigits)
type BaseDouble
: 'static
+ Copy
+ num_integer::Integer
+ num_traits::PrimInt
+ num_traits::FromPrimitive
+ num_traits::AsPrimitive<Self::Base>
+ num_traits::Zero
+ num_traits::One
+ num_traits::WrappingAdd
+ num_traits::WrappingSub
+ AddAssign
+ From<u8>
+ From<Self::Base>;
/// signed version of base (used for diffs)
type SignedBase
: 'static
+ Copy
+ num_integer::Integer
+ num_traits::PrimInt
+ num_traits::FromPrimitive
+ num_traits::Zero
+ num_traits::One
+ num_traits::Signed;
/// Value of the RADIX
const RADIX: Self::BaseDouble;
/// Check contents of iterable contains values less than the radix
fn validate_digits<'a, I: IntoIterator<Item = &'a Self::Base>>(i: I) -> bool {
i.into_iter().map(|&d| d.into()).all(|d| Self::BaseDouble::zero() <= d && d < Self::RADIX)
}
/// True if n is the maximum value for this radix (RADIX - 1)
fn max() -> Self::Base {
(Self::RADIX - One::one()).as_()
}
/// True if n is the maximum value for this radix (RADIX - 1)
fn is_max(n: Self::Base) -> bool {
n == Self::max()
}
/// Split single-width BigDigit base-type value into valid BigDigits for
/// this Radix
fn split_single(n: Self::Base) -> (Self::Base, Self::Base) {
let (hi, lo) = num_integer::div_rem(n.as_(), Self::RADIX);
return (hi.as_(), lo.as_());
}
/// Split double-wide value into valid BigDigit and overflow for this Radix
fn split_doublewide(n: Self::BaseDouble) -> (Self::BaseDouble, Self::Base) {
let (hi, lo) = num_integer::div_rem(n, Self::RADIX);
return (hi, lo.as_());
}
/// Split double-wide bigdigit into high and low bigdigits.
///
/// This assumes n will fit in two bigdigits, which is not guaranteed.
/// Use split_doublewide.
///
fn split_wide_digit(n: Self::BaseDouble) -> (Self::Base, Self::Base) {
let (hi, lo) = num_integer::div_rem(n, Self::RADIX);
debug_assert!(hi < Self::RADIX);
return (hi.as_(), lo.as_());
}
/// Perform n + carry, returning sum and storing overflow back in carry
fn add_carry(n: Self::Base, carry: &mut Self::Base) -> Self::Base {
let (hi, lo) = Self::expanding_add(n, *carry);
*carry = hi;
lo
}
/// Perform n += carry, returning overflow in carry
fn addassign_carry(n: &mut Self::Base, carry: &mut Self::Base) {
let (hi, lo) = Self::expanding_add(*n, *carry);
*carry = hi;
*n = lo;
}
/// Perform n += a + carry, returning overflow in carry
fn addassign_with_carry(n: &mut Self::Base, a: Self::Base, carry: &mut Self::Base) {
let sum = n.as_() + a.as_() + carry.as_();
let (hi, lo) = Self::split_wide_digit(sum);
*carry = hi;
*n = lo;
}
/// Perform a + b, returning tuple of (high, low) digits
fn add_expand_doublewide(a: Self::Base, b: Self::BaseDouble) -> (Self::Base, Self::Base) {
let a: Self::BaseDouble = a.into();
Self::split_wide_digit(a + b)
}
/// Perform a + b, returning tuple of (high, low) digits
fn expanding_add(a: Self::Base, b: Self::Base) -> (Self::Base, Self::Base) {
let a: Self::BaseDouble = a.into();
let b: Self::BaseDouble = b.into();
Self::split_wide_digit(a + b)
}
/// Perform a * b, returning tuple of (high, low) digits
fn expanding_mul(a: Self::Base, b: Self::Base) -> (Self::Base, Self::Base) {
let a: Self::BaseDouble = a.into();
let b: Self::BaseDouble = b.into();
Self::split_wide_digit(a * b)
}
/// Perform a * b + c, returning tuple of (high, low) digits
fn fused_mul_add(a: Self::Base, b: Self::Base, c: Self::Base) -> (Self::Base, Self::Base) {
let a: Self::BaseDouble = a.into();
let b: Self::BaseDouble = b.into();
let c: Self::BaseDouble = c.into();
Self::split_wide_digit(a * b + c)
}
/// Perform a * b + c + d, returning tuple of (high, low) digits
fn carrying_mul_add(
a: Self::Base,
b: Self::Base,
c: Self::Base,
d: Self::Base,
) -> (Self::Base, Self::Base) {
let a: Self::BaseDouble = a.into();
let b: Self::BaseDouble = b.into();
let c: Self::BaseDouble = c.into();
let d: Self::BaseDouble = d.into();
Self::split_wide_digit(a * b + c + d)
}
/// Perform c += a * b + carry, returning overflow in carry
fn carrying_mul_add_inplace(
a: Self::Base,
b: Self::Base,
c: &mut Self::Base,
carry: &mut Self::Base,
) {
let a: Self::BaseDouble = a.into();
let b: Self::BaseDouble = b.into();
let (hi, lo) = Self::split_wide_digit(a * b + (*c).into() + (*carry).into());
*c = lo;
*carry = hi;
}
/// Add c into little-endian slice of digits, storing overflow in c
///
/// Starts adding at index 0.
///
fn add_carry_into_slice(dest: &mut [Self::Base], c: &mut Self::Base) {
Self::add_carry_into(dest.iter_mut(), c)
}
/// Iterate over digits in 'dest', adding the carry value until it becomes zero.
///
/// If iterator runs out of digits while carry has a value (i.e. the sum overflows),
/// the carry value will not be zero.
///
fn add_carry_into<'a, I: Iterator<Item=&'a mut Self::Base>>(dest: I, c: &mut Self::Base) {
for d in dest {
if c.is_zero() {
return;
}
let (overflow, sum) = Self::expanding_add(*c, *d);
*d = sum;
*c = overflow;
}
}
/// a = a * b + c, storing
fn mulassign_add_carry(
a: &mut Self::Base,
b: Self::Base,
carry: &mut Self::Base,
) {
let (hi, lo) = Self::expanding_mul(*a, b);
*a = Self::add_carry(lo, carry);
*carry += hi;
}
/// return value of (lhs - rhs + carry - borrow)
fn sub_with_carry_borrow(
lhs: Self::Base,
rhs: Self::Base,
carry: &mut Self::Base,
borrow: &mut Self::Base,
) -> Self::Base {
let mut result = Self::BaseDouble::from(lhs);
result = result
.wrapping_sub(&Self::BaseDouble::from(rhs))
.wrapping_add(&(*carry).as_())
.wrapping_sub(&(*borrow).as_());
*borrow = Self::Base::from(result >= (Self::RADIX << 1));
result = result.wrapping_add(&(borrow.as_() * Self::RADIX));
debug_assert!(result < (Self::RADIX << 1));
*carry = Self::Base::from(result >= Self::RADIX);
result = result - carry.as_() * Self::RADIX;
return result.as_();
}
}
/// Radix=*10* / storage=*u8*
#[derive(Copy, Clone, Debug, Default)]
pub struct RADIX_10_u8;
/// Radix=*10,000* storage=*i16*
#[derive(Copy, Clone, Debug, Default)]
pub struct RADIX_10p4_i16;
/// Radix=*1,000,000,000* storage=*u32*
#[derive(Copy, Clone, Debug, Default)]
pub struct RADIX_10p9_u32;
/// Radix=*10,000,000,000,000,000,000* storage=*u64*
#[derive(Copy, Clone, Debug, Default)]
pub struct RADIX_10p19_u64;
/// Radix = 2^<sup>32</sup>
#[derive(Copy, Clone, Debug, Default)]
pub struct RADIX_u32;
/// Radix = 2^<sup>64</sup>
#[derive(Copy, Clone, Debug, Default)]
pub struct RADIX_u64;
pub(crate) trait RadixPowerOfTen: RadixType {
const DIGITS: usize;
/// convert number of digits to number of big-digits
fn divceil_digit_count(digit_count: usize) -> usize {
use num_integer::Integer;
Integer::div_ceil(&digit_count, &Self::DIGITS)
}
/// convert number of digits to number of big-digits
fn divmod_digit_count(digit_count: usize) -> (usize, usize) {
use num_integer::Integer;
Integer::div_rem(&digit_count, &Self::DIGITS)
}
}
impl RadixPowerOfTen for RADIX_10p19_u64 {
const DIGITS: usize = 19;
}
impl RadixPowerOfTen for RADIX_10p9_u32 {
const DIGITS: usize = 9;
}
impl RadixPowerOfTen for RADIX_10_u8 {
const DIGITS: usize = 1;
}
impl RadixPowerOfTen for RADIX_10p4_i16 {
const DIGITS: usize = 4;
}
impl RADIX_10p19_u64 {
/// Divide double-wide u64 by radix, storing the quotient in the low u64,
/// and returning the remainder
pub(crate) fn rotating_div_u64_radix(hi: u64, lo: &mut u64) -> <Self as RadixType>::Base {
use num_integer::div_rem;
type BaseDouble = <RADIX_10p19_u64 as RadixType>::BaseDouble;
let num = BaseDouble::from(*lo) + (BaseDouble::from(hi) << 64);
let (q, r) = div_rem(num, Self::RADIX);
*lo = q.as_();
r.as_()
}
}
#[cfg(test)]
mod test_validate {
use super::*;
macro_rules! impl_case {
(valid $name:ident : $radix:ident ~ $values:expr) => {
#[test]
fn $name() {
assert!($radix::validate_digits($values.iter()));
}
};
(invalid $name:ident : $radix:ident ~ $values:expr) => {
#[test]
fn $name() {
assert!(!$radix::validate_digits($values.iter()));
}
};
}
impl_case!(valid case_valid: RADIX_10p4_i16 ~ [1, 2, 3, 4, 5, 600]);
impl_case!(invalid case_invalid_too_big: RADIX_10p4_i16 ~ [10000, 1, 2, 3, 4, 5, 600]);
impl_case!(invalid case_invalid_negative: RADIX_10p4_i16 ~ [1, 2, -3, 4, 5, 600]);
impl_case!(invalid case_invalid_leading_zeros: RADIX_10p4_i16 ~ [0, 1, 2, -3, 4, 5, 600]);
impl_case!(invalid case_p9_toobig: RADIX_10p9_u32 ~ [3330199352]);
}
impl RadixType for RADIX_u32 {
type Base = u32;
type BaseDouble = u64;
type SignedBase = i32;
const RADIX: Self::BaseDouble = 1u64 << 32;
// all u32 are valid in this radix
fn validate_digits<'a, I: IntoIterator<Item = &'a Self::Base>>(_: I) -> bool {
true
}
fn expanding_add(a: u32, b: u32) -> (u32, u32) {
let (sum, overflow) = a.overflowing_add(b);
(u32::from(overflow), sum)
}
}
impl RadixType for RADIX_u64 {
type Base = u64;
type BaseDouble = u128;
type SignedBase = i64;
const RADIX: Self::BaseDouble = 1u128 << 64;
// all u64 are valid in this radix
fn validate_digits<'a, I: IntoIterator<Item = &'a Self::Base>>(_: I) -> bool {
true
}
fn expanding_add(a: u64, b: u64) -> (u64, u64) {
let (sum, overflow) = a.overflowing_add(b);
(u64::from(overflow), sum)
}
}
impl RadixType for RADIX_10p4_i16 {
type Base = i16;
type BaseDouble = i32;
type SignedBase = i64;
const RADIX: Self::BaseDouble = 10_000;
}
impl RadixType for RADIX_10p9_u32 {
type Base = u32;
type BaseDouble = u64;
type SignedBase = i32;
const RADIX: Self::BaseDouble = 1_000_000_000;
}
impl RadixType for RADIX_10p19_u64 {
type Base = u64;
type BaseDouble = u128;
type SignedBase = i64;
const RADIX: Self::BaseDouble = 10_000_000_000_000_000_000;
}
impl RadixType for RADIX_10_u8 {
type Base = u8;
type BaseDouble = u8;
type SignedBase = i8;
const RADIX: Self::BaseDouble = 10;
}
#[cfg(test)]
mod tests {
use super::*;
include!("radix.tests.rs");
}
@@ -0,0 +1,82 @@
mod radix_10p19_u64 {
use super::*;
use super::RADIX_10p19_u64 as Radix;
#[test]
fn split_wide_digit_10e19_0() {
let (hi, lo) = Radix::split_wide_digit(0);
assert_eq!(hi, 0);
assert_eq!(lo, 0);
}
#[test]
fn split_wide_digit_10e19_sqrd() {
let (hi, lo) = Radix::split_wide_digit(99999999999999999980000000000000000001);
assert_eq!(hi, 9999999999999999998);
assert_eq!(lo, 1);
}
#[test]
fn split_wide_digit_5060270152244608514849739578370464703() {
let (hi, lo) = Radix::split_wide_digit(5060270152244608514849739578370464703);
assert_eq!(hi, 506027015224460851);
assert_eq!(lo, 4849739578370464703);
}
#[test]
fn add_with_carry() {
let mut carry = 20;
let sum = Radix::add_carry(222292843123382, &mut carry);
assert_eq!(sum, 222292843123402);
assert_eq!(carry, 0);
}
#[test]
fn add_with_carry_overflow_18446744073709551600_55() {
let mut carry = 55;
let sum = Radix::add_carry(18446744073709551600, &mut carry);
assert_eq!(sum, 8446744073709551655);
assert_eq!(carry, 1);
}
#[test]
fn add_with_carry_overflow_2() {
let result = &mut [0, 1, 2];
let mut carry = 40;
Radix::add_carry_into_slice(result, &mut carry);
assert_eq!(result, &[40, 1, 2]);
assert_eq!(carry, 0);
}
}
mod radix_u64 {
use super::*;
use super::RADIX_u64 as Radix;
#[test]
fn add_with_carry_overflow_1() {
let mut carry = 55;
let sum = Radix::add_carry(18446744073709551600, &mut carry);
assert_eq!(sum, 39);
assert_eq!(carry, 1);
}
#[test]
fn add_with_carry_overflow_2() {
let result = &mut 0;
let mut carry = 40;
Radix::addassign_carry(result, &mut carry);
assert_eq!(*result, 40);
assert_eq!(carry, 0);
}
#[test]
fn add_with_carry_overflow_3() {
let mut result = 3533561901698977160;
let mut carry = 17004058994074047095;
Radix::addassign_carry(&mut result, &mut carry);
assert_eq!(result, 2090876822063472639);
assert_eq!(carry, 1);
}
}
+307
View File
@@ -0,0 +1,307 @@
//! Define arithmetical context
//!
use crate::*;
use stdlib::num::NonZeroU64;
use arithmetic::store_carry;
use rounding::NonDigitRoundingData;
// const DEFAULT_PRECISION: u64 = ${RUST_BIGDECIMAL_DEFAULT_PRECISION} or 100;
include!(concat!(env!("OUT_DIR"), "/default_precision.rs"));
/// Mathematical Context
///
/// Stores rules for numerical operations, such as how to round and
/// number of digits to keep.
///
/// Defaults are defined at compile time, determined by environment
/// variables:
///
/// | Variable | Description | default |
/// |-----------------------------------------|-----------------|----------|
/// | `RUST_BIGDECIMAL_DEFAULT_PRECISION` | digit precision | 100 |
/// | `RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE` | rounding-mode | HalfEven |
///
/// It is recommended that the user set explicit values of a Context and *not*
/// rely on compile time constants, but the option is there if necessary.
///
#[derive(Debug, Clone)]
pub struct Context {
/// total number of digits
precision: NonZeroU64,
/// how to round
rounding: RoundingMode,
}
impl Context {
/// Create context with precision and rounding mode
pub fn new(precision: NonZeroU64, rounding: RoundingMode) -> Self {
Context {
precision: precision,
rounding: rounding,
}
}
/// Copy context with new precision value
pub fn with_precision(&self, precision: NonZeroU64) -> Self {
Self {
precision: precision,
..*self
}
}
/// Copy context with new precision value
pub fn with_prec<T: ToPrimitive>(&self, precision: T) -> Option<Self> {
precision
.to_u64()
.and_then(NonZeroU64::new)
.map(|prec| self.with_precision(prec))
}
/// Copy context with new rounding mode
pub fn with_rounding_mode(&self, mode: RoundingMode) -> Self {
Self {
rounding: mode,
..*self
}
}
/// Rounding mode 'Down' for truncating results when
/// proper rounding is not necessary
pub(crate) fn new_truncating(prec: u64) -> Self {
Self {
rounding: RoundingMode::Down,
precision: NonZeroU64::new(prec.max(1)).unwrap(),
}
}
/// Return maximum precision
pub fn precision(&self) -> NonZeroU64 {
self.precision
}
/// Return rounding mode
pub fn rounding_mode(&self) -> RoundingMode {
self.rounding
}
/// Round decimal to precision in this context, using rounding-mode
pub fn round_decimal(&self, n: BigDecimal) -> BigDecimal {
n.with_precision_round(self.precision(), self.rounding_mode())
}
/// Round decimal to precision in this context, using rounding-mode
pub fn round_decimal_ref<'a, D: Into<BigDecimalRef<'a>>>(&self, n: D) -> BigDecimal {
let d = n.into().to_owned();
d.with_precision_round(self.precision(), self.rounding_mode())
}
/// Round the bigint to the context's precision, returning it along with
/// the scale indicating how man digits were removed
#[allow(dead_code)]
pub(crate) fn round_bigint(
self, n: num_bigint::BigInt
) -> WithScale<num_bigint::BigInt> {
self.rounding.round_bigint_to_prec(n, self.precision)
}
/// Round the biguint to the context's precision, returning it along with
/// the scale indicating how man digits were removed
#[allow(dead_code)]
pub(crate) fn round_biguint(
self, n: num_bigint::BigUint
) -> WithScale<num_bigint::BigUint> {
let ndrd = NonDigitRoundingData { mode: self.rounding, sign: Sign::Plus };
ndrd.round_biguint_to_prec(n, self.precision)
}
/// Round digits x and y with the rounding mode
#[allow(dead_code)]
pub(crate) fn round_pair(&self, sign: Sign, x: u8, y: u8, trailing_zeros: bool) -> u8 {
self.rounding.round_pair(sign, (x, y), trailing_zeros)
}
/// Round digits x and y with the rounding mode
#[allow(dead_code)]
pub(crate) fn round_pair_with_carry(
&self,
sign: Sign,
x: u8,
y: u8,
trailing_zeros: bool,
carry: &mut u8,
) -> u8 {
self.rounding.round_pair_with_carry(sign, (x, y), trailing_zeros, carry)
}
/// Multiply two decimals, returning product rounded to this Context's precision
///
/// ```
/// # use bigdecimal::{BigDecimal, Context};
/// let x: BigDecimal = "1.5".parse().unwrap();
/// let y: BigDecimal = "3.1415926".parse().unwrap();
///
/// let ctx = Context::default().with_prec(5).unwrap();
/// let product = ctx.multiply(&x, &y);
///
/// // rounds to 5 digits of precision
/// assert_eq!(product, "4.7124".parse::<BigDecimal>().unwrap());
/// // does not equal the 'full' precision
/// assert_ne!(product, "4.71238890".parse::<BigDecimal>().unwrap());
/// ```
///
pub fn multiply<'a, L, R>(&self, lhs: L, rhs: R) -> BigDecimal
where
L: Into<BigDecimalRef<'a>>,
R: Into<BigDecimalRef<'a>>,
{
use arithmetic::multiplication::multiply_decimals_with_context;
let mut result = BigDecimal::zero();
multiply_decimals_with_context(&mut result, lhs, rhs, self);
result
}
/// Calculate `1/n`, rounding at this Context's precision
///
/// If n is zero, return zero.
///
/// ```
/// # use bigdecimal::{BigDecimal, Context};
/// let x: BigDecimal = "3".parse().unwrap();
///
/// let ctx = Context::default().with_prec(5).unwrap();
/// let one_over_three = ctx.invert(&x);
///
/// // rounds to 5 digits of precision
/// assert_eq!(one_over_three, "0.33333".parse::<BigDecimal>().unwrap());
/// ```
///
pub fn invert<'a, T: Into<BigDecimalRef<'a>>>(&self, n: T) -> BigDecimal {
n.into().inverse_with_context(self)
}
}
impl stdlib::default::Default for Context {
fn default() -> Self {
Self {
precision: NonZeroU64::new(DEFAULT_PRECISION).unwrap(),
rounding: RoundingMode::default(),
}
}
}
impl Context {
/// Add two big digit references
pub fn add_refs<'a, 'b, A, B>(&self, a: A, b: B) -> BigDecimal
where
A: Into<BigDecimalRef<'a>>,
B: Into<BigDecimalRef<'b>>,
{
let mut sum = BigDecimal::zero();
self.add_refs_into(a, b, &mut sum);
sum
}
/// Add two decimal refs, storing value in dest
pub fn add_refs_into<'a, 'b, A, B>(&self, a: A, b: B, dest: &mut BigDecimal)
where
A: Into<BigDecimalRef<'a>>,
B: Into<BigDecimalRef<'b>>,
{
let sum = a.into() + b.into();
*dest = sum.with_precision_round(self.precision, self.rounding)
}
}
#[cfg(test)]
mod test_context {
use super::*;
#[test]
fn constructor_and_setters() {
let ctx = Context::default();
let c = ctx.with_prec(44).unwrap();
assert_eq!(c.precision.get(), 44);
assert_eq!(c.rounding, RoundingMode::HalfEven);
let c = c.with_rounding_mode(RoundingMode::Down);
assert_eq!(c.precision.get(), 44);
assert_eq!(c.rounding, RoundingMode::Down);
}
#[test]
fn sum_two_references() {
use stdlib::ops::Neg;
let ctx = Context::default();
let a: BigDecimal = "209682.134972197168613072130300".parse().unwrap();
let b: BigDecimal = "3.0782968222271332463325639E-12".parse().unwrap();
let sum = ctx.add_refs(&a, &b);
let expected: BigDecimal =
"209682.1349721971716913689525271332463325639".parse().unwrap();
assert_eq!(sum, expected);
// make negative copy of b without cloning values
let neg_b = b.to_ref().neg();
let sum = ctx.add_refs(&a, neg_b);
let expected: BigDecimal =
"209682.1349721971655347753080728667536674361".parse().unwrap();
assert_eq!(sum, expected);
let sum = ctx.with_prec(27).unwrap().with_rounding_mode(RoundingMode::Up).add_refs(&a, neg_b);
let expected: BigDecimal =
"209682.134972197165534775309".parse().unwrap();
assert_eq!(sum, expected);
}
mod round_decimal_ref {
use super::*;
#[test]
fn case_bigint_1234567_prec3() {
let ctx = Context::default().with_prec(3).unwrap();
let i = BigInt::from(1234567);
let d = ctx.round_decimal_ref(&i);
assert_eq!(d.int_val, 123.into());
assert_eq!(d.scale, -4);
}
#[test]
fn case_bigint_1234500_prec4_halfup() {
let ctx = Context::default()
.with_prec(4).unwrap()
.with_rounding_mode(RoundingMode::HalfUp);
let i = BigInt::from(1234500);
let d = ctx.round_decimal_ref(&i);
assert_eq!(d.int_val, 1235.into());
assert_eq!(d.scale, -3);
}
#[test]
fn case_bigint_1234500_prec4_halfeven() {
let ctx = Context::default()
.with_prec(4).unwrap()
.with_rounding_mode(RoundingMode::HalfEven);
let i = BigInt::from(1234500);
let d = ctx.round_decimal_ref(&i);
assert_eq!(d.int_val, 1234.into());
assert_eq!(d.scale, -3);
}
#[test]
fn case_bigint_1234567_prec10() {
let ctx = Context::default().with_prec(10).unwrap();
let i = BigInt::from(1234567);
let d = ctx.round_decimal_ref(&i);
assert_eq!(d.int_val, 1234567000.into());
assert_eq!(d.scale, 3);
}
}
}
+555
View File
@@ -0,0 +1,555 @@
//! Implementation of comparison operations
//!
//! Comparisons between decimals and decimal refs
//! are not directly supported as we lose some type
//! inference features at the savings of a single
//! '&' character.
//!
//! &BigDecimal and BigDecimalRef are comparable.
//!
use crate::*;
use stdlib::cmp::Ordering;
use stdlib::iter;
impl PartialEq for BigDecimal {
fn eq(&self, rhs: &BigDecimal) -> bool {
self.to_ref() == rhs.to_ref()
}
}
impl<'rhs, T> PartialEq<T> for BigDecimalRef<'_>
where
T: Into<BigDecimalRef<'rhs>> + Copy,
{
fn eq(&self, rhs: &T) -> bool {
let rhs: BigDecimalRef<'rhs> = (*rhs).into();
check_equality_bigdecimal_ref(*self, rhs)
}
}
fn check_equality_bigdecimal_ref(lhs: BigDecimalRef, rhs: BigDecimalRef) -> bool {
match (lhs.sign(), rhs.sign()) {
// both zero
(Sign::NoSign, Sign::NoSign) => return true,
// signs are different
(a, b) if a != b => return false,
// signs are same, do nothing
_ => {}
}
let unscaled_int;
let scaled_int;
let trailing_zero_count;
match arithmetic::checked_diff(lhs.scale, rhs.scale) {
(Ordering::Equal, _) => {
return lhs.digits == rhs.digits;
}
(Ordering::Greater, Some(scale_diff)) => {
unscaled_int = lhs.digits;
scaled_int = rhs.digits;
trailing_zero_count = scale_diff;
}
(Ordering::Less, Some(scale_diff)) => {
unscaled_int = rhs.digits;
scaled_int = lhs.digits;
trailing_zero_count = scale_diff;
}
_ => {
// all other cases imply overflow in difference of scale,
// numbers must not be equal
return false;
}
}
debug_assert_ne!(trailing_zero_count, 0);
// test if unscaled_int is guaranteed to be less than
// scaled_int*10^trailing_zero_count based on highest bit
if highest_bit_lessthan_scaled(unscaled_int, scaled_int, trailing_zero_count) {
return false;
}
// try compare without allocating
if trailing_zero_count < 20 {
let pow = ten_to_the_u64(trailing_zero_count as u8);
let mut a_digits = unscaled_int.iter_u32_digits();
let mut b_digits = scaled_int.iter_u32_digits();
let mut carry = 0;
loop {
match (a_digits.next(), b_digits.next()) {
(Some(next_a), Some(next_b)) => {
let wide_b = match (next_b as u64).checked_mul(pow) {
Some(tmp) => tmp + carry,
None => break,
};
let true_b = wide_b as u32;
if next_a != true_b {
return false;
}
carry = wide_b >> 32;
}
(None, Some(_)) => {
return false;
}
(Some(a_digit), None) => {
if a_digit != (carry as u32) {
return false;
}
carry = 0;
}
(None, None) => {
return carry == 0;
}
}
}
// we broke out of loop due to overflow - compare via allocation
let scaled_int = scaled_int * pow;
return &scaled_int == unscaled_int;
}
let trailing_zero_count = trailing_zero_count.to_usize().unwrap();
let unscaled_digits = unscaled_int.to_radix_le(10);
if trailing_zero_count > unscaled_digits.len() {
return false;
}
// split into digits below the other value, and digits overlapping
let (low_digits, overlap_digits) = unscaled_digits.split_at(trailing_zero_count);
// if any of the low digits are zero, they are not equal
if low_digits.iter().any(|&d| d != 0) {
return false;
}
let scaled_digits = scaled_int.to_radix_le(10);
// different lengths with trailing zeros
if overlap_digits.len() != scaled_digits.len() {
return false;
}
// return true if all digits are the same
overlap_digits.iter().zip(scaled_digits.iter()).all(|(digit_a, digit_b)| digit_a == digit_b)
}
impl PartialOrd for BigDecimal {
#[inline]
fn partial_cmp(&self, other: &BigDecimal) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd for BigDecimalRef<'_> {
fn partial_cmp(&self, other: &BigDecimalRef<'_>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BigDecimal {
#[inline]
fn cmp(&self, other: &BigDecimal) -> Ordering {
self.to_ref().cmp(&other.to_ref())
}
}
impl Ord for BigDecimalRef<'_> {
/// Complete ordering implementation for BigDecimal
///
/// # Example
///
/// ```
/// use std::str::FromStr;
///
/// let a = bigdecimal::BigDecimal::from_str("-1").unwrap();
/// let b = bigdecimal::BigDecimal::from_str("1").unwrap();
/// assert!(a < b);
/// assert!(b > a);
/// let c = bigdecimal::BigDecimal::from_str("1").unwrap();
/// assert!(b >= c);
/// assert!(c >= b);
/// let d = bigdecimal::BigDecimal::from_str("10.0").unwrap();
/// assert!(d > c);
/// let e = bigdecimal::BigDecimal::from_str(".5").unwrap();
/// assert!(e < c);
/// ```
#[inline]
fn cmp(&self, other: &BigDecimalRef) -> Ordering {
use Ordering::*;
let scmp = self.sign().cmp(&other.sign());
if scmp != Ordering::Equal {
return scmp;
}
if self.sign() == Sign::NoSign {
return Ordering::Equal;
}
let result = match arithmetic::checked_diff(self.scale, other.scale) {
(Greater, Some(scale_diff)) | (Equal, Some(scale_diff)) => {
compare_scaled_biguints(self.digits, other.digits, scale_diff)
}
(Less, Some(scale_diff)) => {
compare_scaled_biguints(other.digits, self.digits, scale_diff).reverse()
}
(res, None) => {
// The difference in scale does not fit in a u64,
// we can safely assume the value of digits do not matter
// (unless we have a 2^64 (i.e. ~16 exabyte) long number
// larger scale means smaller number, reverse this ordering
res.reverse()
}
};
if other.sign == Sign::Minus {
result.reverse()
} else {
result
}
}
}
/// compare scaled uints: a <=> b * 10^{scale_diff}
///
fn compare_scaled_biguints(a: &BigUint, b: &BigUint, scale_diff: u64) -> Ordering {
use Ordering::*;
if scale_diff == 0 {
return a.cmp(b);
}
// check if highest bit of a is less than b * 10^scale_diff
if highest_bit_lessthan_scaled(a, b, scale_diff) {
return Ordering::Less;
}
// if biguints fit it u64 or u128, compare using those (avoiding allocations)
if let Some(result) = compare_scalar_biguints(a, b, scale_diff) {
return result;
}
let a_digit_count = count_decimal_digits_uint(a);
let b_digit_count = count_decimal_digits_uint(b);
let digit_count_cmp = a_digit_count.cmp(&(b_digit_count + scale_diff));
if digit_count_cmp != Equal {
return digit_count_cmp;
}
let a_digits = a.to_radix_le(10);
let b_digits = b.to_radix_le(10);
debug_assert_eq!(a_digits.len(), a_digit_count as usize);
debug_assert_eq!(b_digits.len(), b_digit_count as usize);
let mut a_it = a_digits.iter().rev();
let mut b_it = b_digits.iter().rev();
loop {
match (a_it.next(), b_it.next()) {
(Some(ai), Some(bi)) => {
match ai.cmp(bi) {
Equal => continue,
result => return result,
}
}
(Some(&ai), None) => {
if ai == 0 && a_it.all(Zero::is_zero) {
return Equal;
} else {
return Greater;
}
}
(None, Some(&bi)) => {
if bi == 0 && b_it.all(Zero::is_zero) {
return Equal;
} else {
return Less;
}
}
(None, None) => {
return Equal;
}
}
}
}
/// Try fitting biguints into primitive integers, using those for ordering if possible
fn compare_scalar_biguints(a: &BigUint, b: &BigUint, scale_diff: u64) -> Option<Ordering> {
let scale_diff = scale_diff.to_usize()?;
// try u64, then u128
compare_scaled_uints::<u64>(a, b, scale_diff)
.or_else(|| compare_scaled_uints::<u128>(a, b, scale_diff))
}
/// Implementation comparing biguints cast to generic type
fn compare_scaled_uints<'a, T>(
a: &'a BigUint,
b: &'a BigUint,
scale_diff: usize,
) -> Option<Ordering>
where
T: num_traits::PrimInt + TryFrom<&'a BigUint>,
{
let ten = T::from(10).unwrap();
let a = T::try_from(a).ok();
let b = T::try_from(b).ok().and_then(
|b| num_traits::checked_pow(ten, scale_diff).and_then(
|p| b.checked_mul(&p)));
match (a, b) {
(Some(a), Some(scaled_b)) => Some(a.cmp(&scaled_b)),
// if scaled_b doesn't fit in size T, while 'a' does, then a is certainly less
(Some(_), None) => Some(Ordering::Less),
// if a doesn't fit in size T, while 'scaled_b' does, then a is certainly greater
(None, Some(_)) => Some(Ordering::Greater),
// neither fits, cannot determine relative size
(None, None) => None,
}
}
/// Return highest_bit(a) < highest_bit(b * 10^{scale})
///
/// Used for optimization when comparing scaled integers
///
/// ```math
/// a < b * 10^{scale}
/// log(a) < log(b) + scale * log(10)
/// ```
///
fn highest_bit_lessthan_scaled(a: &BigUint, b: &BigUint, scale: u64) -> bool {
let a_bits = a.bits();
let b_bits = b.bits();
if a_bits < b_bits {
return true;
}
let log_scale = LOG2_10 * scale as f64;
match b_bits.checked_add(log_scale as u64) {
Some(scaled_b_bit) => a_bits < scaled_b_bit,
None => true, // overflowing u64 means we are definitely bigger
}
}
macro_rules! impl_prim_cmp {
($t:ty) => {
impl PartialOrd<$t> for BigDecimal {
fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
self.to_ref().partial_cmp(other)
}
}
impl PartialEq<$t> for BigDecimal {
fn eq(&self, rhs: &$t) -> bool {
self.to_ref().eq(rhs)
}
}
impl PartialOrd<$t> for &BigDecimal {
fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
self.to_ref().partial_cmp(other)
}
}
impl PartialOrd<$t> for BigDecimalRef<'_>
{
fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
let rhs = BigDecimal::from(other);
self.partial_cmp(&rhs.to_ref())
}
}
impl PartialEq<$t> for &BigDecimal {
fn eq(&self, rhs: &$t) -> bool {
self.to_ref().eq(rhs)
}
}
impl PartialEq<$t> for BigDecimalRef<'_>
{
fn eq(&self, rhs: &$t) -> bool {
let rhs = BigDecimal::from(rhs);
check_equality_bigdecimal_ref(*self, rhs.to_ref())
}
}
};
}
impl_prim_cmp!(u8);
impl_prim_cmp!(u16);
impl_prim_cmp!(u32);
impl_prim_cmp!(u64);
impl_prim_cmp!(u128);
impl_prim_cmp!(i8);
impl_prim_cmp!(i16);
impl_prim_cmp!(i32);
impl_prim_cmp!(i64);
impl_prim_cmp!(i128);
#[cfg(test)]
mod test {
use super::*;
mod compare_scaled_biguints {
use super::*;
macro_rules! impl_test {
($name:ident: $a:literal > $b:literal e $e:literal) => {
impl_test!($name: $a Greater $b e $e);
};
($name:ident: $a:literal < $b:literal e $e:literal) => {
impl_test!($name: $a Less $b e $e);
};
($name:ident: $a:literal = $b:literal e $e:literal) => {
impl_test!($name: $a Equal $b e $e);
};
($name:ident: $a:literal $op:ident $b:literal e $e:literal) => {
#[test]
fn $name() {
let a: BigUint = $a.parse().unwrap();
let b: BigUint = $b.parse().unwrap();
let result = compare_scaled_biguints(&a, &b, $e);
assert_eq!(result, Ordering::$op);
}
};
}
impl_test!(case_500_51e1: "500" < "51" e 1);
impl_test!(case_500_44e1: "500" > "44" e 1);
impl_test!(case_5000_50e2: "5000" = "50" e 2);
impl_test!(case_1234e9_12345e9: "1234000000000" < "12345" e 9);
impl_test!(case_1116xx459_759xx717e2: "1116386634271380982470843247639640260491505327092723527088459" < "759522625769651746138617259189939751893902453291243506584717" e 2);
}
/// Test that large-magnitidue exponentials will not crash
#[test]
fn test_cmp_on_exp_boundaries() {
let a = BigDecimal::new(1.into(), i64::MAX);
let z = BigDecimal::new(1.into(), i64::MIN);
assert_ne!(a, z);
assert_ne!(z, a);
assert!(a < z);
assert_eq!(a, a);
assert_eq!(z, z);
}
mod ord {
use super::*;
macro_rules! impl_test {
($name:ident: $a:literal < $b:literal) => {
#[test]
fn $name() {
let a: BigDecimal = $a.parse().unwrap();
let b: BigDecimal = $b.parse().unwrap();
assert!(&a < &b);
assert!(&b > &a);
assert_ne!(a, b);
}
};
}
impl_test!(case_diff_signs: "-1" < "1");
impl_test!(case_n1_0: "-1" < "0");
impl_test!(case_0_1: "0" < "1");
impl_test!(case_1d2345_1d2346: "1.2345" < "1.2346");
impl_test!(case_compare_extreme: "1e-9223372036854775807" < "1");
impl_test!(case_compare_extremes: "1e-9223372036854775807" < "1e9223372036854775807");
impl_test!(case_small_difference: "472697816888807260.1604" < "472697816888807260.16040000000000000000001");
impl_test!(case_very_small_diff: "-1.0000000000000000000000000000000000000000000000000001" < "-1");
impl_test!(case_1_2p128: "1" < "340282366920938463463374607431768211455");
impl_test!(case_1_1e39: "1000000000000000000000000000000000000000" < "1e41");
impl_test!(case_1d414xxx573: "1.414213562373095048801688724209698078569671875376948073176679730000000000000000000000000000000000000" < "1.41421356237309504880168872420969807856967187537694807317667974000000000");
impl_test!(case_11d414xxx573: "1.414213562373095048801688724209698078569671875376948073176679730000000000000000000000000000000000000" < "11.41421356237309504880168872420969807856967187537694807317667974000000000");
}
mod eq {
use super::*;
macro_rules! impl_test {
($name:ident: $a:literal = $b:literal) => {
#[test]
fn $name() {
let a: BigDecimal = $a.parse().unwrap();
let b: BigDecimal = $b.parse().unwrap();
assert_eq!(&a, &b);
assert_eq!(a, b);
}
};
}
impl_test!(case_zero: "0" = "0.00");
impl_test!(case_1_1d00: "1" = "1.00");
impl_test!(case_n1_n1000en3: "-1" = "-1000e-3");
impl_test!(case_0d000034500_345en7: "0.000034500" = "345e-7");
}
#[test]
fn test_borrow_neg_cmp() {
let x: BigDecimal = "1514932018891593.916341142773".parse().unwrap();
let y: BigDecimal = "1514932018891593916341142773e-12".parse().unwrap();
assert_eq!(x, y);
let x_ref = x.to_ref();
assert_eq!(x_ref, &y);
assert_ne!(x_ref.neg(), x_ref);
assert_eq!(x_ref.neg().neg(), x_ref);
}
mod cmp_prim {
use super::*;
#[test]
fn cmp_zero_u8() {
let n = BigDecimal::zero();
assert!(&n == 0u8);
}
}
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig { cases: 5000, ..Default::default() })]
#[test]
fn cmp_matches_f64(
f in proptest::num::f64::NORMAL | proptest::num::f64::SUBNORMAL | proptest::num::f64::ZERO,
g in proptest::num::f64::NORMAL | proptest::num::f64::SUBNORMAL | proptest::num::f64::ZERO
) {
let a: BigDecimal = BigDecimal::from_f64(f).unwrap();
let b: BigDecimal = BigDecimal::from_f64(g).unwrap();
let expected = PartialOrd::partial_cmp(&f, &g).unwrap();
let value = a.cmp(&b);
prop_assert_eq!(expected, value)
}
}
}
}
+80
View File
@@ -0,0 +1,80 @@
//! Code for implementing From/To BigDecimals
use crate::BigDecimal;
use crate::stdlib::convert::TryFrom;
use num_bigint::BigInt;
macro_rules! impl_from_int_primitive {
($t:ty) => {
impl From<$t> for BigDecimal {
fn from(n: $t) -> Self {
BigDecimal {
int_val: n.into(),
scale: 0,
}
}
}
impl From<&$t> for BigDecimal {
fn from(n: &$t) -> Self {
BigDecimal {
int_val: (*n).into(),
scale: 0,
}
}
}
};
}
impl_from_int_primitive!(u8);
impl_from_int_primitive!(u16);
impl_from_int_primitive!(u32);
impl_from_int_primitive!(u64);
impl_from_int_primitive!(u128);
impl_from_int_primitive!(i8);
impl_from_int_primitive!(i16);
impl_from_int_primitive!(i32);
impl_from_int_primitive!(i64);
impl_from_int_primitive!(i128);
impl TryFrom<f32> for BigDecimal {
type Error = super::ParseBigDecimalError;
#[inline]
fn try_from(n: f32) -> Result<Self, Self::Error> {
crate::parsing::try_parse_from_f32(n)
}
}
impl TryFrom<f64> for BigDecimal {
type Error = super::ParseBigDecimalError;
#[inline]
fn try_from(n: f64) -> Result<Self, Self::Error> {
crate::parsing::try_parse_from_f64(n)
}
}
impl From<BigInt> for BigDecimal {
fn from(int_val: BigInt) -> Self {
BigDecimal {
int_val: int_val,
scale: 0,
}
}
}
// Anything that may be a big-integer paired with a scale
// parameter may be a bigdecimal
impl<T: Into<BigInt>> From<(T, i64)> for BigDecimal {
fn from((int_val, scale): (T, i64)) -> Self {
Self {
int_val: int_val.into(),
scale: scale,
}
}
}
File diff suppressed because it is too large Load Diff
+383
View File
@@ -0,0 +1,383 @@
//! Code for num_traits
use num_traits::{Zero, Num, Signed, FromPrimitive, ToPrimitive, AsPrimitive};
use num_bigint::{BigInt, Sign, ToBigInt};
#[cfg(not(feature = "std"))]
use num_traits::float::FloatCore;
use crate::stdlib;
use stdlib::str::FromStr;
use stdlib::string::{String, ToString};
use stdlib::convert::TryFrom;
use stdlib::ops::Neg;
use stdlib::cmp::Ordering;
use crate::BigDecimal;
use crate::BigDecimalRef;
use crate::ParseBigDecimalError;
#[cfg(not(feature = "std"))]
// f64::powi is only available in std, no_std must use libm
fn powi(x: f64, n: i32) -> f64 {
libm::pow(x, n as f64)
}
#[cfg(feature = "std")]
fn powi(x: f64, n: i32) -> f64 {
x.powi(n)
}
impl Num for BigDecimal {
type FromStrRadixErr = ParseBigDecimalError;
/// Creates and initializes a BigDecimal.
#[inline]
fn from_str_radix(s: &str, radix: u32) -> Result<BigDecimal, ParseBigDecimalError> {
if radix != 10 {
return Err(ParseBigDecimalError::Other(String::from(
"The radix for decimal MUST be 10",
)));
}
let exp_separator: &[_] = &['e', 'E'];
// split slice into base and exponent parts
let (base_part, exponent_value) = match s.find(exp_separator) {
// exponent defaults to 0 if (e|E) not found
None => (s, 0),
// split and parse exponent field
Some(loc) => {
// slice up to `loc` and 1 after to skip the 'e' char
let (base, e_exp) = s.split_at(loc);
(base, i128::from_str(&e_exp[1..])?)
}
};
// TEMPORARY: Test for emptiness - remove once BigInt supports similar error
if base_part.is_empty() {
return Err(ParseBigDecimalError::Empty);
}
let mut digit_buffer = String::new();
let last_digit_loc = base_part.len() - 1;
// split decimal into a digit string and decimal-point offset
let (digits, decimal_offset) = match base_part.find('.') {
// No dot! pass directly to BigInt
None => (base_part, 0),
// dot at last digit, pass all preceding digits to BigInt
Some(loc) if loc == last_digit_loc => {
(&base_part[..last_digit_loc], 0)
}
// decimal point found - necessary copy into new string buffer
Some(loc) => {
// split into leading and trailing digits
let (lead, trail) = (&base_part[..loc], &base_part[loc + 1..]);
digit_buffer.reserve(lead.len() + trail.len());
// copy all leading characters into 'digits' string
digit_buffer.push_str(lead);
// copy all trailing characters after '.' into the digits string
digit_buffer.push_str(trail);
// count number of trailing digits
let trail_digits = trail.chars().filter(|c| *c != '_').count();
(digit_buffer.as_str(), trail_digits as i128)
}
};
// Calculate scale by subtracing the parsed exponential
// value from the number of decimal digits.
// Return error if anything overflows outside i64 boundary.
let scale = decimal_offset
.checked_sub(exponent_value)
.and_then(|scale| scale.to_i64())
.ok_or_else(||
ParseBigDecimalError::Other(
format!("Exponent overflow when parsing '{}'", s))
)?;
let big_int = BigInt::from_str_radix(digits, radix)?;
Ok(BigDecimal::new(big_int, scale))
}
}
impl ToPrimitive for BigDecimal {
fn to_i64(&self) -> Option<i64> {
self.to_ref().to_i64()
}
fn to_i128(&self) -> Option<i128> {
self.to_ref().to_i128()
}
fn to_u64(&self) -> Option<u64> {
self.to_ref().to_u64()
}
fn to_u128(&self) -> Option<u128> {
self.to_ref().to_u128()
}
fn to_f64(&self) -> Option<f64> {
self.to_ref().to_f64()
}
}
impl ToPrimitive for BigDecimalRef<'_> {
fn to_i64(&self) -> Option<i64> {
match self.sign() {
Sign::Plus if self.scale == 0 => self.digits.to_i64(),
Sign::Minus if self.scale == 0 => {
self.digits.to_u64().and_then(
|d| match d.cmp(&(i64::MAX as u64 + 1)) {
Ordering::Less => Some((d as i64).neg()),
Ordering::Equal => Some(i64::MIN),
Ordering::Greater => None,
}
)
}
Sign::Plus | Sign::Minus => self.to_owned_with_scale(0).int_val.to_i64(),
Sign::NoSign => Some(0),
}
}
fn to_i128(&self) -> Option<i128> {
match self.sign() {
Sign::Plus if self.scale == 0 => self.digits.to_i128(),
Sign::Minus if self.scale == 0 => {
self.digits.to_u128().and_then(
|d| match d.cmp(&(i128::MAX as u128 + 1)) {
Ordering::Less => Some((d as i128).neg()),
Ordering::Equal => Some(i128::MIN),
Ordering::Greater => None,
}
)
}
Sign::Plus | Sign::Minus => self.to_owned_with_scale(0).int_val.to_i128(),
Sign::NoSign => Some(0),
}
}
fn to_u64(&self) -> Option<u64> {
match self.sign() {
Sign::Plus if self.scale == 0 => self.digits.to_u64(),
Sign::Plus => self.to_owned_with_scale(0).int_val.to_u64(),
Sign::NoSign => Some(0),
Sign::Minus => None,
}
}
fn to_u128(&self) -> Option<u128> {
match self.sign() {
Sign::Plus if self.scale == 0 => self.digits.to_u128(),
Sign::Plus => self.to_owned_with_scale(0).int_val.to_u128(),
Sign::NoSign => Some(0),
Sign::Minus => None,
}
}
fn to_f64(&self) -> Option<f64> {
let copy_sign_to_float = |f: f64| if self.sign == Sign::Minus { f.neg() } else { f };
if self.digits.is_zero() {
return Some(0.0);
}
if self.scale == 0 {
return self.digits.to_f64().map(copy_sign_to_float);
}
// borrow bugint value
let (mut int_cow, mut scale) = self.to_cow_biguint_and_scale();
// approximate number of base-10 digits
let digit_count = ((int_cow.bits() + 1) as f64 * stdlib::f64::consts::LOG10_2).floor() as u64;
// trim trailing digits, 19 at a time, leaving about 25
// which should be more than accurate enough for direct
// conversion to f64
const N: u64 = 25;
let digits_to_remove = digit_count.saturating_sub(N);
let ten_to_19 = 10u64.pow(19);
let iter_count = digits_to_remove / 19;
for _ in 0..iter_count {
*int_cow.to_mut() /= ten_to_19;
scale -= 19;
}
match scale.to_i32().and_then(|x| x.checked_neg()) {
Some(pow) if 0 <= pow => {
// 'simple' integer case
let f = int_cow.to_f64().map(copy_sign_to_float)?;
(f * powi(10.0, pow)).into()
}
Some(exp) => {
// format decimal as floating point and let the default parser generate the f64
#[cfg(not(feature = "std"))]
{
let s = format!("{}e{}", int_cow, exp);
s.parse().map(copy_sign_to_float).ok()
}
#[cfg(feature = "std")]
{
use std::io::Write;
// save allocation of a String by using local buffer of bytes
// since we know the size will be small
//
// ~ 1 '-' + (N+19) digits + 1 'e' + 11 i32 digits = 32 + N
// (plus a little extra for safety)
let mut buf = [0u8; 50 + N as usize];
write!(&mut buf[..], "{}e{}", int_cow, exp).ok()?;
let i = buf.iter().position(|&c| c == 0)?;
let s = stdlib::str::from_utf8(&buf[..i]).ok()?;
s.parse().map(copy_sign_to_float).ok()
}
}
None => {
// exponenent too big for i32: return appropriate infinity
let result = if self.sign != Sign::Minus {
f64::INFINITY
} else {
f64::NEG_INFINITY
};
result.into()
}
}
}
}
impl FromPrimitive for BigDecimal {
#[inline]
fn from_i64(n: i64) -> Option<Self> {
Some(BigDecimal::from(n))
}
#[inline]
fn from_u64(n: u64) -> Option<Self> {
Some(BigDecimal::from(n))
}
#[inline]
fn from_i128(n: i128) -> Option<Self> {
Some(BigDecimal::from(n))
}
#[inline]
fn from_u128(n: u128) -> Option<Self> {
Some(BigDecimal::from(n))
}
#[inline]
fn from_f32(n: f32) -> Option<Self> {
BigDecimal::try_from(n).ok()
}
#[inline]
fn from_f64(n: f64) -> Option<Self> {
BigDecimal::try_from(n).ok()
}
}
impl ToBigInt for BigDecimal {
fn to_bigint(&self) -> Option<BigInt> {
Some(self.with_scale(0).int_val)
}
}
#[cfg(test)]
mod test {
use super::*;
mod from_str_radix {
use super::*;
#[test]
fn out_of_bounds() {
let d = BigDecimal::from_str_radix("1e-9223372036854775808", 10);
assert_eq!(d.unwrap_err(), ParseBigDecimalError::Other("Exponent overflow when parsing '1e-9223372036854775808'".to_string()));
}
}
mod to_f64 {
use super::*;
use paste::paste;
use crate::stdlib;
macro_rules! impl_case {
($name:ident: $f:expr) => {
#[test]
fn $name() {
let f: f64 = $f;
let s = format!("{}", f);
let n: BigDecimal = s.parse().unwrap();
let result = n.to_f64().unwrap();
assert_eq!(result, f, "src='{}'", s);
}
};
($name:ident: $src:literal => $expected:expr) => {
#[test]
fn $name() {
let n: BigDecimal = $src.parse().unwrap();
assert_eq!(n.to_f64().unwrap(), $expected);
}
};
}
impl_case!(case_zero: 0.0);
impl_case!(case_neg_zero: -0.0);
impl_case!(case_875en6: 0.000875);
impl_case!(case_f64_min: f64::MIN);
impl_case!(case_f64_max: f64::MAX);
impl_case!(case_f64_min_pos: f64::MIN_POSITIVE);
impl_case!(case_pi: stdlib::f64::consts::PI);
impl_case!(case_neg_e: -stdlib::f64::consts::E);
impl_case!(case_1en500: 1e-500);
impl_case!(case_3en310: 3e-310);
impl_case!(case_0d001: 0.001);
impl_case!(case_pos2_224en320: 2.224e-320);
impl_case!(case_neg2_224en320: -2.224e-320);
impl_case!(case_12d34: "12.34" => 12.34);
impl_case!(case_0d14: "0.14" => 0.14);
impl_case!(case_3d14: "3.14" => 3.14);
impl_case!(case_54e23: "54e23" => 54e23);
impl_case!(case_n54e23: "-54e23" => -54e23);
impl_case!(case_12en78: "12e-78" => 12e-78);
impl_case!(case_n12en78: "-12e-78" => -1.2e-77);
impl_case!(case_n1en320: "-1e-320" => -1e-320);
impl_case!(case_1d0001en920: "1.0001e-920" => 0.0);
impl_case!(case_50000d0000: "50000.0000" => 50000.0);
impl_case!(case_13100e4: "13100e4" => 131000000.0);
impl_case!(case_44223e9999: "44223e9999" => f64::INFINITY);
impl_case!(case_neg44223e9999: "-44223e9999" => f64::NEG_INFINITY);
}
}
#[cfg(all(test, property_tests))]
mod proptests {
use super::*;
use paste::paste;
use proptest::prelude::*;
use proptest::num::f64::{NORMAL as NormalF64, SUBNORMAL as SubnormalF64};
proptest! {
#![proptest_config(ProptestConfig::with_cases(20_000))]
#[test]
fn to_f64_roundtrip(f in NormalF64 | SubnormalF64) {
let d = BigDecimal::from_f64(f).unwrap();
let v = d.to_f64();
prop_assert!(v.is_some());
prop_assert_eq!(f, v.unwrap());
}
}
}
+454
View File
@@ -0,0 +1,454 @@
//! Implement math operations: Add,Sub, etc
use crate::*;
macro_rules! impl_add_for_primitive {
($t:ty) => {
impl_add_for_primitive!(IMPL:ADD $t);
impl_add_for_primitive!(IMPL:ADD-ASSIGN $t);
impl_add_for_primitive!(IMPL:ADD &$t);
impl_add_for_primitive!(IMPL:ADD-ASSIGN &$t);
};
(IMPL:ADD $t:ty) => {
impl Add<$t> for BigDecimal {
type Output = BigDecimal;
fn add(mut self, rhs: $t) -> BigDecimal {
self += rhs;
self
}
}
impl Add<$t> for &BigDecimal {
type Output = BigDecimal;
fn add(self, rhs: $t) -> BigDecimal {
self.to_ref() + rhs
}
}
impl Add<$t> for BigDecimalRef<'_> {
type Output = BigDecimal;
fn add(self, rhs: $t) -> BigDecimal {
BigDecimal::from(rhs) + self
}
}
impl Add<BigDecimal> for $t {
type Output = BigDecimal;
fn add(self, rhs: BigDecimal) -> BigDecimal {
rhs + self
}
}
impl Add<&BigDecimal> for $t {
type Output = BigDecimal;
fn add(self, rhs: &BigDecimal) -> BigDecimal {
rhs + self
}
}
};
(IMPL:ADD-ASSIGN &$t:ty) => {
// special case for the ref types
impl AddAssign<&$t> for BigDecimal {
fn add_assign(&mut self, rhs: &$t) {
*self += *rhs;
}
}
};
(IMPL:ADD-ASSIGN $t:ty) => {
impl AddAssign<$t> for BigDecimal {
fn add_assign(&mut self, rhs: $t) {
if rhs == 0 {
// no-op
} else if self.scale == 0 {
self.int_val += rhs;
} else {
*self += BigDecimal::from(rhs);
}
}
}
};
}
impl_add_for_primitive!(u8);
impl_add_for_primitive!(u16);
impl_add_for_primitive!(u32);
impl_add_for_primitive!(u64);
impl_add_for_primitive!(u128);
impl_add_for_primitive!(i8);
impl_add_for_primitive!(i16);
impl_add_for_primitive!(i32);
impl_add_for_primitive!(i64);
impl_add_for_primitive!(i128);
macro_rules! impl_sub_for_primitive {
($t:ty) => {
impl_sub_for_primitive!(IMPL:SUB $t);
impl_sub_for_primitive!(IMPL:SUB-ASSIGN $t);
impl_sub_for_primitive!(IMPL:SUB &$t);
impl_sub_for_primitive!(IMPL:SUB-ASSIGN &$t);
};
(IMPL:SUB $t:ty) => {
impl Sub<$t> for BigDecimal {
type Output = BigDecimal;
fn sub(mut self, rhs: $t) -> BigDecimal {
self -= rhs;
self
}
}
impl Sub<$t> for &BigDecimal {
type Output = BigDecimal;
fn sub(self, rhs: $t) -> BigDecimal {
let res = BigDecimal::from(rhs).neg();
res + self
}
}
impl Sub<BigDecimal> for $t {
type Output = BigDecimal;
fn sub(self, rhs: BigDecimal) -> BigDecimal {
rhs.neg() + self
}
}
impl Sub<&BigDecimal> for $t {
type Output = BigDecimal;
fn sub(self, rhs: &BigDecimal) -> BigDecimal {
rhs.neg() + self
}
}
};
(IMPL:SUB-ASSIGN &$t:ty) => {
impl SubAssign<&$t> for BigDecimal {
fn sub_assign(&mut self, rhs: &$t) {
*self -= *rhs;
}
}
};
(IMPL:SUB-ASSIGN $t:ty) => {
impl SubAssign<$t> for BigDecimal {
fn sub_assign(&mut self, rhs: $t) {
if self.scale == 0 {
self.int_val -= rhs;
} else {
*self -= BigDecimal::from(rhs);
}
}
}
};
}
impl_sub_for_primitive!(u8);
impl_sub_for_primitive!(u16);
impl_sub_for_primitive!(u32);
impl_sub_for_primitive!(u64);
impl_sub_for_primitive!(u128);
impl_sub_for_primitive!(i8);
impl_sub_for_primitive!(i16);
impl_sub_for_primitive!(i32);
impl_sub_for_primitive!(i64);
impl_sub_for_primitive!(i128);
macro_rules! impl_mul_for_primitive {
($t:ty) => {
impl_mul_for_primitive!(IMPL:MUL $t);
impl_mul_for_primitive!(IMPL:MUL-ASSIGN $t);
impl_mul_for_primitive!(IMPL:MUL &$t);
impl_mul_for_primitive!(IMPL:MUL-ASSIGN &$t);
};
(IMPL:MUL $t:ty) => {
impl Mul<$t> for BigDecimal {
type Output = BigDecimal;
fn mul(mut self, rhs: $t) -> BigDecimal {
self *= rhs;
self
}
}
impl Mul<$t> for &BigDecimal {
type Output = BigDecimal;
fn mul(self, rhs: $t) -> BigDecimal {
let res = BigDecimal::from(rhs);
res * self
}
}
impl Mul<BigDecimal> for $t {
type Output = BigDecimal;
fn mul(self, rhs: BigDecimal) -> BigDecimal {
rhs * self
}
}
impl Mul<&BigDecimal> for $t {
type Output = BigDecimal;
fn mul(self, rhs: &BigDecimal) -> BigDecimal {
rhs * self
}
}
};
(IMPL:MUL-ASSIGN $t:ty) => {
impl MulAssign<$t> for BigDecimal {
fn mul_assign(&mut self, rhs: $t) {
if rhs.is_zero() {
*self = BigDecimal::zero()
} else if rhs.is_one() {
// no-op
} else {
*self *= BigDecimal::from(rhs);
}
}
}
};
}
impl_mul_for_primitive!(u8);
impl_mul_for_primitive!(u16);
impl_mul_for_primitive!(u32);
impl_mul_for_primitive!(u64);
impl_mul_for_primitive!(u128);
impl_mul_for_primitive!(i8);
impl_mul_for_primitive!(i16);
impl_mul_for_primitive!(i32);
impl_mul_for_primitive!(i64);
impl_mul_for_primitive!(i128);
macro_rules! impl_div_for_primitive {
(f32) => {
impl_div_for_primitive!(IMPL:DIV:FLOAT f32);
impl_div_for_primitive!(IMPL:DIV:REF &f32);
};
(f64) => {
impl_div_for_primitive!(IMPL:DIV:FLOAT f64);
impl_div_for_primitive!(IMPL:DIV:REF &f64);
};
($t:ty) => {
impl_div_for_primitive!(IMPL:DIV $t);
impl_div_for_primitive!(IMPL:DIV:REF &$t);
impl_div_for_primitive!(IMPL:DIV-ASSIGN $t);
};
(IMPL:DIV $t:ty) => {
impl Div<$t> for BigDecimal {
type Output = BigDecimal;
fn div(self, denom: $t) -> BigDecimal {
if denom.is_one() {
self
} else if denom.checked_neg() == Some(1) {
self.neg()
} else if denom.clone() == 2 {
self.half()
} else if denom.checked_neg() == Some(2) {
self.half().neg()
} else {
self / BigDecimal::from(denom)
}
}
}
impl Div<$t> for &BigDecimal {
type Output = BigDecimal;
fn div(self, denom: $t) -> BigDecimal {
self.clone() / denom
}
}
impl Div<BigDecimal> for $t {
type Output = BigDecimal;
fn div(self, denom: BigDecimal) -> BigDecimal {
if self.is_one() {
denom.inverse()
} else {
BigDecimal::from(self) / denom
}
}
}
impl Div<&BigDecimal> for $t {
type Output = BigDecimal;
fn div(self, denom: &BigDecimal) -> BigDecimal {
self / denom.clone()
}
}
};
(IMPL:DIV-ASSIGN $t:ty) => {
impl DivAssign<$t> for BigDecimal {
fn div_assign(&mut self, rhs: $t) {
if rhs.is_zero() {
*self = BigDecimal::zero()
} else if rhs.is_one() {
// no-op
} else {
*self = self.clone() / BigDecimal::from(rhs);
}
}
}
};
(IMPL:DIV:REF $t:ty) => {
impl Div<$t> for BigDecimal {
type Output = BigDecimal;
fn div(self, denom: $t) -> BigDecimal {
self / *denom
}
}
impl Div<BigDecimal> for $t {
type Output = BigDecimal;
fn div(self, denom: BigDecimal) -> Self::Output {
*self / denom
}
}
impl Div<&BigDecimal> for $t {
type Output = BigDecimal;
fn div(self, denom: &BigDecimal) -> Self::Output {
*self / denom
}
}
impl DivAssign<$t> for BigDecimal {
fn div_assign(&mut self, denom: $t) {
self.div_assign(*denom)
}
}
};
(IMPL:DIV:FLOAT $t:ty) => {
impl Div<$t> for BigDecimal {
type Output = BigDecimal;
#[allow(clippy::float_cmp)]
fn div(self, denom: $t) -> BigDecimal {
if !denom.is_normal() {
BigDecimal::zero()
} else if denom == (1.0 as $t) {
self
} else if denom == (-1.0 as $t) {
self.neg()
} else if denom == (2.0 as $t) {
self.half()
} else if denom == (-2.0 as $t) {
self.half().neg()
} else {
self / BigDecimal::try_from(denom).unwrap()
}
}
}
impl Div<$t> for &BigDecimal {
type Output = BigDecimal;
fn div(self, denom: $t) -> BigDecimal {
self.clone() / denom
}
}
impl Div<BigDecimal> for $t {
type Output = BigDecimal;
fn div(self, denom: BigDecimal) -> Self::Output {
if !self.is_normal() {
BigDecimal::zero()
} else if self.is_one() {
denom.inverse()
} else {
BigDecimal::try_from(self).unwrap() / denom
}
}
}
impl Div<&BigDecimal> for $t {
type Output = BigDecimal;
fn div(self, denom: &BigDecimal) -> Self::Output {
if !self.is_normal() {
BigDecimal::zero()
} else if self.is_one() {
denom.inverse()
} else {
BigDecimal::try_from(self).unwrap() / denom
}
}
}
impl DivAssign<$t> for BigDecimal {
fn div_assign(&mut self, denom: $t) {
if !denom.is_normal() {
*self = BigDecimal::zero()
} else {
*self = self.clone() / BigDecimal::try_from(denom).unwrap()
};
}
}
};
}
impl_div_for_primitive!(u8);
impl_div_for_primitive!(u16);
impl_div_for_primitive!(u32);
impl_div_for_primitive!(u64);
impl_div_for_primitive!(u128);
impl_div_for_primitive!(i8);
impl_div_for_primitive!(i16);
impl_div_for_primitive!(i32);
impl_div_for_primitive!(i64);
impl_div_for_primitive!(i128);
impl_div_for_primitive!(f32);
impl_div_for_primitive!(f64);
impl Neg for BigDecimal {
type Output = BigDecimal;
#[inline]
fn neg(mut self) -> BigDecimal {
self.int_val = -self.int_val;
self
}
}
impl Neg for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn neg(self) -> BigDecimal {
-self.clone()
}
}
impl Neg for BigDecimalRef<'_> {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
sign: self.sign.neg(),
digits: self.digits,
scale: self.scale,
}
}
}
+338
View File
@@ -0,0 +1,338 @@
//! Addition operator trait implementation
//!
use super::*;
use stdlib::borrow::ToOwned;
impl Add<BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigDecimal) -> BigDecimal {
arithmetic::addition::add_bigdecimals(self, rhs)
}
}
impl<'a, T: Into<BigDecimalRef<'a>>> Add<T> for BigDecimal {
type Output = BigDecimal;
fn add(mut self, rhs: T) -> BigDecimal {
self.add_assign(rhs);
self
}
}
impl Add<BigInt> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigInt) -> BigDecimal {
self + BigDecimal::from(rhs)
}
}
impl Add<BigDecimal> for &'_ BigDecimal {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigDecimal) -> BigDecimal {
rhs + self
}
}
impl<'a, T: Into<BigDecimalRef<'a>>> Add<T> for &'_ BigDecimal {
type Output = BigDecimal;
fn add(self, rhs: T) -> BigDecimal {
arithmetic::addition::add_bigdecimal_refs(self, rhs, None)
}
}
impl Add<BigInt> for &'_ BigDecimal {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigInt) -> BigDecimal {
self.to_ref() + rhs
}
}
impl Add<BigDecimal> for BigDecimalRef<'_> {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigDecimal) -> BigDecimal {
rhs + self
}
}
impl<'a, T: Into<BigDecimalRef<'a>>> Add<T> for BigDecimalRef<'_> {
type Output = BigDecimal;
fn add(self, rhs: T) -> BigDecimal {
arithmetic::addition::add_bigdecimal_refs(self, rhs, None)
}
}
impl Add<BigInt> for BigDecimalRef<'_> {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigInt) -> BigDecimal {
self + BigDecimal::from(rhs)
}
}
impl Add<BigDecimal> for BigInt {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigDecimal) -> BigDecimal {
BigDecimal::from(self) + rhs
}
}
impl Add<&BigDecimal> for BigInt {
type Output = BigDecimal;
fn add(self, rhs: &BigDecimal) -> BigDecimal {
BigDecimal::from(self) + rhs
}
}
impl Add<BigDecimalRef<'_>> for BigInt {
type Output = BigDecimal;
fn add(self, rhs: BigDecimalRef<'_>) -> BigDecimal {
BigDecimal::from(self) + rhs
}
}
impl Add<BigDecimal> for &BigInt {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigDecimal) -> BigDecimal {
rhs + self
}
}
impl Add<&BigDecimal> for &BigInt {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: &BigDecimal) -> BigDecimal {
rhs + self
}
}
impl Add<BigDecimalRef<'_>> for &BigInt {
type Output = BigDecimal;
#[inline]
fn add(self, rhs: BigDecimalRef<'_>) -> BigDecimal {
rhs + self
}
}
impl AddAssign<BigDecimal> for BigDecimal {
fn add_assign(&mut self, rhs: BigDecimal) {
arithmetic::addition::addassign_bigdecimals(self, rhs)
}
}
impl<'a, N: Into<BigDecimalRef<'a>>> AddAssign<N> for BigDecimal {
#[inline]
fn add_assign(&mut self, rhs: N) {
arithmetic::addition::addassign_bigdecimal_ref(self, rhs)
}
}
impl AddAssign<BigInt> for BigDecimal {
#[inline]
fn add_assign(&mut self, rhs: BigInt) {
self.add_assign(BigDecimal::from(rhs));
}
}
#[cfg(test)]
mod test {
use super::*;
use paste::paste;
macro_rules! impl_case {
( $name:ident: $a:literal + $b:literal => $c:literal ) => {
#[test]
fn $name() {
let a: BigDecimal = $a.parse().unwrap();
let b: BigDecimal = $b.parse().unwrap();
let c: BigDecimal = $c.parse().unwrap();
assert_eq!(c, a.clone() + b.clone());
assert_eq!(c, a.clone() + b.to_ref());
assert_eq!(c, a.clone() + &b);
assert_eq!(c, &a + b.clone());
assert_eq!(c, &a + b.to_ref());
assert_eq!(c, &a + &b);
assert_eq!(c, a.to_ref() + b.clone());
assert_eq!(c, a.to_ref() + b.to_ref());
assert_eq!(c, a.to_ref() + &b);
// Reversed
assert_eq!(c, b.clone() + a.clone());
assert_eq!(c, b.clone() + a.to_ref());
assert_eq!(c, b.clone() + &a);
assert_eq!(c, &b + a.clone());
assert_eq!(c, &b + a.to_ref());
assert_eq!(c, &b + &a);
assert_eq!(c, b.to_ref() + a.clone());
assert_eq!(c, b.to_ref() + a.to_ref());
assert_eq!(c, b.to_ref() + &a);
let mut n = a.clone();
n += b.clone();
assert_eq!(c, n);
let mut n = a.clone();
n += &b;
assert_eq!(c, n);
let mut n = a.clone();
n += b.to_ref();
assert_eq!(c, n);
let mut n = b.clone();
n += a.clone();
assert_eq!(c, n);
let mut n = b.clone();
n += &a;
assert_eq!(c, n);
let mut n = b.clone();
n += a.to_ref();
assert_eq!(c, n);
}
};
( $name:ident: $a:literal + (int) $b:literal => $c:literal ) => {
#[test]
fn $name() {
let a: BigDecimal = $a.parse().unwrap();
let b: BigInt = $b.parse().unwrap();
let c: BigDecimal = $c.parse().unwrap();
assert_eq!(c, a.clone() + b.clone());
assert_eq!(c, a.clone() + &b);
assert_eq!(c, &a + &b);
assert_eq!(c, &a + b.clone());
assert_eq!(c, a.to_ref() + &b);
assert_eq!(c, b.clone() + a.clone());
assert_eq!(c, b.clone() + a.to_ref());
assert_eq!(c, b.clone() + &a);
assert_eq!(c, &b + a.clone());
assert_eq!(c, &b + a.to_ref());
assert_eq!(c, &b + &a);
let mut n = a.clone();
n += b.clone();
assert_eq!(c, n);
let mut n = a.clone();
n += &b;
assert_eq!(c, n);
}
};
}
impl_case!(case_1234en2_1234en3: "12.34" + "1.234" => "13.574");
impl_case!(case_1234en2_n1234en3: "12.34" + "-1.234" => "11.106");
impl_case!(case_1234en2_n1234en2: "12.34" + "-12.34" => "0");
impl_case!(case_1234e6_1234en6: "1234e6" + "1234e-6" => "1234000000.001234");
impl_case!(case_1234en6_1234e6: "1234e6" + "1234e-6" => "1234000000.001234");
impl_case!(case_18446744073709551616_1: "18446744073709551616.0" + "1" => "18446744073709551617");
impl_case!(case_184467440737e3380_0: "184467440737e3380" + "0" => "184467440737e3380");
impl_case!(case_0_776en1: "0" + "77.6" => "77.6");
impl_case!(case_80802295e5_int0: "80802295e5" + (int)"0" => "80802295e5");
impl_case!(case_239200en4_intneg101: "23.9200" + (int)"-101" => "-77.0800");
impl_case!(case_46636423395767125en15_int0: "46.636423395767125" + (int)"123" => "169.636423395767125");
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::*;
use num_traits::FromPrimitive;
proptest! {
#[test]
fn add_refs_and_owners(f: f32, g: f32) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
prop_assume!(g.is_normal());
let a = BigDecimal::from_f32(f).unwrap();
let b = BigDecimal::from_f32(g).unwrap();
let own_plus_ref = a.clone() + &b;
let ref_plus_own = &a + b.clone();
let mut c = a.clone();
c += &b;
let mut d = a.clone();
d += b;
prop_assert_eq!(&own_plus_ref, &ref_plus_own);
prop_assert_eq!(&c, &ref_plus_own);
prop_assert_eq!(&d, &ref_plus_own);
}
#[test]
fn addition_is_communative(f: f32, g: f32) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
prop_assume!(g.is_normal());
let a = BigDecimal::from_f32(f).unwrap();
let b = BigDecimal::from_f32(g).unwrap();
let a_plus_b = &a + &b;
let b_plus_a = &b + &a;
prop_assert_eq!(a_plus_b, b_plus_a)
}
#[test]
fn addition_is_associative(f: f32, g: f32, h: f32) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
prop_assume!(g.is_normal());
prop_assume!(h.is_normal());
let a = BigDecimal::from_f32(f).unwrap();
let b = BigDecimal::from_f32(g).unwrap();
let c = BigDecimal::from_f32(h).unwrap();
let ab = &a + &b;
let ab_c = ab + &c;
let bc = &b + &c;
let a_bc = a + bc;
prop_assert_eq!(ab_c, a_bc)
}
}
}
}
+90
View File
@@ -0,0 +1,90 @@
//! Implement division
use super::*;
impl Div<BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn div(self, other: BigDecimal) -> BigDecimal {
if other.is_zero() {
panic!("Division by zero");
}
if self.is_zero() || other.is_one_quickcheck() == Some(true) {
return self;
}
let scale = self.scale - other.scale;
if self.int_val == other.int_val {
return BigDecimal {
int_val: 1.into(),
scale: scale,
};
}
let max_precision = DEFAULT_PRECISION;
return impl_division(self.int_val, &other.int_val, scale, max_precision);
}
}
impl<'a> Div<&'a BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn div(self, other: &'a BigDecimal) -> BigDecimal {
if other.is_zero() {
panic!("Division by zero");
}
if self.is_zero() || other.is_one_quickcheck() == Some(true) {
return self;
}
let scale = self.scale - other.scale;
if self.int_val == other.int_val {
return BigDecimal {
int_val: 1.into(),
scale: scale,
};
}
let max_precision = DEFAULT_PRECISION;
return impl_division(self.int_val, &other.int_val, scale, max_precision);
}
}
forward_ref_val_binop!(impl Div for BigDecimal, div);
impl Div<&BigDecimal> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn div(self, other: &BigDecimal) -> BigDecimal {
if other.is_zero() {
panic!("Division by zero");
}
// TODO: Fix setting scale
if self.is_zero() || other.is_one_quickcheck() == Some(true) {
return self.clone();
}
let scale = self.scale - other.scale;
let num_int = &self.int_val;
let den_int = &other.int_val;
if num_int == den_int {
return BigDecimal {
int_val: 1.into(),
scale: scale,
};
}
let max_precision = DEFAULT_PRECISION;
return impl_division(num_int.clone(), den_int, scale, max_precision);
}
}
// tests in lib.tests.ops.div.rs
+342
View File
@@ -0,0 +1,342 @@
//! Multiplication operator trait implementation
//!
use super::*;
use crate::stdlib::mem::swap;
impl Mul<BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(mut self, rhs: BigDecimal) -> BigDecimal {
if self.is_one_quickcheck() == Some(true) {
return rhs;
}
if rhs.is_one_quickcheck() != Some(true) {
self.scale += rhs.scale;
self.int_val *= rhs.int_val;
}
self
}
}
impl Mul<&BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(mut self, rhs: &BigDecimal) -> BigDecimal {
if self.is_one_quickcheck() == Some(true) {
self.scale = rhs.scale;
self.int_val.set_zero();
self.int_val += &rhs.int_val;
} else if rhs.is_zero() {
self.scale = 0;
self.int_val.set_zero();
} else if !self.is_zero() && rhs.is_one_quickcheck() != Some(true) {
self.scale += rhs.scale;
self.int_val *= &rhs.int_val;
}
self
}
}
impl Mul<BigDecimal> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(self, rhs: BigDecimal) -> BigDecimal {
rhs * self
}
}
impl Mul<&BigDecimal> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(self, rhs: &BigDecimal) -> BigDecimal {
if self.is_one_quickcheck() == Some(true) {
rhs.normalized()
} else if rhs.is_one_quickcheck() == Some(true) {
self.normalized()
} else {
let scale = self.scale + rhs.scale;
BigDecimal::new(&self.int_val * &rhs.int_val, scale)
}
}
}
impl Mul<BigInt> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(mut self, rhs: BigInt) -> BigDecimal {
self.int_val *= rhs;
self
}
}
impl Mul<&BigInt> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(mut self, rhs: &BigInt) -> BigDecimal {
self.int_val *= rhs;
self
}
}
impl Mul<BigInt> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(self, mut rhs: BigInt) -> BigDecimal {
rhs *= &self.int_val;
BigDecimal::new(rhs, self.scale)
}
}
impl Mul<&BigInt> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(self, rhs: &BigInt) -> BigDecimal {
if rhs.is_one() {
self.normalized()
} else if self.is_one_quickcheck() == Some(true) {
BigDecimal::new(rhs.clone(), 0)
} else {
let value = &self.int_val * rhs;
BigDecimal::new(value, self.scale)
}
}
}
// swap (lhs * rhs) to (rhs * lhs) for (BigInt * BigDecimal)
forward_communative_binop!(impl Mul<BigDecimal>::mul for BigInt);
forward_communative_binop!(impl Mul<&BigDecimal>::mul for BigInt);
forward_communative_binop!(impl Mul<BigDecimal>::mul for &BigInt);
forward_communative_binop!(impl Mul<&BigDecimal>::mul for &BigInt);
impl Mul<BigUint> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(mut self, rhs: BigUint) -> BigDecimal {
self *= rhs;
self
}
}
impl Mul<&BigUint> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(mut self, rhs: &BigUint) -> BigDecimal {
self *= rhs;
self
}
}
impl Mul<BigUint> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(self, rhs: BigUint) -> BigDecimal {
self * BigInt::from_biguint(Sign::Plus, rhs)
}
}
impl Mul<&BigUint> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn mul(self, rhs: &BigUint) -> BigDecimal {
if rhs.is_one() {
self.normalized()
} else if self.is_one_quickcheck() == Some(true) {
let value = BigInt::from_biguint(Sign::Plus, rhs.clone());
BigDecimal::new(value, 0)
} else {
let biguint = self.int_val.magnitude() * rhs;
let value = BigInt::from_biguint(self.sign(), biguint);
BigDecimal::new(value, self.scale)
}
}
}
// swap (lhs * rhs) to (rhs * lhs) for (BigUint * BigDecimal)
forward_communative_binop!(impl Mul<BigDecimal>::mul for BigUint);
forward_communative_binop!(impl Mul<&BigDecimal>::mul for BigUint);
forward_communative_binop!(impl Mul<BigDecimal>::mul for &BigUint);
forward_communative_binop!(impl Mul<&BigDecimal>::mul for &BigUint);
impl MulAssign<BigDecimal> for BigDecimal {
#[inline]
fn mul_assign(&mut self, rhs: BigDecimal) {
if self.is_one_quickcheck() == Some(true) {
self.int_val = rhs.int_val;
self.scale = rhs.scale;
} else if rhs.is_one_quickcheck() != Some(true) {
self.scale += rhs.scale;
self.int_val *= rhs.int_val;
}
}
}
impl MulAssign<&BigDecimal> for BigDecimal {
#[inline]
fn mul_assign(&mut self, rhs: &BigDecimal) {
if rhs.is_one_quickcheck() == Some(true) {
return;
}
self.scale += rhs.scale;
self.int_val *= &rhs.int_val;
}
}
impl MulAssign<&BigInt> for BigDecimal {
#[inline]
fn mul_assign(&mut self, rhs: &BigInt) {
if rhs.is_one() {
return;
}
self.int_val *= rhs;
}
}
impl MulAssign<BigInt> for BigDecimal {
#[inline]
fn mul_assign(&mut self, rhs: BigInt) {
*self *= &rhs
}
}
impl MulAssign<BigUint> for BigDecimal {
#[inline]
fn mul_assign(&mut self, rhs: BigUint) {
if rhs.is_one() {
return;
}
*self *= BigInt::from_biguint(Sign::Plus, rhs);
// *self *= &rhs
}
}
impl MulAssign<&BigUint> for BigDecimal {
#[inline]
fn mul_assign(&mut self, rhs: &BigUint) {
if rhs.is_one() {
return;
}
// No way to multiply bigint and biguint, we have to clone
*self *= BigInt::from_biguint(Sign::Plus, rhs.clone());
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod bigdecimal_tests {
use super::*;
use num_traits::{ToPrimitive, FromPrimitive, Signed, Zero, One};
use num_bigint;
use paste::paste;
macro_rules! impl_test {
($name:ident; $a:literal * $b:literal => $expected:literal) => {
#[test]
fn $name() {
let mut a: BigDecimal = $a.parse().unwrap();
let b: BigDecimal = $b.parse().unwrap();
let expected: BigDecimal = $expected.parse().unwrap();
let prod = a.clone() * b.clone();
assert_eq!(prod, expected);
assert_eq!(prod.scale, expected.scale);
let prod = a.clone() * &b;
assert_eq!(prod, expected);
// assert_eq!(prod.scale, expected.scale);
let prod = &a * b.clone();
assert_eq!(prod, expected);
// assert_eq!(prod.scale, expected.scale);
let prod = &a * &b;
assert_eq!(prod, expected);
assert_eq!(prod.scale, expected.scale);
a *= b;
assert_eq!(a, expected);
assert_eq!(a.scale, expected.scale);
}
};
($name:ident; $bigt:ty; $a:literal * $b:literal => $expected:literal) => {
#[test]
fn $name() {
let a: BigDecimal = $a.parse().unwrap();
let b: $bigt = $b.parse().unwrap();
let c: BigDecimal = $expected.parse().unwrap();
let prod = a.clone() * b.clone();
assert_eq!(prod, c);
assert_eq!(prod.scale, c.scale);
let prod = b.clone() * a.clone();
assert_eq!(prod, c);
assert_eq!(prod.scale, c.scale);
let prod = a.clone() * &b;
assert_eq!(prod, c);
assert_eq!(prod.scale, c.scale);
let prod = b.clone() * &a;
assert_eq!(prod, c);
// assert_eq!(prod.scale, c.scale);
let prod = &a * b.clone();
assert_eq!(prod, c);
assert_eq!(prod.scale, c.scale);
let prod = &b * a.clone();
assert_eq!(prod, c);
// assert_eq!(prod.scale, c.scale);
let prod = &a * &b;
assert_eq!(prod, c);
// assert_eq!(prod.scale, c.scale);
let prod = &b * &a;
assert_eq!(prod, c);
// assert_eq!(prod.scale, c.scale);
}
};
}
impl_test!(case_2_1; "2" * "1" => "2");
impl_test!(case_12d34_1d234; "12.34" * "1.234" => "15.22756");
impl_test!(case_2e1_1; "2e1" * "1" => "2e1");
impl_test!(case_3_d333333; "3" * ".333333" => "0.999999");
impl_test!(case_2389472934723_209481029831; "2389472934723" * "209481029831" => "500549251119075878721813");
impl_test!(case_1ed450_1e500; "1e-450" * "1e500" => "0.1e51");
impl_test!(case_n995052931ddd_4d523087321; "-995052931372975485719.533153137" * "4.523087321" => "-4500711297616988541501.836966993116075977");
impl_test!(case_995052931ddd_n4d523087321; "995052931372975485719.533153137" * "-4.523087321" => "-4500711297616988541501.836966993116075977");
impl_test!(case_n8d37664968_n4d523087321; "-8.37664968" * "-1.9086963714056968482094712882596748" => "15.988480848752691653730876239769592670324064");
impl_test!(case_n8d37664968_0; "-8.37664968" * "0" => "0.00000000");
impl_test!(case_8d561_10; BigInt; "8.561" * "10" => "85.610");
// Test multiplication between big decimal and big integer
impl_test!(case_10000_638655273892892437; BigInt; "10000" * "638655273892892437" => "6386552738928924370000");
impl_test!(case_1en10_n9056180052657301; BigInt; "1e-10" * "-9056180052657301" => "-905618.0052657301");
impl_test!(case_n9en1_n368408638655273892892437473; BigInt; "-9e-1" * "-368408638655273892892437473" => "331567774789746503603193725.7");
impl_test!(case_n1d175470587012343730098_577575785; BigInt; "-1.175470587012343730098" * "577575785" => "-678923347.038065234601180476930");
impl_test!(case_1d000000_7848321491728058276; BigInt; "1.000000" * "7848321491728058276" => "7848321491728058276.000000");
impl_test!(case_16535178640845d04844_1; BigInt; "16535178640845.04844" * "1" => "16535178640845.04844");
impl_test!(case_1d000000_u7848321491728058276; BigUint; "1.000000" * "7848321491728058276" => "7848321491728058276.000000");
impl_test!(case_16535178640845d04844_u1; BigUint; "16535178640845.04844" * "1" => "16535178640845.04844");
}
+167
View File
@@ -0,0 +1,167 @@
//! Remainder implementations
use super::*;
impl Rem<BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn rem(self, other: BigDecimal) -> BigDecimal {
let scale = cmp::max(self.scale, other.scale);
let num = self.take_and_scale(scale).int_val;
let den = other.take_and_scale(scale).int_val;
BigDecimal::new(num % den, scale)
}
}
impl Rem<&BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn rem(self, other: &BigDecimal) -> BigDecimal {
let scale = cmp::max(self.scale, other.scale);
let num = self.take_and_scale(scale).int_val;
let den = &other.int_val;
let result = if scale == other.scale {
num % den
} else {
num % (den * ten_to_the((scale - other.scale) as u64))
};
BigDecimal::new(result, scale)
}
}
impl Rem<BigDecimal> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn rem(self, other: BigDecimal) -> BigDecimal {
let scale = cmp::max(self.scale, other.scale);
let num = &self.int_val;
let den = other.take_and_scale(scale).int_val;
let result = if scale == self.scale {
num % den
} else {
let scaled_num = num * ten_to_the((scale - self.scale) as u64);
scaled_num % den
};
BigDecimal::new(result, scale)
}
}
impl Rem<&BigDecimal> for &BigDecimal {
type Output = BigDecimal;
#[inline]
fn rem(self, other: &BigDecimal) -> BigDecimal {
let scale = cmp::max(self.scale, other.scale);
let num = &self.int_val;
let den = &other.int_val;
let result = match self.scale.cmp(&other.scale) {
Ordering::Equal => num % den,
Ordering::Less => {
let scaled_num = num * ten_to_the((scale - self.scale) as u64);
scaled_num % den
}
Ordering::Greater => {
let scaled_den = den * ten_to_the((scale - other.scale) as u64);
num % scaled_den
}
};
BigDecimal::new(result, scale)
}
}
impl RemAssign<&BigDecimal> for BigDecimal {
fn rem_assign(&mut self, other: &BigDecimal) {
let rem = (&*self).rem(other);
*self = rem;
}
}
#[cfg(test)]
mod test {
use super::*;
use paste::paste;
macro_rules! impl_case {
($a:literal % $b:literal => $c:literal ) => {
paste! {
impl_case!([< case_ $a _ $b >]: $a % $b => $c);
}
};
($name:ident: $a:literal % $b:literal => $c:literal ) => {
#[test]
fn $name() {
let mut a: BigDecimal = $a.parse().unwrap();
let b: BigDecimal = $b.parse().unwrap();
let c: BigDecimal = $c.parse().unwrap();
assert_eq!(a.clone() % b.clone(), c);
assert_eq!(a.clone() % &b, c);
assert_eq!(&a % b.clone(), c);
assert_eq!(&a % &b, c);
a %= &b;
assert_eq!(a, c);
}
};
}
impl_case!("100" % "5" => "0");
impl_case!("2e1" % "1" => "0");
impl_case!("2" % "1" => "0");
impl_case!("1" % "3" => "1");
impl_case!("1" % "5e-1" => "0");
impl_case!("15e-1" % "1" => "0.5");
impl_case!("1" % "3e-2" => "1e-2");
impl_case!("10" % "3e-3" => "0.001");
impl_case!("3" % "2" => "1");
impl_case!("1234e-2" % "1233e-3" => "0.01");
impl_case!(case_neg3_2: "-3" % "2" => "-1");
impl_case!(case_3_neg2: "3" % "-2" => "1");
impl_case!(case_neg3_neg2: "3" % "-2" => "1");
impl_case!(case_neg95eneg1_515eneg2: "-9.5" % "5.15" => "-4.35");
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::*;
use num_traits::FromPrimitive;
proptest! {
#[test]
fn quotient_and_remainder(f: f32, g: f32) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
prop_assume!(g.is_normal());
prop_assume!(!g.is_zero());
let (f, g) = if f.abs() > g.abs() {
(f, g)
} else {
(g, f)
};
let a = BigDecimal::from_f32(f).unwrap();
let b = BigDecimal::from_f32(g).unwrap();
let r = &a % &b;
let q = (&a / &b).with_scale(0);
assert_eq!(a, q * b + r);
}
}
}
}
+345
View File
@@ -0,0 +1,345 @@
//!
//! Subtraction operator trait implementation
//!
use crate::*;
impl Sub<BigDecimal> for BigDecimal {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigDecimal) -> BigDecimal {
if rhs.is_zero() {
return self;
}
if self.is_zero() {
return rhs.neg();
}
let mut lhs = self;
match lhs.scale.cmp(&rhs.scale) {
Ordering::Equal => {
lhs.int_val -= rhs.int_val;
lhs
}
Ordering::Less => {
lhs.take_and_scale(rhs.scale) - rhs
}
Ordering::Greater => {
let rhs = rhs.take_and_scale(lhs.scale);
lhs - rhs
},
}
}
}
impl Sub<BigDecimal> for &'_ BigDecimal {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigDecimal) -> BigDecimal {
self.to_ref() - rhs
}
}
impl Sub<BigDecimal> for BigDecimalRef<'_> {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigDecimal) -> BigDecimal {
(rhs - self).neg()
}
}
impl<'a, T: Into<BigDecimalRef<'a>>> Sub<T> for BigDecimal {
type Output = BigDecimal;
fn sub(mut self, rhs: T) -> BigDecimal {
self.sub_assign(rhs);
self
}
}
impl<'a, T: Into<BigDecimalRef<'a>>> Sub<T> for &'_ BigDecimal {
type Output = BigDecimal;
fn sub(self, rhs: T) -> BigDecimal {
let rhs = rhs.into();
match self.scale.cmp(&rhs.scale) {
Ordering::Equal => {
self.clone() - rhs
}
Ordering::Less => {
self.with_scale(rhs.scale) - rhs
}
Ordering::Greater => {
self - rhs.to_owned_with_scale(self.scale)
}
}
}
}
impl<'a, T: Into<BigDecimalRef<'a>>> Sub<T> for BigDecimalRef<'_> {
type Output = BigDecimal;
fn sub(self, rhs: T) -> BigDecimal {
let rhs = rhs.into();
match self.scale.cmp(&rhs.scale) {
Ordering::Equal => self.to_owned() - rhs,
Ordering::Less => self.to_owned_with_scale(rhs.scale) - rhs,
Ordering::Greater => self - rhs.to_owned_with_scale(self.scale),
}
}
}
impl Sub<BigInt> for BigDecimal {
type Output = BigDecimal;
fn sub(mut self, rhs: BigInt) -> BigDecimal {
self.sub_assign(rhs);
self
}
}
impl Sub<BigInt> for &'_ BigDecimal {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigInt) -> BigDecimal {
self.to_ref() - rhs
}
}
impl Sub<BigInt> for BigDecimalRef<'_> {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigInt) -> BigDecimal {
self - BigDecimal::from(rhs)
}
}
impl Sub<BigDecimal> for BigInt {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigDecimal) -> BigDecimal {
(rhs - self).neg()
}
}
impl Sub<BigDecimal> for &BigInt {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigDecimal) -> BigDecimal {
(rhs - self).neg()
}
}
impl<'a> Sub<BigDecimalRef<'a>> for BigInt {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigDecimalRef<'a>) -> BigDecimal {
(rhs - &self).neg()
}
}
impl<'a> Sub<BigDecimalRef<'a>> for &BigInt {
type Output = BigDecimal;
#[inline]
fn sub(self, rhs: BigDecimalRef<'a>) -> BigDecimal {
(rhs - self).neg()
}
}
impl SubAssign<BigDecimal> for BigDecimal {
#[inline]
fn sub_assign(&mut self, rhs: BigDecimal) {
if rhs.is_zero() {
return;
}
if self.is_zero() {
*self = rhs.neg();
return;
}
match self.scale.cmp(&rhs.scale) {
Ordering::Equal => {
self.int_val -= rhs.int_val;
}
Ordering::Less => {
self.int_val *= ten_to_the((rhs.scale - self.scale) as u64);
self.int_val -= rhs.int_val;
self.scale = rhs.scale;
}
Ordering::Greater => {
let mut rhs_int_val = rhs.int_val;
rhs_int_val *= ten_to_the((self.scale - rhs.scale) as u64);
self.int_val -= rhs_int_val;
}
}
}
}
impl<'rhs, T: Into<BigDecimalRef<'rhs>>> SubAssign<T> for BigDecimal {
#[inline]
fn sub_assign(&mut self, rhs: T) {
let rhs = rhs.into();
if rhs.is_zero() {
return;
}
if self.is_zero() {
*self = rhs.neg().to_owned();
return;
}
match self.scale.cmp(&rhs.scale) {
Ordering::Equal => {
self.int_val -= rhs.to_owned().int_val;
}
Ordering::Less => {
self.int_val *= ten_to_the((rhs.scale - self.scale) as u64);
self.int_val -= rhs.to_owned().int_val;
self.scale = rhs.scale;
}
Ordering::Greater => {
*self -= rhs.to_owned_with_scale(self.scale);
}
}
}
}
impl SubAssign<BigInt> for BigDecimal {
#[inline(always)]
fn sub_assign(&mut self, rhs: BigInt) {
*self -= BigDecimal::new(rhs, 0)
}
}
#[cfg(test)]
mod test {
use super::*;
use paste::paste;
macro_rules! impl_case {
($name:ident: $a:literal - $b:literal => $c:literal ) => {
#[test]
fn $name() {
let a: BigDecimal = $a.parse().unwrap();
let b: BigDecimal = $b.parse().unwrap();
let c: BigDecimal = $c.parse().unwrap();
assert_eq!(c, a.clone() - b.clone());
assert_eq!(c, a.clone() - &b);
assert_eq!(c, &a - b.clone());
assert_eq!(c, &a - &b);
assert_eq!(c, a.to_ref() - &b);
assert_eq!(c, &a - b.to_ref());
assert_eq!(c, a.to_ref() - b.to_ref());
let mut n = a.clone();
n -= b.to_ref();
assert_eq!(n, c);
let mut n = a.clone();
n -= &b;
assert_eq!(n, c);
let mut n = a.clone();
n -= b.clone();
assert_eq!(n, c);
let mut n = a.clone();
(&mut n).sub_assign(b.clone());
assert_eq!(n, c);
}
};
($name:ident: $a:literal - (int) $b:literal => $c:literal ) => {
#[test]
fn $name() {
let a: BigDecimal = $a.parse().unwrap();
let b: BigInt = $b.parse().unwrap();
let expected: BigDecimal = $c.parse().unwrap();
assert_eq!(expected, a.clone() - b.clone());
assert_eq!(expected, a.clone() - &b);
assert_eq!(expected, &a - &b);
assert_eq!(expected, &a - b.clone());
assert_eq!(expected, a.to_ref() - &b);
let expected_neg = expected.clone().neg();
assert_eq!(expected_neg, b.clone() - a.clone());
assert_eq!(expected_neg, &b - a.to_ref());
assert_eq!(expected_neg, &b - a.clone());
}
};
}
impl_case!(case_1234en2_1234en3: "12.34" - "1.234" => "11.106");
impl_case!(case_1234en2_n1234en3: "12.34" - "-1.234" => "13.574");
impl_case!(case_1234e6_1234en6: "1234e6" - "1234e-6" => "1233999999.998766");
impl_case!(case_1234en6_1234e6: "1234e-6" - "1234e6" => "-1233999999.998766");
impl_case!(case_712911676en6_4856259269250829: "712911676e-6" - "4856259269250829" => "-4856259269250116.088324");
impl_case!(case_85616001e4_0: "85616001e4" - "0" => "85616001e4");
impl_case!(case_0_520707672en5: "0" - "5207.07672" => "-520707672e-5");
impl_case!(case_99291289e5_int0: "99291289e5" - (int)"0" => "99291289e5");
impl_case!(case_7051277471570131en16_int1: "0.7051277471570131" - (int)"1" => "-0.2948722528429869");
impl_case!(case_4068603022763836en8_intneg10: "40686030.22763836" - (int)"-10" => "40686040.22763836");
#[cfg(property_tests)]
mod prop {
use super::*;
use proptest::*;
use num_traits::FromPrimitive;
proptest! {
#[test]
fn sub_refs_and_owners(f: f32, g: f32) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
prop_assume!(g.is_normal());
let a = BigDecimal::from_f32(f).unwrap();
let b = BigDecimal::from_f32(g).unwrap();
let own_minus_ref = a.clone() - &b;
let ref_minus_own = &a - b.clone();
let mut c = a.clone();
c -= &b;
let mut d = a.clone();
d -= b;
prop_assert_eq!(&own_minus_ref, &ref_minus_own);
prop_assert_eq!(&c, &ref_minus_own);
prop_assert_eq!(&d, &ref_minus_own);
}
#[test]
fn subtraction_is_anticommunative(f: f32, g: f32) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
prop_assume!(g.is_normal());
let a = BigDecimal::from_f32(f).unwrap();
let b = BigDecimal::from_f32(g).unwrap();
let a_minus_b = &a - &b;
let b_minus_a = &b - &a;
prop_assert_eq!(a_minus_b, -b_minus_a)
}
}
}
}
+459
View File
@@ -0,0 +1,459 @@
//!
//! Support for serde implementations
//!
use crate::*;
use serde_crate::{self as serde, de, ser, Serialize, Deserialize};
// const SERDE_SCALE_LIMIT: usize = = ${RUST_BIGDECIMAL_SERDE_SCALE_LIMIT} or 150_000;
#[cfg(feature = "serde_json")]
include!(concat!(env!("OUT_DIR"), "/serde_scale_limit.rs"));
impl ser::Serialize for BigDecimal {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.collect_str(&self)
}
}
/// Used by SerDe to construct a BigDecimal
struct BigDecimalVisitor;
impl<'de> de::Visitor<'de> for BigDecimalVisitor {
type Value = BigDecimal;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a number or formatted decimal string")
}
fn visit_str<E>(self, value: &str) -> Result<BigDecimal, E>
where
E: de::Error,
{
BigDecimal::from_str(value).map_err(|err| E::custom(format!("{}", err)))
}
fn visit_u64<E>(self, value: u64) -> Result<BigDecimal, E>
where
E: de::Error,
{
Ok(BigDecimal::from(value))
}
fn visit_i64<E>(self, value: i64) -> Result<BigDecimal, E>
where
E: de::Error,
{
Ok(BigDecimal::from(value))
}
fn visit_u128<E>(self, value: u128) -> Result<BigDecimal, E>
where
E: de::Error,
{
Ok(BigDecimal::from(value))
}
fn visit_i128<E>(self, value: i128) -> Result<BigDecimal, E>
where
E: de::Error,
{
Ok(BigDecimal::from(value))
}
fn visit_f32<E>(self, value: f32) -> Result<BigDecimal, E>
where
E: de::Error,
{
BigDecimal::try_from(value).map_err(|err| E::custom(format!("{}", err)))
}
fn visit_f64<E>(self, value: f64) -> Result<BigDecimal, E>
where
E: de::Error,
{
BigDecimal::try_from(value).map_err(|err| E::custom(format!("{}", err)))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
match map.next_key::<&str>() {
Ok(Some("$serde_json::private::Number")) => {
map.next_value::<BigDecimal>()
}
_ => {
Err(de::Error::invalid_type(de::Unexpected::Map, &self))
}
}
}
}
#[cfg(not(feature = "string-only"))]
impl<'de> de::Deserialize<'de> for BigDecimal {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_any(BigDecimalVisitor)
}
}
#[cfg(feature = "string-only")]
impl<'de> de::Deserialize<'de> for BigDecimal {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_str(BigDecimalVisitor)
}
}
#[cfg(test)]
mod test {
use super::*;
use paste::paste;
use serde_test::{
Token, assert_tokens, assert_de_tokens, assert_de_tokens_error
};
mod serde_serialize_deserialize_str {
use super::*;
macro_rules! impl_case {
($name:ident : $input:literal => $output:literal) => {
#[test]
fn $name() {
let expected = Token::Str($output);
let decimal: BigDecimal = $input.parse().unwrap();
assert_tokens(&decimal, &[expected]);
}
};
}
impl_case!(case_1d0: "1.0" => "1.0");
impl_case!(case_0d5: "0.5" => "0.5");
impl_case!(case_50: "50" => "50");
impl_case!(case_50000: "50000" => "50000");
impl_case!(case_1en3: "1e-3" => "0.001");
impl_case!(case_10e11: "10e11" => "1000000000000");
impl_case!(case_d25: ".25" => "0.25");
impl_case!(case_12d34e1: "12.34e1" => "123.4");
impl_case!(case_40d0010: "40.0010" => "40.0010");
}
#[cfg(not(feature = "string-only"))]
mod serde_deserialize_int {
use super::*;
macro_rules! impl_case {
( $( $ttype:ident ),+ : -$input:literal ) => {
$( paste! { impl_case!([< case_n $input _ $ttype:lower >] : $ttype : -$input); } )*
};
( $( $ttype:ident ),+ : $input:literal ) => {
$( paste! { impl_case!([< case_ $input _ $ttype:lower >] : $ttype : $input); } )*
};
($name:ident : $type:ident : $input:literal) => {
#[test]
fn $name() {
let expected = BigDecimal::from($input);
let token = Token::$type($input);
assert_de_tokens(&expected, &[token]);
}
};
}
impl_case!(I8, I16, I32, I64, U8, U16, U32, U64 : 0);
impl_case!(I8, I16, I32, I64, U8, U16, U32, U64 : 1);
impl_case!(I8, I16, I32, I64 : -1);
impl_case!(I64: -99999999999i64);
impl_case!(I64: -9_223_372_036_854_775_808i64);
}
#[cfg(not(feature = "string-only"))]
mod serde_deserialize_float {
use super::*;
macro_rules! impl_case {
( $name:ident : $input:literal => $ttype:ident : $expected:literal ) => {
paste! {
#[test]
fn [< $name _ $ttype:lower >]() {
let expected: BigDecimal = $expected.parse().unwrap();
let token = Token::$ttype($input);
assert_de_tokens(&expected, &[token]);
}
}
};
( $name:ident : $input:literal => $( $ttype:ident : $expected:literal )+ ) => {
$( impl_case!($name : $input => $ttype : $expected); )*
};
( $name:ident : $input:literal => $( $ttype:ident ),+ : $expected:literal ) => {
$( impl_case!($name : $input => $ttype : $expected); )*
};
}
impl_case!(case_1d0 : 1.0 => F32, F64 : "1");
impl_case!(case_1d1 : 1.1 => F32 : "1.10000002384185791015625"
F64 : "1.100000000000000088817841970012523233890533447265625");
impl_case!(case_0d001834988943300:
0.001834988943300 => F32 : "0.001834988943301141262054443359375"
F64 : "0.00183498894330000003084768511740776375518180429935455322265625");
impl_case!(case_n869651d9131236838:
-869651.9131236838 => F32 : "-869651.9375"
F64 : "-869651.91312368377111852169036865234375");
impl_case!(case_n1en20:
-1e-20 => F32 : "-9.999999682655225388967887463487205224055287544615566730499267578125E-21"
F64 : "-999999999999999945153271454209571651729503702787392447107715776066783064379706047475337982177734375e-119");
}
#[cfg(not(feature = "string-only"))]
mod serde_deserialize_nan {
use super::*;
#[test]
fn case_f32() {
let tokens = [ Token::F32(f32::NAN) ];
assert_de_tokens_error::<BigDecimal>(&tokens, "NAN");
}
#[test]
fn case_f64() {
let tokens = [ Token::F64(f64::NAN) ];
assert_de_tokens_error::<BigDecimal>(&tokens, "NAN");
}
}
#[cfg(feature = "serde_json")]
mod json_support {
use super::*;
use impl_serde::{Serialize, Deserialize};
use serde_json;
#[derive(Serialize, Deserialize)]
struct TestStruct {
name: String,
value: BigDecimal,
#[serde(with = "crate::serde::json_num")]
number: BigDecimal,
}
#[test]
fn test_struct_parsing() {
let json_src = r#"
{ "name": "foo", "value": 0.0008741329382918, "number": "12.34" }
"#;
let my_struct: TestStruct = serde_json::from_str(&json_src).unwrap();
assert_eq!(&my_struct.name, "foo");
assert_eq!(&my_struct.value, &"0.0008741329382918".parse::<BigDecimal>().unwrap());
assert_eq!(&my_struct.number, &"12.34".parse::<BigDecimal>().unwrap());
let s = serde_json::to_string(&my_struct).unwrap();
assert_eq!(s, r#"{"name":"foo","value":"0.0008741329382918","number":12.34}"#);
}
}
}
/// Serialize/deserialize [`BigDecimal`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`.
///
// The following example is ignored as it requires derives which we don't import and aren't compatible
// with our locked versions of rust due to proc_macro2.
/// ```ignore
/// # extern crate serde;
/// # use serde::{Serialize, Deserialize};
/// # use bigdecimal::BigDecimal;
/// # use std::str::FromStr;
///
/// #[derive(Serialize, Deserialize)]
/// pub struct ArbitraryExample {
/// #[serde(with = "bigdecimal::serde::json_num")]
/// value: BigDecimal,
/// }
///
/// let value = ArbitraryExample { value: BigDecimal::from_str("123.400").unwrap() };
/// assert_eq!(
/// &serde_json::to_string(&value).unwrap(),
/// r#"{"value":123.400}"#
/// );
/// ```
#[cfg(feature = "serde_json")]
pub mod arbitrary_precision {
use super::*;
pub fn deserialize<'de, D>(deserializer: D) -> Result<BigDecimal, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let n = BigDecimal::deserialize(deserializer)?;
if n.scale.abs() > SERDE_SCALE_LIMIT && SERDE_SCALE_LIMIT > 0 {
let msg = format!("Calculated exponent '{}' out of bounds", -n.scale);
Err(serde::de::Error::custom(msg))
} else {
Ok(n)
}
}
pub fn serialize<S>(value: &BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_json::Number::from_str(&value.to_string())
.map_err(ser::Error::custom)?
.serialize(serializer)
}
}
/// Serialize/deserialize [`Option<BigDecimal>`] as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`.
///
// The following example is ignored as it requires derives which we don't import and aren't compatible
// with our locked versions of rust due to proc_macro2.
/// ```ignore
/// # extern crate serde;
/// # use serde::{Serialize, Deserialize};
/// # use bigdecimal::BigDecimal;
/// # use std::str::FromStr;
///
/// #[derive(Serialize, Deserialize)]
/// pub struct ArbitraryExample {
/// #[serde(with = "bigdecimal::impl_serde::arbitrary_precision_option")]
/// value: Option<BigDecimal>,
/// }
///
/// let value = ArbitraryExample { value: Some(BigDecimal::from_str("123.400").unwrap()) };
/// assert_eq!(
/// &serde_json::to_string(&value).unwrap(),
/// r#"{"value":123.400}"#
/// );
///
/// let value = ArbitraryExample { value: None };
/// assert_eq!(
/// &serde_json::to_string(&value).unwrap(),
/// r#"{"value":null}"#
/// );
/// ```
#[cfg(feature = "serde_json")]
pub mod arbitrary_precision_option {
use super::*;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<BigDecimal>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
Option::<serde_json::Number>::deserialize(deserializer)?
.map(|num| num.as_str().parse().map_err(serde::de::Error::custom))
.transpose()
}
pub fn serialize<S>(value: &Option<BigDecimal>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match *value {
Some(ref decimal) => {
serde_json::Number::from_str(&decimal.to_string())
.map_err(serde::ser::Error::custom)?
.serialize(serializer)
}
None => serializer.serialize_none(),
}
}
}
#[cfg(all(test, feature = "serde_json"))]
mod test_jsonification {
use super::*;
extern crate serde_json;
mod deserialize_f64 {
use super::*;
macro_rules! impl_case {
($name:ident : $input:expr) => {
impl_case!($name: $input => $input);
};
($name:ident : $input:expr => $expected:literal) => {
#[test]
fn $name() {
let mut json_deserialize = serde_json::Deserializer::from_str($input);
let value = arbitrary_precision::deserialize(&mut json_deserialize)
.expect("should parse JSON");
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(expected, value);
}
};
}
impl_case!(case_1d0: "1.0" => "1.0");
impl_case!(case_0d001: "0.001" => "0.001");
impl_case!(case_41009d2207etc: "41009.22075436852032769878903135430037010");
}
mod serde_struct_decimals {
use super::*;
use crate as bigdecimal;
#[derive(Serialize, Deserialize)]
pub struct TestExample {
#[serde(with = "bigdecimal::serde::json_num")]
value: BigDecimal,
}
macro_rules! impl_case {
($name:ident : $input:expr => (error)) => {
#[test]
fn $name() {
let res = serde_json::from_str::<TestExample>($input);
assert!(res.is_err());
}
};
($name:ident : $input:expr => $expected:expr) => {
#[test]
fn $name() {
let obj: TestExample = serde_json::from_str($input).unwrap();
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(expected, obj.value);
// expect output to be input with all spaces removed
let s = serde_json::to_string(&obj).unwrap();
let input_stripped = $input.replace(" ", "");
assert_eq!(&s, &input_stripped);
}
};
}
impl_case!(case_1d0: r#"{ "value": 1.0 }"# => "1.0" );
impl_case!(case_2d01: r#"{ "value": 2.01 }"# => "2.01" );
impl_case!(case_50: r#"{ "value": 50 }"# => "50" );
impl_case!(case_high_prec: r#"{ "value": 64771126779.35857825871133263810255301911 }"# => "64771126779.35857825871133263810255301911" );
impl_case!(case_nan: r#"{ "value": nan }"# => (error) );
#[test]
fn scale_out_of_bounds() {
use serde_crate::de::Error;
let src = r#"{ "value": 1e92233720392233 }"#;
let parse_err = serde_json::from_str::<TestExample>(&src).err().unwrap();
let err_str = parse_err.to_string();
assert!(err_str.starts_with("Calculated exponent '92233720392233' out of bounds"), "{}", err_str);
}
}
}
@@ -0,0 +1,80 @@
use crate::*;
use stdlib::str::FromStr;
impl FromStr for BigDecimal {
type Err = ParseBigDecimalError;
#[inline]
fn from_str(s: &str) -> Result<BigDecimal, ParseBigDecimalError> {
// implemented in impl_num.rs
BigDecimal::from_str_radix(s, 10)
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! impl_case {
($name:ident: $input:literal => $int:literal E $exp:literal) => {
#[test]
fn $name() {
let dec = BigDecimal::from_str($input).unwrap();
assert_eq!(dec.int_val, $int.into());
assert_eq!(dec.scale, -($exp));
}
};
}
impl_case!(case_1331d107: "1331.107" => 1331107 E -3 );
impl_case!(case_1d0: "1.0" => 10 E -1 );
impl_case!(case_2e1: "2e1" => 2 E 1 );
impl_case!(case_0d00123: "0.00123" => 123 E -5);
impl_case!(case_n123: "-123" => -123 E -0);
impl_case!(case_n1230: "-1230" => -1230 E -0);
impl_case!(case_12d3: "12.3" => 123 E -1);
impl_case!(case_123en1: "123e-1" => 123 E -1);
impl_case!(case_1d23ep1: "1.23e+1" => 123 E -1);
impl_case!(case_1d23ep3: "1.23E+3" => 123 E 1);
impl_case!(case_1d23en8: "1.23E-8" => 123 E -10);
impl_case!(case_n1d23en10: "-1.23E-10" => -123 E -12);
impl_case!(case_123_: "123_" => 123 E -0);
impl_case!(case_31_862_140d830_686_979: "31_862_140.830_686_979" => 31862140830686979i128 E -9);
impl_case!(case_n1_1d2_2: "-1_1.2_2" => -1122 E -2);
impl_case!(case_999d521_939: "999.521_939" => 999521939 E -6);
impl_case!(case_679d35_84_03en2: "679.35_84_03E-2" => 679358403 E -8);
impl_case!(case_271576662d_e4: "271576662.__E4" => 271576662 E 4);
impl_case!(case_1_d_2: "1_._2" => 12 E -1);
}
#[cfg(test)]
mod test_invalid {
use super::*;
macro_rules! impl_case {
($name:ident: $input:literal => $exp:literal) => {
#[test]
#[should_panic(expected = $exp)]
fn $name() {
BigDecimal::from_str($input).unwrap();
}
};
}
impl_case!(case_bad_string_empty : "" => "Empty");
impl_case!(case_bad_string_empty_exponent : "123.123E" => "Empty");
impl_case!(case_bad_string_only_decimal_point : "." => "Empty");
impl_case!(test_bad_string_only_decimal_and_exponent : ".e4" => "Empty");
impl_case!(test_bad_string_only_decimal_and_underscore : "_._" => "InvalidDigit");
impl_case!(case_bad_string_hello : "hello" => "InvalidDigit");
impl_case!(case_bad_string_nan : "nan" => "InvalidDigit");
impl_case!(case_bad_string_invalid_char : "12z3.12" => "InvalidDigit");
impl_case!(case_bad_string_nan_exponent : "123.123eg" => "InvalidDigit");
impl_case!(case_bad_string_multiple_decimal_points : "123.12.45" => "InvalidDigit");
impl_case!(case_bad_string_hex : "0xCafeBeef" => "InvalidDigit");
}
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
// Test BigDecimal::double
macro_rules! impl_case {
($name:ident : $a:literal => $ex:literal ) => {
paste! {
#[test]
fn $name() {
let value = BigDecimal::from_str($a).unwrap();
let expected = BigDecimal::from_str($ex).unwrap();
let result = value.double();
assert_eq!(result, expected);
assert_eq!(result.int_val, expected.int_val);
assert_eq!(result.scale, expected.scale);
}
}
};
}
impl_case!(case_zero : "0" => "0");
impl_case!(case_1 : "1" => "2");
impl_case!(case_100Em2 : "1.00" => "2.00");
impl_case!(case_150Em2 : "1.50" => "3.00");
impl_case!(case_neg150Em2 : "-1.50" => "-3.00");
impl_case!(case_32909E4 : "32909E4" => "6.5818E+8");
impl_case!(case_1_1156024145937225657484 : "1.1156024145937225657484" => "2.2312048291874451314968");
+173
View File
@@ -0,0 +1,173 @@
mod impl_div {
use super::*;
macro_rules! impl_case {
($name:ident: $numerator:literal / $denominator:literal => $quotient:literal) => {
#[test]
fn $name() {
let a: BigDecimal = $numerator.parse().unwrap();
let b: BigDecimal = $denominator.parse().unwrap();
let c: BigDecimal = $quotient.parse().unwrap();
let q = a.clone() / b.clone();
assert_eq!(q, c);
assert_eq!(q.scale, c.scale);
assert_eq!(a.clone() / &b, c);
assert_eq!(&a / b.clone(), c);
assert_eq!(&a / &b, c);
}
};
}
impl_case!(zero_over_one: "0" / "1" => "0");
impl_case!(zero_over_ten: "0" / "10" => "0");
impl_case!(two_over_one: "2" / "1" => "2");
impl_case!(div_2e1_one: "2e1" / "1" => "2e1");
impl_case!(div_10_10: "10" / "10" => "1");
impl_case!(div_100_10d0: "100" / "10.0" => "1e1");
impl_case!(div_20d0_200: "20.0" / "200" => "0.1");
impl_case!(div_4_2: "4" / "2" => "2");
impl_case!(div_15_3: "15" / "3" => "5");
impl_case!(div_1_2: "1" / "2" => "0.5");
impl_case!(div_1_2en2: "1" / "2e-2" => "5e1");
impl_case!(div_1_0d2: "1" / "0.2" => "5");
impl_case!(div_1d0_0d02: "1.0" / "0.02" => "5e1");
impl_case!(div_1_0d020: "1" / "0.020" => "5e1");
impl_case!(div_1d0_0d020: "1.0" / "0.020" => "5e1");
impl_case!(div_5d0_4d00: "5.0" / "4.00" => "1.25");
impl_case!(div_5d0_4d000: "5.0" / "4.000" => "1.25");
impl_case!(div_5_4d000: "5" / "4.000" => "1.25");
impl_case!(div_5_4: "5" / "4" => "125e-2");
impl_case!(div_100_5: "100" / "5" => "20");
impl_case!(div_n50_5: "-50" / "5" => "-10");
impl_case!(div_200_n5: "200" / "-5" => "-40.");
impl_case!(div_1_3: "1" / "3" => ".3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333");
impl_case!(div_n2_n3: "-2" / "-3" => ".6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667");
impl_case!(div_n12d34_1d233: "-12.34" / "1.233" => "-10.00811030008110300081103000811030008110300081103000811030008110300081103000811030008110300081103001");
impl_case!(div_125348_352d2283: "125348" / "352.2283" => "355.8714617763535752237966114591019517738921035021887792661748076460636467881768727839301952739175132");
impl_case!(div_22_7: "22" / "7" => "3.142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857143");
#[test]
#[should_panic(expected = "Division by zero")]
fn test_division_by_zero_panics() {
let x = BigDecimal::from_str("3.14").unwrap();
let _r = x / 0;
}
#[test]
#[should_panic(expected = "Division by zero")]
fn test_division_by_zero_panics_v2() {
let x = BigDecimal::from_str("3.14").unwrap();
let _r = x / BigDecimal::zero();
}
#[test]
fn test_division_by_large_number() {
let n = 1u8;
let d: BigDecimal = "79437738588056219546528239237352667078".parse().unwrap();
let quotient_n_ref_d = n / &d;
let quotient_n_d = n / d.clone();
assert_eq!(quotient_n_ref_d, quotient_n_d);
let expected: BigDecimal = "1.258847517281104957975270408416632052090243053529147458917576143852500316808428812104171430669001064E-38".parse().unwrap();
assert_eq!(quotient_n_ref_d, expected);
}
}
#[test]
fn divide_by_f32_infinity() {
let num: BigDecimal = "123".parse().unwrap();
let quotient = num / f32::INFINITY;
assert!(quotient.is_zero());
}
#[test]
fn divide_by_f32_nan() {
let num: BigDecimal = "123".parse().unwrap();
let quotient = num / f32::NAN;
assert!(quotient.is_zero());
}
#[test]
fn divide_by_f64_infinity() {
let num: BigDecimal = "123".parse().unwrap();
let quotient = num / f64::INFINITY;
assert!(quotient.is_zero());
}
#[test]
fn divide_by_f64_nan() {
let num: BigDecimal = "123".parse().unwrap();
let quotient = num / f64::NAN;
assert!(quotient.is_zero());
}
macro_rules! impl_case {
($name:ident: num / $denom:literal => $expected:literal) => {
#[test]
fn $name() {
let num = numerator();
let den = $denom;
let expected: BigDecimal = $expected.parse().unwrap();
{
let quotient = &num / den;
assert_eq!(&quotient, &expected);
}
let quotient = num / den;
assert_eq!(quotient, expected);
}
};
($name:ident: $numer:literal / den => $expected:literal) => {
#[test]
fn $name() {
let num = $numer;
let den = denominator();
let expected: BigDecimal = $expected.parse().unwrap();
{
let quotient = num / &den;
assert_eq!(&quotient, &expected);
}
let quotient = num / den;
assert_eq!(quotient, expected);
}
}
}
mod dec123_over_float {
use super::*;
fn numerator() -> BigDecimal {
"123".parse().unwrap()
}
impl_case!(divide_by_one: num / 1.0 => "123");
impl_case!(divide_by_neg_one: num / -1.0 => "-123");
impl_case!(divide_by_two: num / 2.0 => "61.5");
impl_case!(divide_by_neg_two: num / -2.0 => "-61.5");
impl_case!(divide_by_half: num / 0.25 => "492");
impl_case!(divide_by_0d3: num / 0.3 => "410.0000000000000151730480032104732806385410274753293198491224660103204812897069364682009036747721645");
impl_case!(divide_by_n10d01: num / -10.01 => "-12.28771228771228797395438676755509642184314950109407611037730443717479254091338404424812715057130341");
}
mod num_float_over_dec123 {
use super::*;
fn denominator() -> BigDecimal {
"123".parse().unwrap()
}
impl_case!(num_one: 1.0 / den => "0.008130081300813008130081300813008130081300813008130081300813008130081300813008130081300813008130081301");
impl_case!(num_123: 123.0 / den => "1");
impl_case!(num_676d5: 676.5 / den => "5.5");
}
@@ -0,0 +1,116 @@
// Property tests to be included by lib.rs (if enabled)
mod arithmetic {
use super::*;
use stdlib::num::NonZeroI128;
macro_rules! impl_test {
($t:ty) => {
paste! { proptest! {
#[test]
fn [< add_ref $t >](n: $t, m: i128, e: i8) {
let d = BigDecimal::new(m.into(), e as i64);
let sum = n + &d;
let s1 = &d + n;
let s2 = d.clone() + n;
prop_assert_eq!(&sum, &s1);
prop_assert_eq!(&sum, &s2);
let mut s = d;
s += n;
prop_assert_eq!(sum, s);
}
#[test]
fn [< sub_ $t >](n: $t, m: i128, e: i8) {
let d = BigDecimal::new(m.into(), e as i64);
let diff_n_d = n - &d;
let diff_d_n = d.clone() - n;
prop_assert_eq!(&diff_n_d, &diff_d_n.neg());
let mut a = d.clone();
a -= n;
prop_assert_eq!(&a, &diff_n_d.neg());
}
#[test]
fn [< mul_ $t >](n: $t, m: i128, e: i8) {
let d = BigDecimal::new(m.into(), e as i64);
let prod_n_d = n * &d;
let prod_d_n = d.clone() * n;
prop_assert_eq!(&prod_n_d, &prod_d_n);
let mut r = d.clone();
r *= n;
prop_assert_eq!(&prod_n_d, &r);
let r = d.neg() * n;
prop_assert_eq!(prod_n_d.neg(), r);
}
#[test]
fn [< div_ $t >](n: $t, m: NonZeroI128, e: i8) {
let d = BigDecimal::new(m.get().into(), e as i64);
let quotient_n_ref_d = n / &d;
let quotient_n_d = n / d.clone();
prop_assert_eq!(&quotient_n_ref_d, &quotient_n_d);
let prod = quotient_n_d * &d;
let diff = n - &prod;
prop_assert!(diff.abs() < BigDecimal::new(1.into(), 60));
}
} }
};
(float-div $t:ty) => {
paste! { proptest! {
#[test]
fn [< div_ $t >](n: $t, m: NonZeroI128, e: i8) {
let d = BigDecimal::new(m.get().into(), e as i64);
let quotient_n_ref_d = n / &d;
let quotient_n_d = n / d.clone();
prop_assert_eq!(&quotient_n_ref_d, &quotient_n_d);
let quotient_ref_d_n = &d / n;
let quotient_d_n = d.clone() / n;
prop_assert_eq!(&quotient_ref_d_n, &quotient_d_n);
let mut q = d.clone();
q /= n;
prop_assert_eq!(&q, &quotient_d_n);
}
} }
};
}
impl_test!(u8);
impl_test!(u16);
impl_test!(u32);
impl_test!(u64);
impl_test!(u128);
impl_test!(i8);
impl_test!(i16);
impl_test!(i32);
impl_test!(i64);
impl_test!(i128);
impl_test!(float-div f32);
impl_test!(float-div f64);
proptest! {
#[test]
fn square(f: f32) {
// ignore non-normal numbers
prop_assume!(f.is_normal());
let n: BigDecimal = BigDecimal::from_f32(f).unwrap();
let n_times_n = &n * &n;
prop_assert_eq!(n_times_n, n.square())
}
}
}
+139
View File
@@ -0,0 +1,139 @@
mod is_one {
use super::*;
#[test]
fn all_from_1_1000() {
let mut i = BigUint::from(1u8);
for s in 1..1000 {
i *= 10u8;
let d = BigDecimalRef::from(WithScale { value: &i, scale: s });
assert!(d.is_one(), "{}", s);
i += 1u8;
let d = BigDecimalRef::from(WithScale { value: &i, scale: s });
assert!(!d.is_one(), "{}", s);
i -= 1u8;
}
}
macro_rules! impl_case {
($name:ident: $input:literal => $expected:literal) => {
#[test]
fn $name() {
let d: BigDecimal = $input.parse().unwrap();
assert_eq!(d.is_one(), $expected);
}
};
}
impl_case!(case_0: "0" => false);
impl_case!(case_1: "1" => true);
impl_case!(case_n1: "-1" => false);
impl_case!(case_10en1: "10e-1" => true);
impl_case!(case_100en2: "100e-2" => true);
impl_case!(case_10000en4: "10000e-4" => true);
impl_case!(case_1d00000000000000000: "1.00000000000000000" => true);
impl_case!(case_1d000000000000000000: "1.000000000000000000" => true);
impl_case!(case_1d0000000000000000000: "1.0000000000000000000" => true);
impl_case!(case_1d00000000000000001: "1.00000000000000001" => false);
impl_case!(case_1d000000000000000001: "1.000000000000000001" => false);
impl_case!(case_1d0000000000000000001: "1.0000000000000000001" => false);
impl_case!(case_1d00000000000000000000000000000000: "1.00000000000000000000000000000000" => true);
impl_case!(case_1d00000000000000000000000000000001: "1.00000000000000000000000000000001" => false);
impl_case!(case_10d0000000000000000000000000000000000: "10.00000000000000000000000000000000000" => false);
}
mod is_one_quickcheck {
use super::*;
#[test]
fn all_from_1_1e39() {
let mut i = BigUint::from(1u8);
for s in 1..39 {
i *= 10u8;
let d = BigDecimalRef::from(WithScale { value: &i, scale: s });
assert_eq!(d.is_one_quickcheck(), Some(true), "{}", s);
i += 1u8;
let d = BigDecimalRef::from(WithScale { value: &i, scale: s });
assert_eq!(d.is_one_quickcheck(), Some(false), "{}", s);
i -= 1u8;
}
}
macro_rules! impl_case {
($name:ident: $input:literal => $expected:expr) => {
#[test]
fn $name() {
let d: BigDecimal = $input.parse().unwrap();
assert_eq!(d.is_one_quickcheck(), $expected);
}
};
}
impl_case!(case_0: "0" => Some(false));
impl_case!(case_1: "1" => Some(true));
impl_case!(case_n1: "-1" => Some(false));
impl_case!(case_10000000000000000000en19: "10000000000000000000e-19" => Some(true));
impl_case!(case_1d00000000000000000000: "1.00000000000000000000" => Some(true));
impl_case!(case_1d00000000000000000000000000000001: "1.00000000000000000000000000000001" => Some(false));
impl_case!(case_pi15: "3.141592653589793" => Some(false));
impl_case!(case_pi20: "3.14159265358979323846" => Some(false));
impl_case!(case_pi50: "3.1415926535897932384626433832795028841971693993751" => None);
impl_case!(case_10d00000000000000000000: "10.00000000000000000000" => Some(false));
impl_case!(case_1000000000d00000000000000000000: "1000000000.00000000000000000000" => Some(false));
impl_case!(case_10000000000000000000000000d0000000000000000000000000000000000000000000: "10000000000000000000000000.0000000000000000000000000000000000000000000" => Some(false));
impl_case!(case_10000000000000000000000000d0x: "10000000000000000000000000.0" => Some(false));
}
mod decimal_digit_count {
use super::*;
macro_rules! impl_case {
($name:ident: $input:literal => $expected:literal) => {
#[test]
fn $name() {
let d: BigDecimal = $input.parse().unwrap();
assert_eq!(d.decimal_digit_count(), $expected);
}
};
}
impl_case!(case_0: "0" => 1);
impl_case!(case_1: "1" => 1);
impl_case!(case_10: "10" => 2);
impl_case!(case_10d01: "10.01" => 4);
impl_case!(case_d00099999999999: ".00099999999999" => 11);
}
mod order_of_magnitude {
use super::*;
macro_rules! impl_case {
($name:ident: $input:literal => $expected:literal) => {
#[test]
fn $name() {
let d: BigDecimal = $input.parse().unwrap();
assert_eq!(d.order_of_magnitude(), $expected);
}
};
}
impl_case!(case_0: "0" => 0);
impl_case!(case_1: "1" => 0);
impl_case!(case_10: "10" => 1);
impl_case!(case_1e1: "1e1" => 1);
impl_case!(case_0e3: "0e3" => 0);
impl_case!(case_30e2: "30e2" => 3);
impl_case!(case_99en2: "99e-2" => -1);
impl_case!(case_n100d0012e1: "-100.0012e1" => 3);
impl_case!(case_16651567373773d553089: "16651567373773.553089" => 13);
}
@@ -0,0 +1,113 @@
// Test BigDecimal::with_scale_round
macro_rules! impl_test {
( name=$($name:expr)*; $scale:literal : $mode:ident => $ex:literal ) => {
paste! {
#[test]
fn [< $($name)* _rounding_ $mode >]() {
let bigdecimal = test_input();
let result = bigdecimal.with_scale_round($scale as i64, RoundingMode::$mode);
let expected = BigDecimal::from_str($ex).unwrap();
assert_eq!(result, expected);
assert_eq!(result.int_val, expected.int_val);
assert_eq!(result.scale, $scale);
}
}
};
( -$scale:literal $( : $($modes:ident),+ => $ex:literal )+ ) => {
$( $( impl_test!(name=scale_neg_ $scale; -$scale : $modes => $ex); )* )*
};
( $scale:literal $( : $($modes:ident),+ => $ex:literal )+ ) => {
$( $( impl_test!(name=scale_ $scale; $scale : $modes => $ex); )* )*
};
}
mod case_3009788271450eNeg9 {
use super::*;
fn test_input() -> BigDecimal {
BigDecimal::from_str("3009.788271450").unwrap()
}
impl_test!(10 : Up, Down => "3009.7882714500");
impl_test!(9 : Up, Down => "3009.788271450");
impl_test!(8 : Up, Down, HalfEven => "3009.78827145");
impl_test!(7 : Up, Ceiling, HalfUp => "3009.7882715"
: Down, Floor, HalfDown, HalfEven => "3009.7882714");
impl_test!(4 : Up, Ceiling, HalfUp, HalfDown, HalfEven => "3009.7883"
: Down, Floor => "3009.7882");
impl_test!(2 : Up => "3009.79"
: Down => "3009.78");
impl_test!(1 : Up => "3009.8"
: Down => "3009.7");
impl_test!(0 : Up => "3010"
: Down => "3009");
impl_test!( -1 : Up => "301e1");
impl_test!( -2 : Up => "31e2");
impl_test!( -3 : Up => "4e3");
impl_test!( -4 : Up => "1e4" );
impl_test!( -5 : Up => "1e5" : Down => "0");
impl_test!( -20 : Up => "1e20" : Down => "0");
}
mod case_neg_636652287787259 {
use super::*;
fn test_input() -> BigDecimal {
BigDecimal::from_str("-636652287787259").unwrap()
}
impl_test!(1 : Up, Down => "-636652287787259.0");
impl_test!(0 : Up, Down => "-636652287787259");
impl_test!(-1 : Up => "-63665228778726e1"
: Down => "-63665228778725e1");
impl_test!(-12 : Up => "-637e12"
: Down => "-636e12");
}
mod case_99999999999999999999999eNeg4 {
use super::*;
fn test_input() -> BigDecimal {
BigDecimal::from_str("99999999999999999999999e-4").unwrap()
}
impl_test!(4 : Up => "9999999999999999999.9999");
impl_test!(3 : Up => "10000000000000000000.000"
: Down => "9999999999999999999.999");
impl_test!(-3 : Up => "10000000000000000e3"
: Down => "9999999999999999e3");
}
mod case_369708962060657eNeg30 {
use super::*;
fn test_input() -> BigDecimal {
BigDecimal::from_str("369708962060657E-30").unwrap()
}
impl_test!(4 : Up => "1e-4");
impl_test!(20 : Up => "36971e-20"
: Down => "36970e-20");
}
mod case_682829560896740e30 {
use super::*;
fn test_input() -> BigDecimal {
BigDecimal::from_str("682829560896740e30").unwrap()
}
impl_test!(4 : Up => "682829560896740000000000000000000000000000000.0000");
impl_test!(0 : Up => "682829560896740000000000000000000000000000000");
impl_test!(-35 : Up => "6828295609e35");
impl_test!(-36 : Up => "682829561e36");
impl_test!(-100 : Up => "1e100");
}
+75
View File
@@ -0,0 +1,75 @@
// \file src/macros.rs
//! macros for
/*
macro_rules! forward_val_val_binop {
(impl $imp:ident for $res:ty, $method:ident) => {
impl $imp<$res> for $res {
type Output = $res;
#[inline]
fn $method(self, other: $res) -> $res {
// forward to val-ref
$imp::$method(self, &other)
}
}
};
}
*/
macro_rules! forward_ref_val_binop {
(impl $imp:ident for $res:ty, $method:ident) => {
impl<'a> $imp<$res> for &'a $res {
type Output = $res;
#[inline]
fn $method(self, other: $res) -> $res {
// forward to ref-ref
$imp::$method(self, &other)
}
}
};
}
macro_rules! forward_communative_binop {
(impl $trait:ident<$t1:ty>::$method:ident for $t2:ty) => {
forward_communative_binop!(
impl $trait<$t1>::$method for $t2; Output=BigDecimal
);
};
(impl $trait:ident<$t1:ty>::$method:ident for $t2:ty; Output=$output:ty) => {
impl $trait<$t1> for $t2 {
type Output = $output;
#[inline]
fn $method(self, rhs: $t1) -> Self::Output {
// swap operands
$trait::$method(rhs, self)
}
}
};
}
/*
macro_rules! forward_val_ref_binop {
(impl $imp:ident for $res:ty, $method:ident) => {
impl<'a> $imp<&'a $res> for $res {
type Output = $res;
#[inline]
fn $method(self, other: &$res) -> $res {
// forward to ref-ref
$imp::$method(&self, other)
}
}
};
}
// Forward everything to ref-ref, when reusing storage is not helpful
macro_rules! forward_all_binop_to_ref_ref {
(impl $imp:ident for $res:ty, $method:ident) => {
forward_val_val_binop!(impl $imp for $res, $method);
forward_val_ref_binop!(impl $imp for $res, $method);
forward_ref_val_binop!(impl $imp for $res, $method);
};
}
*/
+261
View File
@@ -0,0 +1,261 @@
//! Routines for parsing values into BigDecimals
use super::{BigDecimal, ParseBigDecimalError};
use stdlib::num::FpCategory;
use stdlib::cmp::{self, Ordering};
use num_bigint::{BigInt, BigUint, Sign};
use num_traits::Zero;
/// Try creating bigdecimal from f32
///
/// Non "normal" values will return Error case
///
pub(crate) fn try_parse_from_f32(n: f32) -> Result<BigDecimal, ParseBigDecimalError> {
use stdlib::num::FpCategory::*;
match n.classify() {
Nan => Err(ParseBigDecimalError::Other("NAN".into())),
Infinite => Err(ParseBigDecimalError::Other("Infinite".into())),
Subnormal => Ok(parse_from_f32_subnormal(n)),
Normal | Zero => Ok(parse_from_f32(n)),
}
}
/// Return mantissa, exponent, and sign of given floating point number
///
/// ```math
/// f = frac * 2^pow
/// ```
///
fn split_f32_into_parts(f: f32) -> (u32, i64, Sign) {
let bits = f.to_bits();
let frac = (bits & ((1 << 23) - 1)) + (1 << 23);
let exp = (bits >> 23) & 0xFF;
let pow = exp as i64 - 127 - 23;
let sign_bit = bits & (1 << 31);
let sign = if sign_bit == 0 {
Sign::Plus
} else {
Sign::Minus
};
(frac, pow, sign)
}
/// Create bigdecimal from f32
///
pub(crate) fn parse_from_f32(n: f32) -> BigDecimal {
if n.classify() == FpCategory::Subnormal {
return parse_from_f32_subnormal(n);
}
let bits = n.to_bits();
if (bits << 1) == 0 {
return Zero::zero();
}
// n = <sign> frac * 2^pow
let (frac, pow, sign) = split_f32_into_parts(n);
let result;
let scale;
match pow.cmp(&0) {
Ordering::Equal => {
result = BigUint::from(frac);
scale = 0;
}
Ordering::Less => {
let trailing_zeros = cmp::min(frac.trailing_zeros(), -pow as u32);
let reduced_frac = frac >> trailing_zeros;
let reduced_pow = pow + trailing_zeros as i64;
debug_assert!(reduced_pow <= 0);
let shift = BigUint::from(5u8).pow(-reduced_pow as u32);
result = reduced_frac * shift;
scale = -reduced_pow;
}
Ordering::Greater => {
let shift = BigUint::from(2u8).pow(pow.abs() as u32);
result = frac * shift;
scale = 0;
}
}
BigDecimal {
int_val: BigInt::from_biguint(sign, result),
scale: scale,
}
}
/// Create bigdecimal from subnormal f32
pub(crate) fn parse_from_f32_subnormal(n: f32) -> BigDecimal {
debug_assert_eq!(n.classify(), FpCategory::Subnormal);
let bits = n.to_bits();
let sign_bit = bits >> 31;
debug_assert_eq!(bits >> 24, sign_bit << 7);
let frac = bits - (sign_bit << 31);
// 5^149 = 5^126 + 5^23 (f32-bit-bias=126, fraction-bits=23)
let five_to_149 = BigUint::from_slice(&[
1466336501, 2126633373, 2856417274, 1232167559, 2512314040, 1644054862,
3843013918, 3873995871, 858643596, 3706384338, 65604258
]);
let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus };
let magnitude = BigUint::from(frac) * five_to_149;
let scale = 149;
let result = BigDecimal::new(BigInt::from_biguint(sign, magnitude), scale);
return result;
}
#[cfg(test)]
#[allow(non_snake_case)]
mod test_parse_from_f32 {
use super::*;
include!("parsing.tests.parse_from_f32.rs");
}
/// Try creating bigdecimal from f64
///
/// Non "normal" values will return Error case
///
pub(crate) fn try_parse_from_f64(n: f64) -> Result<BigDecimal, ParseBigDecimalError> {
use stdlib::num::FpCategory::*;
match n.classify() {
Nan => Err(ParseBigDecimalError::Other("NAN".into())),
Infinite => Err(ParseBigDecimalError::Other("Infinite".into())),
Subnormal => Ok(parse_from_f64_subnormal(n)),
Normal | Zero => Ok(parse_from_f64(n)),
}
}
/// Return mantissa, exponent, and sign of given floating point number
///
/// ```math
/// f = frac * 2^pow
/// ```
///
fn split_f64_into_parts(f: f64) -> (u64, i64, Sign) {
let bits = f.to_bits();
let frac = (bits & ((1 << 52) - 1)) + (1 << 52);
let exp = (bits >> 52) & 0x7FF;
let pow = exp as i64 - 1023 - 52;
let sign_bit = bits & (1 << 63);
let sign = if sign_bit == 0 {
Sign::Plus
} else {
Sign::Minus
};
(frac, pow, sign)
}
/// Create bigdecimal from subnormal f64
pub(crate) fn parse_from_f64_subnormal(n: f64) -> BigDecimal {
debug_assert_eq!(n.classify(), FpCategory::Subnormal);
let bits = n.to_bits();
let sign_bit = bits >> 63;
debug_assert_eq!(bits >> 52, sign_bit << 11);
// 5^1074 = 5^1022 + 5^52 (f64-bit-bias=1022, fraction-bits=52)
let five_to_1074 = BigUint::from_slice(&[
2993937753, 2678407619, 3969251600, 2340035423, 635686544, 3544357150, 2618749834,
3195461310, 2593678749, 4014721034, 2512738537, 1379014958, 2606506302, 1209795638,
3422246832, 2235398534, 2765471138, 3453720203, 3699786234, 1752628667, 3832472493,
2479745915, 4210941784, 2088904316, 4137646701, 3840319652, 3815898978, 2202136831,
1022273801, 1470939580, 2032173740, 4063736241, 2069243191, 4077145663, 4033014231,
1920904652, 4195885152, 3551517817, 4246423481, 2447790869, 1797774111, 11284306,
195273359, 3811183395, 4065514955, 3382133286, 1078447835, 2100087074, 3915378083,
1127077286, 1409634978, 2331452623, 1301118814, 3692061923, 2506161869, 4270519152,
1066095370, 212429084, 3729063602, 3175008277, 2075072468, 2136773221, 4247151843,
2395660055, 449096848, 2439918400, 1564416362, 3638689409, 3054795416, 1803373736,
1506581328, 2791252870, 3391180271, 1768177410, 3891987426, 3655546435, 3881223940,
903390128
]);
let frac = bits - (sign_bit << 63);
let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus };
let magnitude = BigUint::from(frac) * five_to_1074;
let scale = 1074;
return BigDecimal::new(BigInt::from_biguint(sign, magnitude), scale);
}
/// Create bigdecimal from f64
///
/// Non "normal" values is undefined behavior
///
pub(crate) fn parse_from_f64(n: f64) -> BigDecimal {
if n.classify() == FpCategory::Subnormal {
return parse_from_f64_subnormal(n);
}
let bits = n.to_bits();
// shift right by 1 bit to handle -0.0
if (bits << 1) == 0 {
return Zero::zero();
}
// n = <sign> frac * 2^pow
let (frac, pow, sign) = split_f64_into_parts(n);
debug_assert!(frac > 0);
let result;
let scale;
match pow.cmp(&0) {
Ordering::Equal => {
result = BigUint::from(frac);
scale = 0;
}
Ordering::Less => {
let trailing_zeros = cmp::min(frac.trailing_zeros(), -pow as u32);
let reduced_frac = frac >> trailing_zeros;
let reduced_pow = pow + trailing_zeros as i64;
debug_assert!(reduced_pow <= 0);
let shift = BigUint::from(5u8).pow(-reduced_pow as u32);
result = reduced_frac * shift;
scale = -reduced_pow;
}
Ordering::Greater => {
let shift = BigUint::from(2u8).pow(pow as u32);
result = frac * shift;
scale = 0;
}
}
BigDecimal {
int_val: BigInt::from_biguint(sign, result),
scale: scale,
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod test_parse_from_f64 {
use super::*;
include!("parsing.tests.parse_from_f64.rs");
}
@@ -0,0 +1,118 @@
// tests for function bigdecimal::parsing::parse_from_f32
use paste::paste;
use stdlib::f32;
macro_rules! impl_test {
($name:ident : $input:literal == $expected:literal) => {
paste! {
#[test]
fn [< case $name >]() {
let n = $input as f32;
let d = parse_from_f32(n);
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(d, expected);
}
#[test]
fn [< case_neg $name >]() {
let n = -($input as f32);
let d = parse_from_f32(n);
let expected: BigDecimal = concat!("-", $expected).parse().unwrap();
assert_eq!(d, expected);
}
}
};
}
impl_test!(_0 : 0.0 == "0");
impl_test!(_1 : 1.0 == "1");
impl_test!(_5en1 : 0.5 == "0.5");
impl_test!(_25en2 : 0.25 == "0.25");
impl_test!(_50 : 50. == "50");
impl_test!(_1en3 : 0.001 == "0.001000000047497451305389404296875");
impl_test!(_033203125en8 : 0.033203125 == "0.033203125");
impl_test!(_45En1 : 4.5 == "4.5");
impl_test!(_15625En5 : 0.15625 == "0.15625");
impl_test!(_1192092896En7 : 1.192092896e-7 == "1.1920928955078125E-7");
impl_test!(_1401757440 : 1401757440. == "1401757440");
impl_test!(_215092En1 : 21509.2 == "21509.19921875");
impl_test!(_2289620000 : 2289620000.0 == "2289619968");
impl_test!(_10000000 : 10000000. == "10000000");
impl_test!(_1en05 : 1e-5 == "0.00000999999974737875163555145263671875");
impl_test!(_1en1 : 1e-1 == "0.100000001490116119384765625");
impl_test!(_2en1 : 2e-1 == "0.20000000298023223876953125");
impl_test!(_80000197 : 80000197e0 == "80000200");
impl_test!(_23283064En16 : 2.3283064e-10 == "0.00000000023283064365386962890625");
impl_test!(_14693861798803098En17 : 0.14693861798803098 == "0.146938621997833251953125");
impl_test!(_1e20 : 1e20 == "100000002004087734272");
impl_test!(_1e30 : 1e30 == "1000000015047466219876688855040");
impl_test!(_1e38 : 1e38 == "99999996802856924650656260769173209088");
impl_test!(_317e36 : 317e36 == "317000006395220278118691742155288870912");
impl_test!(_23509889819en48 : 2.3509889819e-38 == "2.35098898190426788090088725919040801362055736959656341832065776397049129686767088287524529732763767242431640625E-38");
impl_test!(_235098744048en49 : 2.35098744048e-38 == "2.350987440475957123602109243087866394712812961308427354153308831195379018097479928428583662025630474090576171875E-38");
impl_test!(_6_99999952316 : 6.99999952316 == "6.999999523162841796875");
impl_test!(_317en40 : 317e-40 == "3.1700000098946435501119816090716154772221806896649747100732700841687651538425285480116144753992557525634765625E-38");
impl_test!(_4294967295 : 4294967295. == "4294967296");
impl_test!(_158456325029e18 : 1.58456325029e+29 == "158456325028528675187087900672");
impl_test!(_1_40129846432e_45 : 1.40129846432e-45 == "1.40129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125E-45");
impl_test!(_1_e42 : 1e-42 == "1.0005271035279193886395429224690001177341070264998322610345467546973108330377044694614596664905548095703125e-42");
impl_test!(_3_92E_n39 : 3.92E-39 == "3.91999933059456489828739575494312783522406115751507460249208160269472102366083987590172910131514072418212890625E-39");
impl_test!(_2_81341650018752E_n308 : 2.81341650018752E-308 == "0");
#[test]
fn case_f32_min() {
let n = f32::MIN;
let d = parse_from_f32(n);
assert_eq!(d, "-340282346638528859811704183484516925440".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_f32_max() {
let n = f32::MAX;
let d = parse_from_f32(n);
assert_eq!(d, "340282346638528859811704183484516925440".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_f32_epsilon() {
let n = f32::EPSILON;
let d = parse_from_f32(n);
assert_eq!(d, "1.1920928955078125E-7".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_f32_pi() {
let n = f32::consts::PI;
let d = parse_from_f32(n);
assert_eq!(d, "3.1415927410125732421875".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_nan() {
let n = f32::from_bits(0b01111111110000000000000000000000);
assert!(n.is_nan());
let d = parse_from_f32(n);
assert_eq!(d, "510423550381407695195061911147652317184".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_try_from_nan() {
let n = f32::NAN;
let d = try_parse_from_f32(n);
assert!(d.is_err());
}
#[test]
fn case_try_from_infinity() {
let n = f32::INFINITY;
let d = try_parse_from_f32(n);
assert!(d.is_err());
}
@@ -0,0 +1,107 @@
// tests for function bigdecimal::parsing::parse_from_f64
use paste::paste;
use stdlib::f64;
macro_rules! impl_test {
($input:literal == $expected:literal) => {
paste! { impl_test!( [< "_" $input >] : $input == $expected); }
};
($name:ident : bits:$input:literal => $expected:literal) => {
impl_test!($name : f64::from_bits($input) => $expected);
};
($name:ident : $input:literal == $expected:literal) => {
impl_test!($name : ($input as f64) => $expected);
};
($name:ident : $input:expr => $expected:literal) => {
paste! {
#[test]
fn [< case $name >]() {
let n = $input;
let d = parse_from_f64(n);
let expected: BigDecimal = $expected.parse().unwrap();
assert_eq!(d, expected);
}
#[test]
fn [< case_neg $name >]() {
let n = f64::from_bits($input.to_bits() | (1<<63));
let d = parse_from_f64(n);
let expected: BigDecimal = concat!("-", $expected).parse().unwrap();
assert_eq!(d, expected);
}
}
};
}
impl_test!(_0 : 0.0 == "0");
impl_test!(_1 : 1.0 == "1");
impl_test!(_2 : 2.0 == "2");
impl_test!(_3 : 3.0 == "3");
impl_test!(_5en1 : 0.5 == "0.5");
impl_test!(_25en2 : 0.25 == "0.25");
impl_test!(_1en1 : 0.1 == "0.1000000000000000055511151231257827021181583404541015625");
impl_test!(_1over3 : 0.333333333333333333333333333333 == "0.333333333333333314829616256247390992939472198486328125");
impl_test!(_pi : 3.141592653589793 == "3.141592653589793115997963468544185161590576171875");
impl_test!(_near_3 : 3.0000000000000004 == "3.000000000000000444089209850062616169452667236328125");
impl_test!(_8eneg306 : 8.544283616667655e-306 == "8.5442836166676545758745469881475846986178991076220674838778719735182619591847930738097459423424470941335996703553180065389909675214026779902482660710563190540056652827644969523715287333767167538014707594736533997824798692690142890189753467148541192574394234161821394612038920127719106177776787375705338074667624093006332620080979623387970617655687653904110103913103933178304212511707769987213793880764157458662751217010283883439888757033430556011326632895537144105152597427684695380215955244686097497705226475608085097617996058799189036784865947060736971859470127760066696392182317083388979882704968230500619384728741377732016919538675848783600526390429792978252568964346334556191024880163233082812954995600973750951114861484914086986464099027216434478759765625e-306");
impl_test!(_8e306 : 3e300 == "3000000000000000157514280765613260746113405743324477464747562346535407373966724587359114125241343592131113331498651634530827569706081291726934376554360120948545161602779727411213490701384364270178106859704912399835243357116902922640223958228340427483737776366460170528514347008416589160596378201620480");
impl_test!(_50 : 50. == "50");
impl_test!(_nanbits : bits:0b_0_11111111111_1000000000000000000000000000000000000000000000000001 => "269653970229347426076201969312749943170150807578117307259170330445749843759196293443300553362892619730839480672521111823337121537071529813188030913831084401350087805833926634314566788423582671529934053315387252306324360914392174188827078768228648633522131134987762597502339006422840407304422939101316534763520");
impl_test!(_3105036184601418e246 : bits:0b_0_11100000000_0000000000000000000000000000000000000000000000000000 => "3105036184601417870297958976925005110513772034233393222278104076052101905372753772661756817657292955900975461394262146412343160088229628782888574550082362278408909952041699811100530571263196889650525998387432937501785693707632115712");
impl_test!(_2_81341650018752E_308 : 2.81341650018752E-308 == "2.8134165001875198278759275525943498067505063001967969175506480744152639496835355462897889950138699429916690515722729976876607247658891051736045520063301219592298855232146428654590713004216312194773871772185068366206180596731958890086634117134422695105490626598276746331472433159429067991016548063113298957324839879447939977012897422163463450947345510093578791948321798481101505330952230105511530048812659083481787407026258844307461890753626327683153826358878159001221539330872743255707112001100520519610144879206546597846231715071742093092641158571855689231930930474890818690333095288369471228217443460522531282790309374378111440076317827545086535792316428407651758951233693496387904508572484340169054222573303301594335791590596740352481219815672375261783599853515625E-308");
#[test]
fn case_f64_min() {
let n = f64::MIN;
let d = parse_from_f64(n);
assert_eq!(d, "-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_f64_max() {
let n = f64::MAX;
let d = parse_from_f64(n);
assert_eq!(d, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_f64_epsilon() {
let n = f64::EPSILON;
let d = parse_from_f64(n);
assert_eq!(d, "2.220446049250313080847263336181640625e-16".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_f64_pi() {
let n = f64::consts::PI;
let d = parse_from_f64(n);
assert_eq!(d, "3.141592653589793115997963468544185161590576171875".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_nan() {
let n = f64::from_bits(0b0_11111111111_1000000000000000000000000000000000000000000000000000);
assert!(n.is_nan());
let d = parse_from_f64(n);
assert_eq!(d, "269653970229347386159395778618353710042696546841345985910145121736599013708251444699062715983611304031680170819807090036488184653221624933739271145959211186566651840137298227914453329401869141179179624428127508653257226023513694322210869665811240855745025766026879447359920868907719574457253034494436336205824".parse::<BigDecimal>().unwrap());
}
#[test]
fn case_try_from_nan() {
let n = f64::NAN;
let d = try_parse_from_f64(n);
assert!(d.is_err());
}
#[test]
fn case_try_from_infinity() {
let n = f64::INFINITY;
let d = try_parse_from_f64(n);
assert!(d.is_err());
}
+512
View File
@@ -0,0 +1,512 @@
//! Rounding structures and subroutines
use crate::*;
use crate::arithmetic::{add_carry, store_carry, extend_adding_with_carry};
use stdlib;
use stdlib::num::NonZeroU64;
// const DEFAULT_ROUNDING_MODE: RoundingMode = ${RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE} or HalfUp;
include!(concat!(env!("OUT_DIR"), "/default_rounding_mode.rs"));
/// Determines how to calculate the last digit of the number
///
/// Default rounding mode is `HalfEven`, overwritable at compile-time
/// by setting the environment-value `RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE`
/// to the name of the mode.
///
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum RoundingMode {
/// Always round away from zero
///
///
/// * 5.5 → 6.0
/// * 2.5 → 3.0
/// * 1.6 → 2.0
/// * 1.1 → 2.0
/// * -1.1 → -2.0
/// * -1.6 → -2.0
/// * -2.5 → -3.0
/// * -5.5 → -6.0
Up,
/// Always round towards zero
///
/// * 5.5 → 5.0
/// * 2.5 → 2.0
/// * 1.6 → 1.0
/// * 1.1 → 1.0
/// * -1.1 → -1.0
/// * -1.6 → -1.0
/// * -2.5 → -2.0
/// * -5.5 → -5.0
Down,
/// Towards +∞
///
/// * 5.5 → 6.0
/// * 2.5 → 3.0
/// * 1.6 → 2.0
/// * 1.1 → 2.0
/// * -1.1 → -1.0
/// * -1.6 → -1.0
/// * -2.5 → -2.0
/// * -5.5 → -5.0
Ceiling,
/// Towards -∞
///
/// * 5.5 → 5.0
/// * 2.5 → 2.0
/// * 1.6 → 1.0
/// * 1.1 → 1.0
/// * -1.1 → -2.0
/// * -1.6 → -2.0
/// * -2.5 → -3.0
/// * -5.5 → -6.0
Floor,
/// Round to 'nearest neighbor', or up if ending decimal is 5
///
/// * 5.5 → 6.0
/// * 2.5 → 3.0
/// * 1.6 → 2.0
/// * 1.1 → 1.0
/// * -1.1 → -1.0
/// * -1.6 → -2.0
/// * -2.5 → -3.0
/// * -5.5 → -6.0
HalfUp,
/// Round to 'nearest neighbor', or down if ending decimal is 5
///
/// * 5.5 → 5.0
/// * 2.5 → 2.0
/// * 1.6 → 2.0
/// * 1.1 → 1.0
/// * -1.1 → -1.0
/// * -1.6 → -2.0
/// * -2.5 → -2.0
/// * -5.5 → -5.0
HalfDown,
/// Round to 'nearest neighbor', if equidistant, round towards
/// nearest even digit
///
/// * 5.5 → 6.0
/// * 2.5 → 2.0
/// * 1.6 → 2.0
/// * 1.1 → 1.0
/// * -1.1 → -1.0
/// * -1.6 → -2.0
/// * -2.5 → -2.0
/// * -5.5 → -6.0
///
HalfEven,
}
impl RoundingMode {
/// Perform the rounding operation
///
/// Parameters
/// ----------
/// * sign (Sign) - Sign of the number to be rounded
/// * pair (u8, u8) - The two digits in question to be rounded.
/// i.e. to round 0.345 to two places, you would pass (4, 5).
/// As decimal digits, they
/// must be less than ten!
/// * trailing_zeros (bool) - True if all digits after the pair are zero.
/// This has an effect if the right hand digit is 0 or 5.
///
/// Returns
/// -------
/// Returns the first number of the pair, rounded. The sign is not preserved.
///
/// Examples
/// --------
/// - To round 2341, pass in `Plus, (4, 1), true` → get 4 or 5 depending on scheme
/// - To round -0.1051, to two places: `Minus, (0, 5), false` → returns either 0 or 1
/// - To round -0.1, pass in `true, (0, 1)` → returns either 0 or 1
///
/// Calculation of pair of digits from full number, and the replacement of that number
/// should be handled separately
///
pub fn round_pair(&self, sign: Sign, pair: (u8, u8), trailing_zeros: bool) -> u8 {
use self::RoundingMode::*;
use stdlib::cmp::Ordering::*;
let (lhs, rhs) = pair;
// if all zero after digit, never round
if rhs == 0 && trailing_zeros {
return lhs;
}
let up = lhs + 1;
let down = lhs;
match (*self, rhs.cmp(&5)) {
(Up, _) => up,
(Down, _) => down,
(Floor, _) => if sign == Sign::Minus { up } else { down },
(Ceiling, _) => if sign == Sign::Minus { down } else { up },
(_, Less) => down,
(_, Greater) => up,
(_, Equal) if !trailing_zeros => up,
(HalfUp, Equal) => up,
(HalfDown, Equal) => down,
(HalfEven, Equal) => if lhs % 2 == 0 { down } else { up },
}
}
/// Round digits, and if rounded up to 10, store 1 in carry and return zero
pub(crate) fn round_pair_with_carry(
&self,
sign: Sign,
pair: (u8, u8),
trailing_zeros: bool,
carry: &mut u8,
) -> u8 {
let r = self.round_pair(sign, pair, trailing_zeros);
store_carry(r, carry)
}
/// Round value at particular digit, returning replacement digit
///
/// Parameters
/// ----------
/// * at_digit (NonZeroU8) - 0-based index of digit at which to round.
/// 0 would be the first digit, and would
///
/// * sign (Sign) - Sign of the number to be rounded
/// * value (u32) - The number containing digits to be rounded.
/// * trailing_zeros (bool) - True if all digits after the value are zero.
///
/// Returns
/// -------
/// Returns the first number of the pair, rounded. The sign is not preserved.
///
/// Examples
/// --------
/// - To round 823418, at digit-index 3: `3, Plus, 823418, true` → 823000 or 824000, depending on scheme
/// - To round -100205, at digit-index 1: `1, Minus, 100205, true` → 100200 or 100210
///
/// Calculation of pair of digits from full number, and the replacement of that number
/// should be handled separately
///
pub fn round_u32(
&self,
at_digit: stdlib::num::NonZeroU8,
sign: Sign,
value: u32,
trailing_zeros: bool,
) -> u32 {
let shift = 10u32.pow(at_digit.get() as u32 - 1);
let splitter = shift * 10;
// split 'value' into high and low
let (top, bottom) = num_integer::div_rem(value, splitter);
let lhs = (top % 10) as u8;
let (rhs, remainder) = num_integer::div_rem(bottom, shift);
let pair = (lhs, rhs as u8);
let rounded = self.round_pair(sign, pair, trailing_zeros && remainder == 0);
// replace low digit with rounded value
let full = top - lhs as u32 + rounded as u32;
// shift rounded value back to position
full * splitter
}
/// Round the bigint to prec digits
pub(crate) fn round_bigint_to_prec(
self, n: num_bigint::BigInt, prec: NonZeroU64
) -> WithScale<num_bigint::BigInt> {
let (sign, mut biguint) = n.into_parts();
let ndrd = NonDigitRoundingData { mode: self, sign };
let ndigits = round_biguint_inplace(&mut biguint, prec, ndrd);
let result = BigInt::from_biguint(sign, biguint);
WithScale::from((result, -ndigits))
}
/// Hint used to skip calculating trailing_zeros if they don't matter
fn needs_trailing_zeros(&self, insig_digit: u8) -> bool {
use RoundingMode::*;
// only need trailing zeros if the rounding digit is 0 or 5
if matches!(self, HalfUp | HalfDown | HalfEven) {
insig_digit == 5
} else {
insig_digit == 0
}
}
}
/// Return compile-time constant default rounding mode
///
/// Defined by RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE at compile time
///
impl Default for RoundingMode {
fn default() -> Self {
DEFAULT_ROUNDING_MODE
}
}
/// All non-digit information required to round digits
///
/// Just the mode and the sign.
///
#[derive(Debug, Clone, Copy)]
pub(crate) struct NonDigitRoundingData {
/// Rounding mode
pub mode: RoundingMode,
/// Sign of digits
pub sign: Sign,
}
impl NonDigitRoundingData {
/// Round pair of digits, storing overflow (10) in the carry
pub fn round_pair(&self, pair: (u8, u8), trailing_zeros: bool) -> u8 {
self.mode.round_pair(self.sign, pair, trailing_zeros)
}
/// round-pair with carry-digits
pub fn round_pair_with_carry(&self, pair: (u8, u8), trailing_zeros: bool, carry: &mut u8) -> u8 {
self.mode.round_pair_with_carry(self.sign, pair, trailing_zeros, carry)
}
/// Use sign and default rounding mode
pub fn default_with_sign(sign: Sign) -> Self {
NonDigitRoundingData { sign, mode: RoundingMode::default() }
}
/// Round BigUint to requested precision, using mode and sign in self
///
/// Returns the biguint with at most 'prec' digits, and scale
/// indicating how many decimal digits were removed.
///
pub(crate) fn round_biguint_to_prec(
self, mut n: num_bigint::BigUint, prec: NonZeroU64
) -> WithScale<num_bigint::BigUint> {
let ndigits = round_biguint_inplace(&mut n, prec, self);
WithScale::from((n, -ndigits))
}
}
/// Relevant information about insignificant digits, used for rounding
///
/// If rounding at indicated point:
///
/// ```txt
/// aaaaizzzzzzzz
/// ^
/// ```
///
/// 'a' values are significant, 'i' is the insignificant digit,
/// and trailing_zeros is true if all 'z' are 0.
///
#[derive(Debug,Clone,Copy)]
pub(crate) struct InsigData {
/// highest insignificant digit
pub digit: u8,
/// true if all digits more insignificant than 'digit' is zero
///
/// This is only useful if relevant for the rounding mode, it
/// may be 'wrong' in these cases.
pub trailing_zeros: bool,
/// rounding-mode and sign
pub rounding_data: NonDigitRoundingData
}
#[allow(dead_code)]
impl InsigData {
/// Build from insig data and lazily calculated trailing-zeros callable
pub fn from_digit_and_lazy_trailing_zeros(
rounder: NonDigitRoundingData,
insig_digit: u8,
calc_trailing_zeros: impl FnOnce() -> bool
) -> Self {
Self {
digit: insig_digit,
trailing_zeros: rounder.mode.needs_trailing_zeros(insig_digit) && calc_trailing_zeros(),
rounding_data: rounder,
}
}
/// Build from slice of insignificant little-endian digits
pub fn from_digit_slice(rounder: NonDigitRoundingData, digits: &[u8]) -> Self {
match digits.split_last() {
Some((&d0, trailing)) => {
Self::from_digit_and_lazy_trailing_zeros(
rounder, d0, || trailing.iter().all(Zero::is_zero)
)
}
None => {
Self {
digit: 0,
trailing_zeros: true,
rounding_data: rounder,
}
}
}
}
/// from sum of overlapping digits, (a is longer than b)
pub fn from_overlapping_digits_backward_sum(
rounder: NonDigitRoundingData,
mut a_digits: stdlib::iter::Rev<stdlib::slice::Iter<u8>>,
mut b_digits: stdlib::iter::Rev<stdlib::slice::Iter<u8>>,
carry: &mut u8,
) -> Self {
debug_assert!(a_digits.len() >= b_digits.len());
debug_assert_eq!(carry, &0);
// most-significant insignificant digit
let insig_digit;
match (a_digits.next(), b_digits.next()) {
(Some(a), Some(b)) => {
// store 'full', initial sum, we will handle carry below
insig_digit = a + b;
}
(Some(d), None) | (None, Some(d)) => {
insig_digit = *d;
}
(None, None) => {
// both digit slices were empty; all zeros
return Self {
digit: 0,
trailing_zeros: true,
rounding_data: rounder,
};
}
};
// find first non-nine value
let mut sum = 9;
while sum == 9 {
let next_a = a_digits.next().unwrap_or(&0);
let next_b = b_digits.next().unwrap_or(&0);
sum = next_a + next_b;
}
// if previous sum was greater than ten,
// the one would carry through all the 9s
let sum = store_carry(sum, carry);
// propagate carry to the highest insignificant digit
let insig_digit = add_carry(insig_digit, carry);
// if the last 'sum' value isn't zero, or if any remaining
// digit is not zero, then it's not trailing zeros
let trailing_zeros = sum == 0
&& rounder.mode.needs_trailing_zeros(insig_digit)
&& a_digits.all(Zero::is_zero)
&& b_digits.all(Zero::is_zero);
Self {
digit: insig_digit,
trailing_zeros: trailing_zeros,
rounding_data: rounder,
}
}
pub fn round_digit(&self, digit: u8) -> u8 {
self.rounding_data.round_pair((digit, self.digit), self.trailing_zeros)
}
pub fn round_digit_with_carry(&self, digit: u8, carry: &mut u8) -> u8 {
self.rounding_data.round_pair_with_carry((digit, self.digit), self.trailing_zeros, carry)
}
pub fn round_slice_into(&self, dest: &mut Vec<u8>, digits: &[u8]) {
let (&d0, rest) = digits.split_first().unwrap_or((&0, &[]));
let digits = rest.iter().copied();
let mut carry = 0;
let r0 = self.round_digit_with_carry(d0, &mut carry);
dest.push(r0);
extend_adding_with_carry(dest, digits, &mut carry);
if !carry.is_zero() {
dest.push(carry);
}
}
#[allow(dead_code)]
pub fn round_slice_into_with_carry(&self, dest: &mut Vec<u8>, digits: &[u8], carry: &mut u8) {
let (&d0, rest) = digits.split_first().unwrap_or((&0, &[]));
let digits = rest.iter().copied();
let r0 = self.round_digit_with_carry(d0, carry);
dest.push(r0);
extend_adding_with_carry(dest, digits, carry);
}
}
/// Round BigUint n to 'prec' digits
fn round_biguint_inplace(
n: &mut num_bigint::BigUint,
prec: NonZeroU64,
rounder: NonDigitRoundingData,
) -> i64 {
use arithmetic::modulo::{mod_ten_2p64_le, mod_100_uint};
use arithmetic::decimal::count_digits_biguint;
let digit_count = count_digits_biguint(n);
let digits_to_remove = digit_count.saturating_sub(prec.get());
if digits_to_remove == 0 {
return 0;
}
if digits_to_remove == 1 {
let insig_digit = mod_ten_2p64_le(n.iter_u64_digits());
*n /= 10u8;
let sig_digit = mod_ten_2p64_le(n.iter_u64_digits());
let rounded_digit = rounder.round_pair((sig_digit, insig_digit), true);
*n += rounded_digit - sig_digit;
if rounded_digit != 10 {
return 1;
}
let digit_count = count_digits_biguint(n);
if digit_count == prec.get() {
return 1;
}
debug_assert_eq!(digit_count, prec.get() + 1);
*n /= 10u8;
return 2;
}
let shifter = ten_to_the_uint(digits_to_remove - 1);
let low_digits = &(*n) % &shifter;
let trailing_zeros = low_digits.is_zero();
*n /= &shifter;
let u = mod_100_uint(n);
let (sig_digit, insig_digit) = u.div_rem(&10);
let rounded_digit = rounder.round_pair((sig_digit, insig_digit), trailing_zeros);
*n /= 10u8;
*n += rounded_digit - sig_digit;
if rounded_digit != 10 {
return digits_to_remove as i64;
}
let digit_count = count_digits_biguint(n);
if digit_count == prec.get() {
return digits_to_remove as i64;
}
debug_assert_eq!(digit_count, prec.get() + 1);
// shift by another digit. Overflow means all significant
// digits were nines, so no need to re-round
*n /= 10u8;
return digits_to_remove as i64 + 1;
}
#[cfg(test)]
include!("rounding.tests.rs");
+365
View File
@@ -0,0 +1,365 @@
#[allow(non_snake_case)]
mod test_round_pair {
use paste::paste;
use super::*;
macro_rules! impl_test {
( $($mode:ident),+ => $expected:literal) => {
$(
paste! {
#[test]
fn [< mode_ $mode >]() {
let (pair, sign, trailing_zeros) = test_input();
let mode = self::RoundingMode::$mode;
let result = mode.round_pair(sign, pair, trailing_zeros);
assert_eq!(result, $expected);
}
}
)*
}
}
macro_rules! define_test_input {
( - $lhs:literal . $rhs:literal $($t:tt)* ) => {
define_test_input!(sign=Sign::Minus, pair=($lhs, $rhs), $($t)*);
};
( $lhs:literal . $rhs:literal $($t:tt)*) => {
define_test_input!(sign=Sign::Plus, pair=($lhs, $rhs), $($t)*);
};
( sign=$sign:expr, pair=$pair:expr, ) => {
define_test_input!(sign=$sign, pair=$pair, trailing_zeros=true);
};
( sign=$sign:expr, pair=$pair:expr, 000x ) => {
define_test_input!(sign=$sign, pair=$pair, trailing_zeros=false);
};
( sign=$sign:expr, pair=$pair:expr, trailing_zeros=$trailing_zeros:literal ) => {
fn test_input() -> ((u8, u8), Sign, bool) { ($pair, $sign, $trailing_zeros) }
};
}
mod case_0_1 {
use super::*;
define_test_input!(0 . 1);
impl_test!(Up, Ceiling => 1);
impl_test!(Down, Floor, HalfUp, HalfDown, HalfEven => 0);
}
mod case_neg_0_1 {
use super::*;
define_test_input!(-0 . 1);
impl_test!(Up, Floor => 1);
impl_test!(Down, Ceiling, HalfUp, HalfDown, HalfEven => 0);
}
mod case_0_5 {
use super::*;
define_test_input!( 0 . 5 );
impl_test!(Up, Ceiling, HalfUp => 1);
impl_test!(Down, Floor, HalfDown, HalfEven => 0);
}
mod case_neg_0_5 {
use super::*;
define_test_input!(-0 . 5);
impl_test!(Up, Floor, HalfUp => 1);
impl_test!(Down, Ceiling, HalfDown, HalfEven => 0);
}
mod case_0_5_000x {
use super::*;
// ...000x indicates a non-zero trailing digit; affects behavior of rounding N.0 and N.5
define_test_input!(0 . 5 000x);
impl_test!(Up, Ceiling, HalfUp, HalfDown, HalfEven => 1);
impl_test!(Down, Floor => 0);
}
mod case_neg_0_5_000x {
use super::*;
define_test_input!(-0 . 5 000x);
impl_test!(Up, Floor, HalfUp, HalfDown, HalfEven => 1);
impl_test!(Down, Ceiling => 0);
}
mod case_0_7 {
use super::*;
define_test_input!(0 . 7);
impl_test!(Up, Ceiling, HalfUp, HalfDown, HalfEven => 1);
impl_test!(Down, Floor => 0);
}
mod case_neg_0_7 {
use super::*;
define_test_input!(-0 . 7);
impl_test!(Up, Floor, HalfUp, HalfDown, HalfEven => 1);
impl_test!(Down, Ceiling => 0);
}
mod case_neg_4_3_000x {
use super::*;
define_test_input!(-4 . 3 000x);
impl_test!(Up, Floor => 5);
impl_test!(Down, Ceiling, HalfUp, HalfDown, HalfEven => 4);
}
mod case_9_5_000x {
use super::*;
define_test_input!(9 . 5 000x);
impl_test!(Up, Ceiling, HalfDown, HalfUp, HalfEven => 10);
impl_test!(Down, Floor => 9);
}
mod case_9_5 {
use super::*;
define_test_input!(9 . 5);
impl_test!(Up, Ceiling, HalfUp, HalfEven => 10);
impl_test!(Down, Floor, HalfDown => 9);
}
mod case_8_5 {
use super::*;
define_test_input!(8 . 5);
impl_test!(Up, Ceiling, HalfUp => 9);
impl_test!(Down, Floor, HalfDown, HalfEven => 8);
}
mod case_neg_6_5 {
use super::*;
define_test_input!(-6 . 5);
impl_test!(Up, Floor, HalfUp => 7);
impl_test!(Down, Ceiling, HalfDown, HalfEven => 6);
}
mod case_neg_6_5_000x {
use super::*;
define_test_input!(-6 . 5 000x);
impl_test!(Up, Floor, HalfUp, HalfDown, HalfEven => 7);
impl_test!(Down, Ceiling => 6);
}
mod case_3_0 {
use super::*;
define_test_input!(3 . 0);
impl_test!(Up, Down, Ceiling, Floor, HalfUp, HalfDown, HalfEven => 3);
}
mod case_3_0_000x {
use super::*;
define_test_input!(3 . 0 000x);
impl_test!(Up, Ceiling => 4);
impl_test!(Down, Floor, HalfUp, HalfDown, HalfEven => 3);
}
mod case_neg_2_0 {
use super::*;
define_test_input!(-2 . 0);
impl_test!(Up, Down, Ceiling, Floor, HalfUp, HalfDown, HalfEven => 2);
}
mod case_neg_2_0_000x {
use super::*;
define_test_input!(-2 . 0 000x);
impl_test!(Up, Floor => 3);
impl_test!(Down, Ceiling, HalfUp, HalfDown, HalfEven => 2);
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod test_round_u32 {
use paste::paste;
use super::*;
macro_rules! impl_test {
( $pos:literal :: $($mode:ident),+ => $expected:literal) => {
$(
paste! {
#[test]
fn [< digit_ $pos _mode_ $mode >]() {
let (value, sign, trailing_zeros) = test_input();
let mode = self::RoundingMode::$mode;
let pos = stdlib::num::NonZeroU8::new($pos as u8).unwrap();
let result = mode.round_u32(pos, sign, value, trailing_zeros);
assert_eq!(result, $expected);
}
}
)*
}
}
macro_rules! define_test_input {
( - $value:literal $($t:tt)* ) => {
define_test_input!(sign=Sign::Minus, value=$value $($t)*);
};
( $value:literal $($t:tt)* ) => {
define_test_input!(sign=Sign::Plus, value=$value $($t)*);
};
( sign=$sign:expr, value=$value:literal ...000x ) => {
define_test_input!(sign=$sign, value=$value, trailing_zeros=false);
};
( sign=$sign:expr, value=$value:literal ) => {
define_test_input!(sign=$sign, value=$value, trailing_zeros=true);
};
( sign=$sign:expr, value=$value:expr, trailing_zeros=$trailing_zeros:literal ) => {
fn test_input() -> (u32, Sign, bool) { ($value, $sign, $trailing_zeros) }
};
}
mod case_13950000 {
use super::*;
define_test_input!(13950000);
impl_test!(3 :: Up => 13950000);
impl_test!(5 :: Up, Ceiling, HalfUp, HalfEven => 14000000);
impl_test!(5 :: Down, HalfDown => 13900000);
}
mod case_neg_35488622_000x {
use super::*;
// ...000x indicates non-zero trailing digit
define_test_input!(-35488622 ...000x);
impl_test!(1 :: Up => 35488630);
impl_test!(1 :: Down => 35488620);
impl_test!(2 :: Up => 35488700);
impl_test!(2 :: Down => 35488600);
impl_test!(7 :: Up, Floor => 40000000);
impl_test!(7 :: Down, Ceiling => 30000000);
impl_test!(8 :: Up => 100000000);
impl_test!(8 :: Down => 0);
}
}
#[allow(non_snake_case)]
mod test_round_biguint_inplace {
use super::*;
use paste::paste;
macro_rules! impl_test {
(IMPL; $prec:literal, $mode:ident => $expected:literal, $exp:literal) => {
paste! {
#[test]
fn [< prec $prec _mode_ $mode >]() {
let mut n = test_input();
let rounder = NonDigitRoundingData {
mode: self::RoundingMode::$mode,
sign: Sign::Plus,
};
let prec = NonZeroU64::new($prec).unwrap();
let result = round_biguint_inplace(&mut n, prec, rounder);
let expected: BigUint = $expected.parse().unwrap();
assert_eq!(&expected, &n);
assert_eq!($exp, result);
}
}
};
($prec:literal: $($mode:ident),+ => $expected:literal E $exp:literal) => {
$( impl_test!(IMPL; $prec, $mode => $expected, $exp); )*
};
($prec:literal: $($($mode:ident),+ => $expected:literal E $exp:literal),+ ) => {
$( impl_test!($prec: $($mode),* => $expected E $exp); )*
};
}
mod case_14414008876e200 {
use super::*;
fn test_input() -> BigUint {
"1441400887642029388383856645559897315584165538408309505171135258930500598075662511441702498467332463786833300335785089981027498174604649257434184055851638492438374046355824930138046686225938550901559011829587689".parse().unwrap()
}
impl_test!(15: Up => "144140088764203" E 196,
Down => "144140088764202" E 196);
impl_test!(11: Down,
HalfEven => "14414008876" E 200,
Up => "14414008877" E 200);
impl_test!(1: Down,
HalfEven => "1" E 210,
Up => "2" E 210);
}
mod case_99191529355902971933631238546419824884699 {
use super::*;
fn test_input() -> BigUint {
"99191529355902971933631238546419824884699".parse().unwrap()
}
impl_test!(42: Down, Up => "99191529355902971933631238546419824884699" E 0);
impl_test!(41: Down,
Up,
HalfEven => "99191529355902971933631238546419824884699" E 0);
impl_test!(40: Up => "9919152935590297193363123854641982488470" E 1,
Down => "9919152935590297193363123854641982488469" E 1);
impl_test!(13: Up => "9919152935591" E 28,
Down => "9919152935590" E 28);
impl_test!(4: Up => "9920" E 37,
Down => "9919" E 37);
impl_test!(3: Up => "992" E 38,
Down => "991" E 38);
impl_test!(2: Up => "10" E 40,
Down => "99" E 39);
impl_test!(1: Up => "1" E 41,
Down => "9" E 40);
}
mod case_99 {
use super::*;
fn test_input() -> BigUint {
"99".parse().unwrap()
}
impl_test!(2: Up => "99" E 0,
Down => "99" E 0);
impl_test!(1: Up => "1" E 2,
Down => "9" E 1);
}
}
+31
View File
@@ -0,0 +1,31 @@
// Wrap std:: modules in namespace
#[allow(unused_imports)]
mod stdlib {
pub use std::{
cmp,
convert,
default,
fmt,
hash,
marker,
mem,
num,
ops,
iter,
slice,
str,
string,
f32,
f64,
};
#[cfg(test)]
pub use std::collections::hash_map::DefaultHasher;
pub use std::borrow;
pub use std::boxed::{self, Box};
pub use std::vec::{self, Vec};
}
+42
View File
@@ -0,0 +1,42 @@
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
#[cfg(test)]
extern crate siphasher;
// Without this import we get the following error:
// error[E0599]: no method naemed `powi` found for type `f64` in the current scope
#[allow(unused_imports)]
use num_traits::float::FloatCore;
// Wrap core:: modules in namespace
#[allow(unused_imports)]
mod stdlib {
pub use core::{
cmp,
convert,
default,
fmt,
hash,
marker,
mem,
num,
ops,
iter,
slice,
str,
i8,
f32,
f64,
};
#[cfg(test)]
pub use siphasher::sip::SipHasher as DefaultHasher;
pub use alloc::borrow;
pub use alloc::string;
pub use alloc::boxed::{self, Box};
pub use alloc::vec::{self, Vec};
}