Crates.io | bigdecimal |
lib.rs | bigdecimal |
version | 0.4.6 |
source | src |
created_at | 2017-04-05 23:48:11.922233 |
updated_at | 2024-10-28 05:23:22.1991 |
description | Arbitrary precision decimal numbers |
homepage | https://github.com/akubera/bigdecimal-rs |
repository | https://github.com/akubera/bigdecimal-rs |
max_upload_size | |
id | 9751 |
size | 375,957 |
Arbitrary-precision decimal numbers implemented in pure Rust.
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.
Add bigdecimal as a dependency to your Cargo.toml
file:
[dependencies]
bigdecimal = "0.4"
Import and use the BigDecimal
struct to solve your problems:
use bigdecimal::BigDecimal;
fn main() {
let two = BigDecimal::from(2);
println!("sqrt(2) = {}", two.sqrt().unwrap());
}
this code will print
sqrt(2) = 1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573
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:
[dependencies]
bigdecimal = { version = "0.4", features = [ "serde-json" ] } # '-' not '_'
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.
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
RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD
1.23e-3
will print as 0.00123
but 1.23e-10
will be 1.23E-10
1e1
would be rendered as 10
RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD
1e15
=> 1000000000000000
1e50000000
) will print in scientific notation, not
a 1 followed by fifty million zeros{:.<PREC>}
- Display with precision
PREC
digits after the decimal placeRUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING
{:e}
/ {:E}
- Exponential format
e
or E
as exponent delimiter{:.<PREC>e}
- formats in scientific notation, keeping number
{:?}
- Debug
123.456
=> BigDecimal(sign=Plus, scale=3, digits=[123456])
-1e10000
=> BigDecimal(sign=Minus, scale=-10000, digits=[1])
{:#?}
- Alternate Debug (used by dbg!()
)
123.456
=> BigDecimal("123456e-3")
-1e10000
=> BigDecimal("-1e10000")
There is a formatting-example 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.
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).
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.
The default Context uses this value for rounding. Valid values are the variants of the RoundingMode enum.
Defaults to HalfEven
.
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!
Given the program:
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.
This repository contains code originally meant for a bigdecimal module in the popular num crate, but was not merged due to uncertainty of what the best design for such a crate should be.
This code is dual-licensed under the permissive MIT & Apache 2.0 licenses.
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.