Crates.io | typed_floats |
lib.rs | typed_floats |
version | 1.0.7 |
created_at | 2023-06-18 20:44:37.658878+00 |
updated_at | 2025-09-22 18:33:18.111631+00 |
description | Types for handling floats with type checking at compile time. |
homepage | |
repository | https://github.com/tdelmas/typed_floats |
max_upload_size | |
id | 893633 |
size | 322,525 |
This crate helps you to ensure the kind of floats you are using, without panic!
(except if the unsafe
function new_unchecked
is used in an unsound way).
zero overhead: everything is checked at compile time.
(only new
and try_from
adds a little overhead at runtime)
NaN
is rejected by all types.
This crate is for you if:
If you want to know at compile time if a float can be negative, positive, zero, finite and ensure it is not NaN
, without panic!
.
If you need core::cmp::Ord
, core::cmp::Eq
or core::hash::Hash
on (non-NaN
) floats.
And their positive and negative counterparts:
Positive
,PositiveFinite
, StrictlyPositive
, StrictlyPositiveFinite
Negative
,NegativeFinite
, StrictlyNegative
, StrictlyNegativeFinite
Type | -∞ |
]-∞; -0.0[ |
-0.0 |
+0.0 |
]+0.0; +∞[ |
+∞ |
NaN |
---|---|---|---|---|---|---|---|
NonNaN |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ |
NonNaNFinite |
❌ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ |
NonZeroNonNaN |
✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ |
NonZeroNonNaNFinite |
❌ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ |
Positive |
❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ❌ |
PositiveFinite |
❌ | ❌ | ❌ | ✔️ | ✔️ | ❌ | ❌ |
StrictlyPositive |
❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ❌ |
StrictlyPositiveFinite |
❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ |
Negative |
✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ |
NegativeFinite |
❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ |
StrictlyNegative |
✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
StrictlyNegativeFinite |
❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
To avoid specifying the kind of float (e.g. like Positive<f32>
), you can use the modules tf64
and tf32
which expose aliases.
When you handle floats, this crate can help you to ensure that you are not using NaN
or Infinity
by mistake. Methods and functions implemented returns a type as strict as possible, so you know when you really have to check for NaN
or Infinity
.
Using one of the type provided by this crate in your public API can help your users to avoid mistakes and limits the checks your functions have to do.
It also helps to make API simpler as you don't have to handle and document all the possible cases with NaN
and Infinity
for example.
E.g. the following function:
fn fast_inv_sqrt(x: StrictlyPositiveFinite) -> StrictlyPositive;
It ensures:
x
is neither NaN
nor Infinity
, and is strictly positiveNaN
and is strictly positive but may be Infinity
In that example:
NaN
, Infinity
, or <= 0
for the parameter x
Infinity
if they want to handle it differently and can't call the function with an invalid parameter.Most methods and traits available on the underlying type are available on the types of this crate.
Most constants are also available, with the most appropriate typed float type (except NAN
for obvious reasons) in the tf64
and tf32
modules (in tf64::consts
and tf32::consts
respectively when the constant comes from core::f64::consts
or core::f32::consts
). Those modules are named that way to avoid conflicts or confusion with the primitives f32
and f64
.
⚠️ Like for primitives f32
and f64
,-0.0 == +0.0
is true
for all types of this crate.
To facilitate comparisons, the methods is_positive_zero
and is_negative_zero
are added.
core::convert::From
/ core::convert::TryFrom
f32
or f64
)f32
and f64
u128
and i128
)NonZero*
(core::num::NonZeroU8
, core::num::NonZeroU16
, core::num::NonZeroU32
, core::num::NonZeroU64
, core::num::NonZeroI8
, core::num::NonZeroI16
, core::num::NonZeroI32
, core::num::NonZeroI64
)(The traits From
and TryFrom
are implemented depending on the situation)
core::cmp::PartialOrd
and core::cmp::PartialEq
🗘 | f32 /f64 |
NonNaN |
NonNaNFinite |
NonZeroNonNaN |
NonZeroNonNaNFinite |
Positive |
PositiveFinite |
StrictlyPositive |
StrictlyPositiveFinite |
Negative |
NegativeFinite |
StrictlyNegative |
StrictlyNegativeFinite |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
f32 /f64 |
N/A | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
NonNaN |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
NonNaNFinite |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
NonZeroNonNaN |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
NonZeroNonNaNFinite |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
Positive |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
PositiveFinite |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
StrictlyPositive |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
StrictlyPositiveFinite |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
Negative |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
NegativeFinite |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
StrictlyNegative |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
StrictlyNegativeFinite |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
Trait | NonNaN |
NonNaNFinite |
NonZeroNonNaN |
NonZeroNonNaNFinite |
Positive |
PositiveFinite |
StrictlyPositive |
StrictlyPositiveFinite |
Negative |
NegativeFinite |
StrictlyNegative |
StrictlyNegativeFinite |
---|---|---|---|---|---|---|---|---|---|---|---|---|
core::cmp::Eq |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
core::cmp::Ord |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
core::hash::Hash |
✔️¹ | ✔️¹ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
core::default::Default |
0.0 |
0.0 |
❌ | ❌ | 0.0 |
0.0 |
❌ | ❌ | -0.0 |
-0.0 |
❌ | ❌ |
¹: there is a (small) overhead because they accept 0.0
and -0.0
(which are equal) so they must core::hash::Hash
to the same value.
All 12 types implement the methods available on f32
and f64
except:
total_cmp(&self, other: &f64) -> Ordering
sin_cos(self) -> (f64, f64)
mul_add(self, a: f64, b: f64) -> f64
clamp(self, min: f64, max: f64) -> f64
LowerExp
UpperExp
Product
Sum
to_int_unchecked
to*_bits
from*_bits
The only method that can panic!
is the unsafe
method new_unchecked
when used in an invalid way.
A panic!
triggered in any other way is considered a security bug and should be reported.
This crate is designed to have a minimal overhead at runtime, in terms of memory, speed and binary size.
It can even be faster than using primitives f32
and f64
directly, as it may avoids some checks by using compiler hints and can use some faster implementations in some cases.
The only methods that adds a little overhead are try_from
because of the checks they do at runtime, compared to the unsafe
method new_unchecked
.
In debug mode, a little overhead is present, both to check the validity of the values and because inline
may not be respected.
Any other overhead is considered a bug and should be reported.
The compiler hints are enabled by default to enable compiler optimization when possible.
Also, some methods are faster than the default implementation. For example:
Eq
is implemented by comparing the bits of the two floats instead of the slower default implementation, that had special cases for NaN
(to handle NaN != NaN
) and -0.0
(to handle -0.0 == 0.0
). (8% faster)Ord
is implemented by directly comparing the bits of the two floats instead of the slower default implementation for negatives and positives types. (4% faster)signum
doesn't needs to check for NaN
. (35% faster)NonNaN
and NonNaNFinite
use a faster implementation of Eq
, opening the door to Jump Threading optimisations.std
: enabled by default, gives all f32
and f64
methods.
serde
: implements Serialize
and Deserialize
for all 12 types.
libm
: use the Float
trait from num-traits
and libm
to implement the missing methods when the std
feature is disabled. When both std
and libm
features are enabled, the std
implementation is used.
compiler_hints
: enabled by default, will add core::hint::unreachable_unchecked
after all debug_assert
.
ensure_no_undefined_behavior
: Will panic!
in release mode instead of risking undefined behavior. This will override the compiler_hints
feature, and adds a little overhead to new_unchecked
. This feature can be enabled by any parent crate to ensure no undefined behavior.
For each operation, at compile time crate determine the most strict type possible for the result.
For example, if you multiply a PositiveFinite
and a StrictlyNegativeFinite
, the result will be a Negative
.
Methods that takes another float as parameter will also return the most strict type possible depending on the both types. For the methods where a trait is not available to specify the return type depending on the parameter type, a new trait is created:
Hypot
, Min
, Max
, Copysign
, DivEuclid
and Atan2
.
0.0 == -0.0
sqrt(-0.0)
returning -0.0
instead of NaN
min(-0.0, 0.0)
possibly returning 0.0
instead of -0.0
(same for max
)frac(-0.0)
returning 0.0
instead of -0.0
Because that would introduce a runtime overhead and may introduce some incompatibilities with existing code.
This crate is tested when a new version is release with:
Also, tests on nightly
, beta
and stable
are run monthly on GitHub actions.
The minimum supported Rust version (MSRV) is 1.70.0 because of the use of dep:
in Cargo.toml
.
A change in the MSRV will be treated as a breaking change.
Tests are run on different architectures on GitHub actions and CircleCI.
core::cmp::Eq
, core::cmp::Ord
and core::hash::Hash
implementations for various num
types (u32
, f64
, num_bigint::BigInt
, etc.)min
and max
functions that work with PartialOrd
.Features provided/checked by those crates:
✔️: provided, ❌: not provided, ❓: unknown
(you may need to scroll to the right to see all the columns: "Production ready", "Avoid panic!
", "Minimal overhead", "Eq/Ord", "Hash", "NaN", "Inf", "Zero", "Positive", "Negative")
Crates | Production ready | Avoid panic! |
Minimal overhead | Eq/Ord | Hash | NaN | Inf | Zero | Positive | Negative |
---|---|---|---|---|---|---|---|---|---|---|
typed_floats |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
checked-float |
✔️ | ✔️ | ❌ | ✔️ | ❌ | ✔️¹ | ✔️¹ | ✔️¹ | ✔️¹ | ✔️¹ |
decorum |
✔️ | ❌ | ❌ | ❌ | ❌ | ✔️¹ | ✔️¹ | ✔️¹ | ✔️¹ | ✔️¹ |
eq-float |
❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
fix_float |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ |
float-derive |
❌ | ❓ | ❓ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
float-ord |
✔️ | ✔️ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
nanbox |
❌ | ❓ | ❓ | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ |
noisy_float |
✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ |
num-order |
✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
ordered-float |
✔️ | ❌ | ❌ | ✔️ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ |
partial-min-max |
❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
real_float |
✔️ | ❌ | ❌ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ✔️ | ❌ |
result_float |
✔️ | ✔️ | ❌ | ✔️ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ |
totally-ordered |
✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
unsigned-f64 |
❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ |
(N.B. "Production ready" is a subjective measure)
¹: Can be manually checked
Is on docs.rs.