Skip to main content

Crate decimal_bytes

Crate decimal_bytes 

Source
Expand description

§decimal-bytes

Arbitrary precision decimals with lexicographically sortable byte encoding.

This crate provides three decimal types optimized for database storage:

  • Decimal: Variable-length arbitrary precision (up to 131,072 digits)
  • Decimal64: Fixed 8-byte representation with embedded scale (precision ≤ 16 digits)
  • Decimal64NoScale: Fixed 8-byte representation with external scale (precision ≤ 18 digits)

All types support PostgreSQL special values (NaN, ±Infinity) with correct sort ordering.

§When to Use Which

TypePrecisionScaleStorageBest For
Decimal64NoScale≤ 18 digitsExternal8 bytesColumnar storage, aggregates
Decimal64≤ 16 digitsEmbedded8 bytesSelf-contained values
DecimalUnlimitedUnlimitedVariableScientific, very large numbers
use decimal_bytes::Decimal64NoScale;

// Scale is provided externally (e.g., from schema metadata)
let scale = 2;
let a = Decimal64NoScale::new("100.50", scale).unwrap();
let b = Decimal64NoScale::new("200.25", scale).unwrap();

// Aggregates work correctly - just sum the raw i64 values!
let sum = a.value() + b.value();  // 30075
let result = Decimal64NoScale::from_raw(sum);
assert_eq!(result.to_string_with_scale(scale), "300.75");

§Decimal64 Example

use decimal_bytes::Decimal64;

// Create with embedded scale
let price = Decimal64::new("99.99", 2).unwrap();
assert_eq!(price.to_string(), "99.99");
assert_eq!(price.scale(), 2);  // Scale is embedded!

// With precision and scale (PostgreSQL NUMERIC semantics)
let d = Decimal64::with_precision_scale("123.456", Some(5), Some(2)).unwrap();
assert_eq!(d.to_string(), "123.46"); // Rounded

// Parse with automatic scale detection
let d: Decimal64 = "123.456".parse().unwrap();
assert_eq!(d.scale(), 3);

// Special values
let inf = Decimal64::infinity();
let nan = Decimal64::nan();
assert!(price < inf);
assert!(inf < nan);

§Decimal Example (Arbitrary Precision)

use decimal_bytes::Decimal;
use std::str::FromStr;

// Create decimals
let a = Decimal::from_str("123.456").unwrap();
let b = Decimal::from_str("123.457").unwrap();

// Byte comparison matches numerical comparison
assert!(a.as_bytes() < b.as_bytes());
assert!(a < b);

// Special values (PostgreSQL compatible)
let inf = Decimal::infinity();
let nan = Decimal::nan();
assert!(a < inf);
assert!(inf < nan);

§Sort Order

The lexicographic byte order matches PostgreSQL NUMERIC:

-Infinity < negative numbers < zero < positive numbers < +Infinity < NaN

Both Decimal and Decimal64 support this sort order including special values.

§Special Value Semantics (PostgreSQL vs IEEE 754)

The Decimal type follows PostgreSQL semantics for special values:

BehaviorPostgreSQL / decimal-bytesIEEE 754 float
NaN == NaNtruefalse
NaN orderingGreatest value (> Infinity)Unordered
Infinity == Infinitytruetrue
use decimal_bytes::Decimal;

let nan1 = Decimal::nan();
let nan2 = Decimal::nan();
let inf = Decimal::infinity();

// NaN equals itself (PostgreSQL behavior, unlike IEEE 754)
assert_eq!(nan1, nan2);

// NaN is greater than everything, including Infinity
assert!(nan1 > inf);

This makes Decimal suitable for use in indexes, sorting, and deduplication where consistent ordering and equality semantics are required.

Structs§

Decimal
An arbitrary precision decimal number stored as sortable bytes.
Decimal64
A fixed-precision decimal stored as a 64-bit integer with embedded scale.
Decimal64NoScale
A fixed-precision decimal stored as a raw 64-bit integer.

Enums§

DecimalError
Errors that can occur during decimal encoding/decoding.
SpecialValue
Special decimal values (IEEE 754 / PostgreSQL compatible)

Constants§

MAX_DECIMAL64_NO_SCALE_PRECISION
Maximum precision that fits in signed i64 (18 digits). i64::MAX = 9,223,372,036,854,775,807 ≈ 9.2 × 10^18
MAX_DECIMAL64_NO_SCALE_SCALE
Maximum scale supported.
MAX_DECIMAL64_PRECISION
Maximum precision that fits in 56-bit value (16 digits).
MAX_DECIMAL64_SCALE
Maximum scale supported (0-18).