Expand description
Type-safe scalar wrappers for numerical tolerances and constrained real values.
This module provides strongly-typed wrappers around primitive scalar types to prevent value confusion and enforce mathematical constraints at compile time. These types are fundamental building blocks for numerical computations that require validated tolerances and constrained real number values.
§Overview
The module provides five primary types:
| Type | Constraint | Zero Valid? | Use Cases |
|---|---|---|---|
AbsoluteTolerance<T> | ≥ 0 | ✅ Yes | Fixed error bounds for comparisons |
RelativeTolerance<T> | ≥ 0 | ✅ Yes | Proportional error bounds |
PositiveRealScalar<T> | > 0 | ❌ No | Lengths, positive quantities |
NonNegativeRealScalar<T> | ≥ 0 | ✅ Yes | Distances, magnitudes, absolute values |
HashableScalar<T> | Finite | N/A | Hash-map and hash-set keys for real or complex scalars |
AbsoluteTolerance<T>, RelativeTolerance<T>, PositiveRealScalar<T>, and
NonNegativeRealScalar<T> are generic over RealScalar, enabling consistent validation
across different numerical backends (native f64, arbitrary-precision rug, etc.).
HashableScalar<T> is generic over FpScalar and supports both real and complex scalars.
§Architectural Design
- Tolerance Types (
AbsoluteTolerance<T>,RelativeTolerance<T>): Semantically specialized wrappers built on top ofNonNegativeRealScalar<T>. They inherit all validation and provide tolerance-specific methods likeRelativeTolerance::absolute_tolerance(). - Constrained Scalar Types (
PositiveRealScalar<T>,NonNegativeRealScalar<T>): Full-featured types with algebraic operations (addition, multiplication, powers), comparison traits, and immutable access patterns. They form the foundation for general mathematical computations. - Hashable Scalars (
HashableScalar<T>): Finite scalar wrappers that provideEqandHashfor use as keys instd::collections::HashMapand members ofstd::collections::HashSet. - Comparative Utilities: The
approximately_equal()function provides precise, tolerance-aware value comparisons across all scalar types.
§Design Philosophy
§Type Safety Through Distinct Types
Rather than using raw primitives like f64 directly, this module provides
semantically meaningful types that encode mathematical constraints:
use num_valid::{
backends::native::validated::RealNative64StrictFinite, RealScalar,
scalars::{AbsoluteTolerance, RelativeTolerance, PositiveRealScalar},
};
use try_create::TryNew;
// These are different types that cannot be confused:
let abs_tol = AbsoluteTolerance::try_new(
RealNative64StrictFinite::try_new(1e-10_f64).unwrap()
).unwrap(); // For absolute comparisons
let rel_tol = RelativeTolerance::try_new(1e-6_f64).unwrap(); // For relative comparisons
let length = PositiveRealScalar::try_new(5.0_f64).unwrap(); // Must be > 0
// Type system prevents mixing them up:
// let wrong: AbsoluteTolerance<f64> = rel_tol; // ← Compilation error!§Validation at Construction Time
All types validate their input at construction time, failing fast on invalid values:
use num_valid::{*, scalars::*};
use try_create::TryNew;
use std::assert_matches;
// Negative tolerances are rejected
let invalid_tol = AbsoluteTolerance::try_new(
RealNative64StrictFinite::try_new(-1e-6_f64).unwrap()
).unwrap_err();
assert_matches!(invalid_tol, ErrorsNonNegativeRealScalar::NegativeValue { .. });
// Zero is not positive
let invalid_pos = PositiveRealScalar::try_new(0.0_f64).unwrap_err();
assert_matches!(invalid_pos, ErrorsPositiveRealScalar::ZeroValue { .. });§Error Handling
Both AbsoluteTolerance<T> and RelativeTolerance<T> use ErrorsNonNegativeRealScalar
for error reporting, since they are wrappers around NonNegativeRealScalar<T>. This provides
consistent error semantics:
- No negative values: Both types reject negative inputs
- Debug validation: NaN and infinity are panicked on in debug builds
- Backtrace support: When the
backtracefeature is enabled
§Tolerance Types
§AbsoluteTolerance<T>
Represents an absolute error bound for numerical comparisons. The tolerance value must be non-negative (≥ 0).
use num_valid::{*, scalars::*};
use try_create::TryNew;
// Create tolerances
let tight = AbsoluteTolerance::try_new(1e-12_f64).unwrap();
let loose = AbsoluteTolerance::try_new(1e-6_f64).unwrap();
let zero = AbsoluteTolerance::<f64>::zero(); // Exact comparison
let epsilon = AbsoluteTolerance::<f64>::epsilon(); // Machine epsilon
// Use in approximate comparison
assert!(approximately_equal(1.0, 1.0 + 1e-13, &tight));
assert!(!approximately_equal(1.0, 1.0 + 1e-11, &tight));§RelativeTolerance<T>
Represents a relative (proportional) error bound. Can be converted to an absolute tolerance based on a reference value.
use num_valid::{*, scalars::RelativeTolerance};
use try_create::TryNew;
let rel_tol = RelativeTolerance::try_new(0.01_f64).unwrap(); // 1% tolerance
// Convert to absolute tolerance based on reference value
let reference = 1000.0_f64;
let abs_tol = rel_tol.absolute_tolerance(reference);
assert_eq!(abs_tol.as_raw_scalar_ref(), &10.0); // 1% of 1000 = 10§Constrained Real Number Types
§PositiveRealScalar<T>
Wraps a real scalar that must be strictly positive (> 0). Zero is not valid.
use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
use try_create::TryNew;
// Valid positive values
let length = PositiveRealScalar::try_new(2.5_f64).unwrap();
let tiny = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
// Zero is NOT positive (x > 0 required)
let zero_result = PositiveRealScalar::try_new(0.0_f64);
assert!(matches!(zero_result, Err(ErrorsPositiveRealScalar::ZeroValue { .. })));
// Negative values rejected
let neg_result = PositiveRealScalar::try_new(-1.0_f64);
assert!(matches!(neg_result, Err(ErrorsPositiveRealScalar::NegativeValue { .. })));§NonNegativeRealScalar<T>
Wraps a real scalar that must be non-negative (≥ 0). Zero is valid.
use num_valid::{*, scalars::*};
use try_create::TryNew;
// Zero is valid for NonNegativeRealScalar
let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
assert_eq!(*zero.as_raw_scalar_ref(), 0.0);
// But NOT for PositiveRealScalar
assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
// Use case: computing distances (can be zero)
fn distance(a: f64, b: f64) -> NonNegativeRealScalar<f64> {
NonNegativeRealScalar::try_new((a - b).abs()).unwrap()
}
let d = distance(5.0, 5.0); // Zero distance is valid
assert_eq!(d.as_raw_scalar_ref(), &0.0);§Generic Programming with RealScalar
All types work seamlessly with any scalar type implementing RealScalar:
use num_valid::{
backends::native::validated::{RealNative64StrictFinite, RealNative64StrictFiniteInDebug},
RealScalar,
scalars::AbsoluteTolerance
};
use try_create::TryNew;
// Same tolerance type works with different backends
type FastTol = AbsoluteTolerance<f64>;
type SafeTol = AbsoluteTolerance<RealNative64StrictFinite>;
type DebugTol = AbsoluteTolerance<RealNative64StrictFiniteInDebug>;
let fast = FastTol::try_new(1e-10).unwrap();
let safe = SafeTol::try_new(RealNative64StrictFinite::try_from_f64(1e-10).unwrap()).unwrap();§Performance Characteristics
All wrapper types are designed as zero-cost abstractions:
| Type | Memory Layout | Runtime Overhead |
|---|---|---|
AbsoluteTolerance<T> | Same as T | Zero (validation at construction only) |
RelativeTolerance<T> | Same as T | Zero (validation at construction only) |
PositiveRealScalar<T> | Same as T | Zero (validation at construction only) |
NonNegativeRealScalar<T> | Same as T | Zero (validation at construction only) |
The #[repr(transparent)] attribute ensures that each wrapper has the exact same
memory layout as its underlying type.
§Relationship to approx Crate
These tolerance types complement the approx crate’s comparison traits. While approx
uses raw f64 for epsilon values (which can be negative, causing silent failures),
these wrappers guarantee non-negativity at construction time:
use num_valid::{*, scalars::AbsoluteTolerance, approx::assert_abs_diff_eq};
// Create a validated tolerance
let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
// Use with approx (extract the inner value)
let a = 1.0_f64;
let b = 1.0 + 1e-11;
assert_abs_diff_eq!(a, b, epsilon = *tol.as_raw_scalar_ref());Structs§
- Absolute
Tolerance - Type-safe wrapper for absolute tolerance values.
- Hashable
Scalar - Wrapper type that makes floating-point scalars hashable for use in hash-based collections.
- NonNegative
Real Scalar - Type-safe wrapper for non-negative real scalar values.
- Positive
Real Scalar - Type-safe wrapper for strictly positive real scalar values.
- Relative
Tolerance - Type-safe wrapper for relative tolerance values.
Enums§
- Errors
Hashable Scalar - Error returned when constructing a
HashableScalarfrom an invalid value. - Errors
NonNegative Real Scalar - Error type for
NonNegativeRealScalar<T>validation failures. - Errors
Positive Real Scalar - Error type for
PositiveRealScalar<T>validation failures.
Functions§
- approximately_
equal - Compares two scalar values within an absolute tolerance.