| Crates.io | decorum |
| lib.rs | decorum |
| version | 0.4.0 |
| created_at | 2017-10-25 04:37:07.147695+00 |
| updated_at | 2024-11-22 01:47:12.5238+00 |
| description | Total ordering, equivalence, hashing, and constraints for floating-point types. |
| homepage | |
| repository | https://github.com/olson-sean-k/decorum |
| max_upload_size | |
| id | 36837 |
| size | 450,777 |
Decorum is a Rust library that provides total ordering, equivalence,
hashing, constraints, error handling, and more for IEEE 754 floating-point
representations. Decorum does not require the std nor alloc libraries,
though they are necessary for some features.
Panic when a NaN is encountered:
use decorum::NotNan;
let x = NotNan::<f64>::assert(0.0);
let y = NotNan::<f64>::assert(0.0);
let z = x / y; // Panics.
Hash totally ordered IEEE 754 floating-point representations:
use decorum::real::UnaryRealFunction;
use decorum::Real;
use std::collections::HashMap;
let key = Real::<f64>::PI;
let mut xs: HashMap<_, _> = [(key, "pi")].into_iter().collect();
Configure the behavior of an IEEE 754 floating-point representation:
pub mod real {
use decorum::constraint::IsReal;
use decorum::divergence::{AsResult, OrError};
use decorum::proxy::{Constrained, OutputFor};
// A 64-bit floating-point type that must represent a real number and returns
// `Result`s from fallible operations.
pub type Real = Constrained<f64, IsReal<OrError<AsResult>>>;
pub type Result = OutputFor<Real>;
}
use real::Real;
pub fn f(x: Real) -> real::Result { ... }
let x = Real::assert(0.0);
let y = Real::assert(0.0);
let z = (x / y)?;
The primary API of Decorum is its Constrained types, which transparently wrap
primitive IEEE 754 floating-point types and configure their behavior.
Constrained
types support many numeric features and operations and integrate with the
num-traits crate and others when Cargo features are
enabled. Depending on its configuration, a proxy can be used as a drop-in
replacement for primitive floating-point types.
The following Constrained behaviors can be configured:
Note that the output type of fallible operations and the error behavior are
independent. A Constrained type may return a Result and yet panic if an error
occurs, which can be useful for conditional compilation and builds wherein
behavior changes but types do not. The behavior of a Constrained type is
configured using two mechanisms: constraints and divergence.
use decorum::constraint::IsReal;
use decorum::divergence::OrPanic;
use decorum::proxy::Constrained;
// `Real` must represent a real number and otherwise panics.
pub type Real = Constrained<f64, IsReal<OrPanic>>;
Constraints specify a subset of floating-point values that a proxy may represent. IEEE 754 floating-point values are divided into three such subsets:
| Subset | Example Member |
|---|---|
| real numbers | 3.1459 |
| infinities | +INF |
| not-a-numbers | NaN |
Constraints can be used to strictly represent real numbers, extended reals, or complete but totally ordered IEEE 754 types (i.e., no constraints). Available constraints are summarized below:
| Constraint | Members | Fallible |
|---|---|---|
IsFloat |
real numbers, infinities, not-a-numbers | no |
IsExtendedReal |
real numbers, infinities | yes |
IsReal |
real numbers | yes |
IsFloat supports all IEEE 754 floating-point values and so applies no
constraint at all. As such, it has no fallible operations w.r.t. the constraint
and does not accept a divergence.
Many operations on members of these subsets may produce values from other
subsets that are illegal w.r.t. constraints, such as the addition of two real
numbers resulting in +INF. A divergence type determines both the behavior
when an illegal value is encountered as well as the output type of such fallible
operations.
| Divergence | OK | Error | Default Output Kind |
|---|---|---|---|
OrPanic |
continue | panic | AsSelf |
OrError |
continue | break | AsExpression |
In the above table, continue refers to returning a non-error value while
break refers to returning an error value. If an illegal value is encountered,
then the OrPanic divergence panics while the OrError divergence
constructs a value that encodes the error. The output type of fallible
operations is determined by an output kind:
| Output Kind | Type | Continue | Break |
|---|---|---|---|
AsSelf |
Self |
self |
|
AsOption |
Option<Self> |
Some(self) |
None |
AsResult |
Result<Self, E> |
Ok(self) |
Err(error) |
AsExpression |
Expression<Self, E> |
Defined(self) |
Undefined(error) |
In the table above, Self refers to a Constrained type and E refers to the
associated error type of its constraint. Note that only the OrPanic divergence
supports AsSelf and can output the same type as its input type for fallible
operations (just like primitive IEEE 754 floating-point types).
With the sole exception of AsSelf, the output type of fallible operations is
extrinsic: fallible operations produce types that differ from their input types.
The Expression type, which somewhat resembles the standard Result type,
improves the ergonomics of error handling by implementing mathematical traits
such that it can be used directly in expressions and defer error checking.
use decorum::constraint::IsReal;
use decorum::divergence::{AsExpression, OrError};
use decorum::proxy::{Constrained, OutputFor};
use decorum::real::UnaryRealFunction;
use decorum::try_expression;
pub type Real = Constrained<f64, IsReal<OrError<AsExpression>>>;
pub type Expr = OutputFor<Real>;
pub fn f(x: Real, y: Real) -> Expr {
let sum = x + y;
sum * g(x)
}
pub fn g(x: Real) -> Expr {
x + Real::ONE
}
let x: Real = try_expression! { f(Real::E, -Real::ONE) };
// ...
When using a nightly Rust toolchain with the unstable Cargo
feature enabled, Expression also supports the (at time of
writing) unstable Try trait and try operator ?.
// As above, but using the try operator `?`.
let x: Real = f(Real::E, -Real::ONE)?;
Constrained types support numerous constructions and conversions depending on
configuration, including conversions for references, slices, subsets, supersets,
and more. Conversions are provided via inherent functions and implementations of
the standard From and TryFrom traits. The following inherent functions are
supported by all Constrained types, though some more bespoke constructions are
available for specific configurations.
| Method | Input | Output | Error |
|---|---|---|---|
new |
primitive | proxy | break |
assert |
primitive | proxy | panic |
try_new |
primitive | proxy | Result::Err |
try_from_{mut_}slice |
primitive | proxy | Result::Err |
into_inner |
proxy | primitive | |
from_subset |
proxy | proxy | |
into_superset |
proxy | proxy |
The following type definitions provide common proxy configurations. Each type implements different traits that describe the supported encoding and elements of IEEE 754 floating-point based on its constraints.
| Type Definition | Sized Aliases | Trait Implementations | Illegal Values |
|---|---|---|---|
Total |
BaseEncoding + InfinityEncoding + NanEncoding |
||
ExtendedReal |
E32, E64 |
BaseEncoding + InfinityEncoding |
NaN |
Real |
R32, R64 |
BaseEncoding |
NaN, -INF, +INF |
Decorum provides the following non-standard total ordering for IEEE 754 floating-point representations:
-INF < ... < 0 < ... < +INF < NaN
IEEE 754 floating-point encoding has multiple representations of zero (-0 and
+0) and NaN. This ordering and equivalence relations consider all zero and
NaN representations equal, which differs from the standard partial
ordering.
Some proxy types disallow unordered NaN values and therefore support a total
ordering based on the ordered subset of non-NaN floating-point values.
Constrained
types that use IsFloat (such as the Total type definition) support NaN but
use the total ordering described above to implement the standard Eq, Hash,
and Ord traits.
The following traits can be used to compare and hash primitive floating-point values (including slices) using this non-standard relation.
| Floating-Point Trait | Standard Trait |
|---|---|
CanonicalEq |
Eq |
CanonicalHash |
Hash |
CanonicalOrd |
Ord |
use decorum::cmp::CanonicalEq;
let x = 0.0f64 / 0.0f64; // `NaN`.
let y = f64::INFINITY + f64::NEG_INFINITY; // `NaN`.
assert!(x.eq_canonical(&y));
Decorum also provides the EmptyOrd trait and the min_or_empty and
max_or_empty functions. This trait defines a particular ordering for types
that may have a notion of empty inhabitants. An empty inhabitant is considered
incomparable, and comparisons return an empty inhabitant when encountered. For
example, None is the empty inhabitant for Option. For floating-point types
(including proxy types), NaNs are considered empty inhabitants. EmptyOrd
functions forward NaNs when comparing these types just like most numeric
operations (unlike f64::max, etc.).
use decorum::cmp;
use decorum::real::{Endofunction, RealFunction, UnaryRealFunction};
pub fn f<T>(x: T, y: T) -> T
where
T: Endofunction + RealFunction,
{
// `min` is assigned an empty inhabitant if either `x` or `y` are an empty
// inhabitant. For `T`, the empty inhabitants are `NaN`s, so this function
// forwards any input `NaN`s to its output.
let min = cmp::min_or_empty(x, y);
min * T::PI
}
The real module provides various traits that describe real numbers and
constructions via IEEE 754 floating-point types. These traits model functions
and operations on real numbers and specify a codomain for functions where the
output is not mathematically confined to the reals or a floating-point exception
may yield a non-real approximation or error. For example, the logarithm of zero
is undefined and the sum of two very large reals results in an infinity in IEEE
754. For proxy types, the codomain is the same as the branch type of its
divergence (see above).
Real number and IEEE 754 encoding traits can both be used for generic programming. The following code demonstrates a function that accepts types that support floating-point infinities and real functions.
use decorum::real::{Endofunction, RealFunction};
use decorum::InfinityEncoding;
fn f<T>(x: T, y: T) -> T
where
T: Endofunction + InfinityEncoding + RealFunction,
{
let z = x / y;
if z.is_infinite() {
x + y
}
else {
z + y
}
}
Decorum supports the following feature flags.
| Feature | Default | Description |
|---|---|---|
approx |
yes | Implements traits from approx for Constrained types. |
serde |
yes | Implements traits from serde for Constrained types. |
std |
yes | Integrates the std library and enables dependent features. |
unstable |
no | Enables features that require an unstable compiler. |