Lossless fractions and decimals for Rust; an aspirational drop-in replacement for floating-point types.
fraction provides:
Fraction, a rational type designed for float-like arithmetic with exact valuesDecimal, a lossless decimal representation with explicit precisionDynaInt, a dynamically growing integer for checked maths- hashable and orderable fractions, including deterministic ordering for
NaN - PostgreSQL, Juniper, Serde, Unicode, and approximate maths support via features
[dependencies]
fraction = "0.16.0"Enable optional integrations explicitly:
[dependencies]
fraction = { version = "0.16.0", features = ["with-postgres-support", "with-serde-support"] }The default feature set enables the features marked Yes below.
| Feature | Default? | Overview |
|---|---|---|
with-bigint |
Yes | Adds num::BigInt/BigUint support, re-exports those types, and enables aliases such as BigFraction. |
with-decimal |
Yes | Adds GenericDecimal and decimal aliases such as Decimal, with precision retained for formatting and comparison. |
with-dynaint |
Yes | Adds DynaInt, which keeps small integers inline and promotes them to a larger backing type on overflow. |
with-approx |
No | Enables with-bigint and adds accuracy-controlled square-root helpers for fractions and, when enabled, decimals. |
with-juniper-support |
No | Adds Juniper GraphQL scalar, input, and output implementations for the enabled fraction and decimal types. |
with-postgres-support |
No | Adds PostgreSQL NUMERIC ToSql/FromSql conversions for the enabled fraction and decimal types. |
with-serde-support |
No | Adds Serde Serialize/Deserialize implementations for enabled fraction, decimal, sign, and dynamic-integer types. |
with-unicode |
No | Adds Unicode fraction formatting and parsing helpers for GenericFraction. |
MSRV note: Rust 1.70 is the declared minimum and is CI-tested for the with-bigint/with-approx surface exercised by
the MSRV smoke harness. Latest stable is tested with all features enabled. Optional integration dependency graphs,
including with-juniper-support and with-postgres-support, may require a newer compiler under fresh dependency
resolution.
Unlike primitive floats, Fraction treats NaN as equal to itself and orders it below negative infinity. That makes
fractions usable in sets, hash maps, and B-trees.
use std::str::FromStr;
use fraction::{Fraction, One, Zero, Sign};
let f = Fraction::new(1u8, 2u8);
assert_eq!(f, Fraction::new_generic(Sign::Plus, 1i32, 2u8).unwrap());
assert_eq!(f, Fraction::from(0.5));
assert_eq!(f, Fraction::from_str("0.5").unwrap());
assert_eq!(f, Fraction::from_str("1/2").unwrap());
assert_eq!(Fraction::from_str("1/0").unwrap(), Fraction::infinity());
assert_eq!(Fraction::from_str("-1/0").unwrap(), Fraction::neg_infinity());
assert_eq!(Fraction::from_str("0/0").unwrap(), Fraction::nan());
assert_eq!(f * 2, Fraction::one());
assert_eq!(f - f, Fraction::zero());use std::str::FromStr;
use fraction::{Decimal, Fraction};
let d = Decimal::from(1);
assert_eq!(d, Decimal::from_fraction(Fraction::from(1)));
let d = Decimal::from(1.3);
assert_eq!(d, Decimal::from_str("1.3").unwrap());
let d = Decimal::from(0.5);
// Decimal fraction notation uses the same zero-denominator mapping as `Fraction::from_str`.
assert_eq!(d, Decimal::from_str("1/2").unwrap());
assert_eq!(Decimal::from_str("1/0").unwrap(), Decimal::infinity());
assert_eq!(Decimal::from_str("-1/0").unwrap(), Decimal::neg_infinity());
assert_eq!(Decimal::from_str("0/0").unwrap(), Decimal::nan());
let one_third = Fraction::new(1u8, 3u8);
assert_eq!(
Decimal::from_fraction_with_precision(one_third, 4).to_string(),
"0.3333"
);
let lhs = Decimal::from(0.5) / Decimal::from(0.3); // 5/3, precision 1
let rhs = Decimal::from_str("1.6").unwrap(); // 8/5, precision 1
assert_eq!(lhs, rhs);
assert_eq!(lhs.partial_cmp(&rhs), Some(std::cmp::Ordering::Equal));Decimals compare by truncating fractional digits to each value’s own precision.
Trailing zeroes are ignored, p0 drops all fractional digits, and canonical zero values
such as -0, -0.9@p0, and -0.04@p1 compare and hash as positive zero.
type F = fraction::Fraction;
let result = F::from(0.7) / F::from(0.4);
assert_eq!(format!("{}", result), "7/4");
assert_eq!(format!("{:.2}", result), "1.75");
assert_eq!(format!("{:#.3}", result), "1.750");When with-unicode is enabled, Unicode display helpers are available.
#[cfg(feature = "with-unicode")]
{
type F = fraction::Fraction;
let res = F::from(0.7) / F::from(0.4);
assert_eq!("7⁄4", format!("{}", res.get_unicode_display()));
assert_eq!("⁷/₄", format!("{}", res.get_unicode_display().supsub()));
}Use Decimal rather than Fraction for PostgreSQL work where possible. PostgreSQL’s binary protocol uses i16, so the
base type for GenericFraction or GenericDecimal should be at least u16.
For very large or repeating values such as 1/3 or 1/7, Fraction can grow to 16383 digits after the decimal point,
which is slower than an explicitly precision-bound Decimal. If you need dynamic growth, DynaInt<u8, _> or
DynaInt<usize, BigUint> can help.
Infinity values round-trip using PostgreSQL numeric infinity markers from PostgreSQL 14+ (0xD000 and 0xF000).
Older PostgreSQL versions do not support these values, and constrained NUMERIC columns (with declared
precision/scale limits) reject infinity as out of range.
Licensed under either of: