Crates.io | typed_floats |
lib.rs | typed_floats |
version | 1.0.2 |
source | src |
created_at | 2023-06-18 20:44:37.658878 |
updated_at | 2024-09-13 22:10:16.336036 |
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 | 307,018 |
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 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 floats.
And their positive and negative counterparts:
Positive
,PositiveFinite
, StrictlyPositive
, StrictlyPositiveFinite
Negative
,NegativeFinite
, StrictlyNegative
, StrictlyNegativeFinite
(Negatives types reject +0.0
and positives types reject -0.0
)
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 TypedFloat 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:
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 may even be faster than using primitives f32
and f64
directly, as it may avoids some checks by using compiler hints.
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.
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)
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 weekly on GitHub actions.
The minimum supported Rust version (MSRV) is 1.70.0 because of the use of dep:
in Cargo.toml
.
Tests are run on different architectures on GitHub actions and CircleCI.
To run all tests:
git clone https://github.com/tdelmas/typed_floats
# generate the published documentation, including some tests
cargo xtask pre-build
cd typed_floats
cargo test --all
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.