| Crates.io | uninum |
| lib.rs | uninum |
| version | 0.1.1 |
| created_at | 2025-12-13 17:31:35.765566+00 |
| updated_at | 2025-12-13 17:31:35.765566+00 |
| description | A robust, ergonomic unified number type for Rust with automatic overflow handling, type promotion, and cross-type consistency. |
| homepage | |
| repository | https://github.com/Synext-Solution/uninum |
| max_upload_size | |
| id | 1983222 |
| size | 728,014 |
A robust, ergonomic, and unified numeric type for Rust with automatic overflow handling, type promotion, and cross-type consistency.
uninum provides a single Number enum that safely encapsulates all standard numeric types, allowing you to perform arithmetic and comparisons without boilerplate conversions, overflow panics, or floating-point precision surprises.
Want a feature-by-feature walkthrough? Check out the Quick Reference for an in-depth tour of the API surface, conversion paths, and integration tips.
uninum?Working with different numeric types in Rust can be verbose and error-prone. You have to handle conversions, worry about integer overflow, and be mindful of floating-point inaccuracies. uninum solves these problems by providing:
Infinity or NaN.decimal feature is enabled, floating-point literals and inexact divisions are automatically converted to a high-precision decimal type, preventing common floating-point errors in financial and scientific calculations.Decimal, then F64) so work keeps flowing.| Variant | Precision | Ideal Use Cases |
|---|---|---|
Decimal |
Exact (≈28 digits) | Finance, billing, human-facing decimal math |
F64 |
~15–17 digits | Scientific/engineering work, extremely large/small values, NaN/∞ |
I64 / U64 |
Exact within range | Counting, IDs, fast integer-heavy logic |
num!: A powerful macro that creates the optimal Number variant from literals (e.g., num!(3.14) becomes a high-precision Decimal).&num + 5, 2.5 * &num, and num == 10 work just as you'd expect.u64::MAX + 1 gracefully promotes to a Decimal or F64 instead of panicking.decimal feature for arbitrary-precision decimal calculations, perfect for financial applications.Float64 wrapper allows floating-point numbers (including NaN) to be used in BTreeMaps and HashMaps with consistent, predictable behavior.serde feature emits a tagged JSON representation so variants, decimal scale, and non-finite floats (NaN, Infinity) round-trip without loss.Add uninum to your Cargo.toml:
[dependencies]
uninum = "0.1.0"
uninum follows a minimum supported Rust version (MSRV) of 1.88.0. The
crate is tested against that toolchain in CI; newer stable compilers work as
well.
For high-precision decimal, serde support, or bitwise operations, enable the corresponding features:
[dependencies]
uninum = { version = "0.1.0", features = ["decimal", "serde", "bitwise"] }
uninum is developed and maintained exclusively by Synext Solution Sdn. Bhd.
Bug reports, feature ideas, and general feedback are welcome through the issue
tracker. However, Synext Solution retains sole discretion over the roadmap and
the official code base: external pull requests are not accepted, and any changes
must be coordinated through the maintainers. You are, of course, free to fork
the project under the dual MIT/Apache-2.0 license if you need customisations.
use uninum::{num, Number};
fn main() {
// The `num!` macro creates the best representation for a literal.
let integer = num!(100); // Stored as an integer
let float = num!(3.14); // Stored as a Decimal (if `decimal` feature is on)
let large_int = num!(u64::MAX);
// --- Ergonomic Operations ---
// All operations work with references, avoiding unnecessary moves.
let result1 = &integer + 50; // Number + i32
let result2 = 2.5 * &integer; // f64 * Number
let result3 = &integer + &float; // Number + Number
println!("100 + 50 = {}", result1);
println!("2.5 * 100 = {}", result2);
println!("100 + 3.14 = {}", result3);
// --- Safety and Precision ---
// No panics! Overflow promotes to a higher-precision type.
let overflow_result = large_int + 1;
println!("u64::MAX + 1 = {}", overflow_result); // No panic!
// Inexact division is handled with precision.
let division = num!(10) / num!(3);
println!("10 / 3 = {}", division); // e.g., "3.33333..." as a Decimal
// --- Comparisons ---
assert!(&integer == 100);
assert!(&float < 4);
assert!(num!(0.1) + num!(0.2) == num!(0.3)); // True if `decimal` feature is enabled!
}
All arithmetic and comparison operators are implemented for both owned Numbers and references &Number. To avoid consuming your variables, always use references (&) for operations.
let num = num!(10);
// Using a reference (&num) - `num` can be reused.
let result1 = &num + 5;
let result2 = &num * 2; // `num` is still available.
println!("Original num is still: {}", num);
// Using an owned value (num) - `num` is moved and consumed.
let result3 = num + 1;
// println!("{}", num); // ❌ This would be a compile-time error.
num! MacroThe num! macro is the recommended way to create Number instances from literals. It intelligently parses the literal to choose the best internal representation:
num!(42), num!(-100) become Number::from(42i64), etc.num!(3.14), num!(1e-5) are parsed as strings to preserve full precision, creating a Decimal if the decimal feature is enabled, or an F64 otherwise. This avoids the inherent imprecision of f64 literals.num!(my_var) is a convenient shorthand for Number::from(my_var).uninum deliberately deviates from the raw IEEE semantics to provide consistent
behaviour across the entire type family:
NaN Equality: all NaN values compare equal to each other. This makes
equality checks across mixed numeric types deterministic (Number::F64(NaN) == Number::Decimal(NaN) evaluates to true).+0 and -0 are treated as the same value for
both equality and ordering comparisons.NaN is equal to itself and considered greater than any finite number,
while infinities follow the expected ordering.uninum is designed to be safe and predictable.
u64::MAX + 1), the operation is re-run after promoting the numbers to a higher-precision type (Decimal or F64).finite / 0 -> +Infinity or -Infinity0 / 0 -> NaNInfinity / Infinity -> NaNNaN values compare equal to each other, and +0 / -0 are treated as the
same value for equality and ordering. Total ordering remains well-defined even
in the presence of infinities or NaNs.decimal featureStandard f64 types are prone to precision errors (e.g., 0.1 + 0.2 != 0.3). The decimal feature (enabled by default) solves this by using the rust_decimal crate for arbitrary-precision decimal arithmetic.
// With the `decimal` feature enabled:
// num!(0.1) creates a precise Decimal, not an approximate f64.
assert!(num!(0.1) + num!(0.2) == num!(0.3)); // This is true!
// Without the `decimal` feature, this would fall back to f64 and fail.
let f64_sum = Number::from(0.1_f64) + Number::from(0.2_f64);
assert!(f64_sum != Number::from(0.3_f64)); // Standard f64 imprecision.
uninum uses feature flags to keep the core library lightweight. With
default-features = false the library has no runtime dependencies; every
extra crate is opt-in so you can control the footprint precisely.
| Feature | Default | Adds Dependency | Description |
|---|---|---|---|
decimal |
Yes | rust_decimal |
Enables high-precision decimal arithmetic using the rust_decimal crate. Highly recommended for financial or scientific applications. |
serde |
No | serde, ryu |
Enables serialization and deserialization support via the serde crate. |
bitwise |
No | (none) | Enables bitwise operations (&, ^, !, <<, >>) for integer variants of Number. |
To use uninum without default features:
[dependencies]
uninum = { version = "0.1.0", default-features = false }
💡 Dependency policy: new dependencies must be justified and introduced as optional features whenever possible. The default configuration should remain as lean as possible so downstream users can rely on a zero-dependency core.
This project is licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work shall be dual licensed as above, without any additional terms or conditions.
We welcome bug reports and feature discussions via the issue tracker, but code changes are handled solely by the Synext maintainer team. See the Contributing Guidelines for details on how to share feedback or follow the internal release workflow.