3 个版本 (稳定版)
1.0.1 | 2024 年 4 月 2 日 |
---|---|
1.0.0 | 2024 年 3 月 9 日 |
0.7.4 | 2024 年 2 月 18 日 |
0.7.3 |
|
0.1.23 |
|
#279 在 数据结构
每月 1,293 次下载
用于 h3o
295KB
4.5K SLoC
此软件包可以帮助您确保所使用的浮点数类型,无需 panic!(除非以不安全的方式使用 unsafe 函数 new_unchecked)。
无开销:所有检查都在编译时进行。(只有 try_from 在运行时增加少许开销)
所有类型都拒绝 NaN。
简而言之
如果您
-
希望在编译时知道一个浮点数是否可以为负、正、零、有限,并确保它不是 NaN,而无需 panic!。
-
需要浮点数上的 Ord、Eq 或 Hash。
此软件包提供的 12 种类型
以及它们的正负对应类型
Positive
,PositiveFinite
,StrictlyPositive
,StrictlyPositiveFinite
Negative
,NegativeFinite
,StrictlyNegative
,StrictlyNegativeFinite
(负数类型拒绝 +0.0
,而正数类型拒绝 -0.0
)
类型 | -∞ | ]-∞; -0.0[ | -0.0 | +0.0 | ]+0.0; +∞[ | +∞ | NaN |
---|---|---|---|---|---|---|---|
NonNaN |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ |
NonNaNFinite |
❌ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ |
NonZeroNonNaN |
✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ |
NonZeroNonNaNFinite |
❌ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ |
Positive |
❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ❌ |
PositiveFinite |
❌ | ❌ | ❌ | ✔️ | ✔️ | ❌ | ❌ |
StrictlyPositive |
❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ❌ |
StrictlyPositiveFinite |
❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ |
Negative |
✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ |
NegativeFinite |
❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ |
StrictlyNegative |
✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
StrictlyNegativeFinite |
❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
为了避免指定浮点数的类型(例如,像 Positive<f32>
),你可以使用模块 tf64
和 tf32
,它们公开了别名。
何时使用
当处理浮点数时
当你处理浮点数时,这个crate可以帮助你确保你不会不小心使用NaN
或Infinity
。实现的方法和函数返回尽可能严格的数据类型,这样你知道何时真正需要检查NaN
或Infinity
。
当编写库时
在你的公共API中使用此crate提供的类型之一可以帮助用户避免错误,并限制函数需要执行的检查。
它还有助于使API更简单,因为你不必处理和记录所有可能的带有NaN
和Infinity
的情况,例如。
例如以下函数
fn fast_inv_sqrt(x: StrictlyPositiveFinite) -> StrictlyPositive;
它确保
- 对于API实现者:参数
x
既不是NaN
也不是Infinity
,并且是严格正数 - 对于用户:结果是既不是
NaN
也是严格正数,但可能是Infinity
在该示例中
- API实现者不必检查参数
x
的NaN、
Infinity
或<= 0
- 如果用户想以不同的方式处理结果并且不能使用无效参数调用该函数,则用户只需检查结果是否为
Infinity
API
底层类型上大多数方法和特质都可用在此crate的类型上。
大多数常量也是可用的,其中最合适的类型是 TypedFloat
(除明显原因外的 NAN
),在 tf64
和 tf32
模块中(当常量来自 core::f64::consts
或 core::f32::consts
时,分别在 tf64::consts
和 tf32::consts
)。这些模块的命名方式是为了避免与原始类型 f32
和 f64
发生冲突或混淆。
⚠️ 与原始类型 f32
和 f64
一样,对于本库的所有类型,-0.0 == +0.0
是 true
。为了方便比较,添加了 is_positive_zero
和 is_negative_zero
方法。
实现的特征
转换:core::convert::From
/ core::convert::TryFrom
- 在本库的所有类型之间(同种类型,
f32
或f64
) - 从
f32
和f64
- 从整数类型(除了
u128
和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
)
(根据情况实现 From
和 TryFrom
特性)
比较: core::cmp::PartialOrd
和 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 |
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
无泛型参数的特性
特性 | 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 |
❌ | ❌ |
¹:由于它们接受 0.0
和 -0.0
(它们是相等的),所以它们必须 core::hash::Hash
到相同的值。
实现的方法
所有 12 种类型都实现了在 f32
和 f64
上可用的方法,除了
- 已弃用和仅限夜间构建的方法
- 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
Panics
唯一可以触发 panic!
的方法是使用不正确的方式调用 unsafe
方法 new_unchecked
。
以任何其他方式触发的 panic!
都被视为安全漏洞,应该报告。
最小开销
这个crate设计时考虑到了运行时的最小开销,包括内存、速度和二进制文件大小。
它甚至可能比直接使用原语 f32
和 f64
更快,因为它可能通过使用编译器提示避免了某些检查。
唯一会增加一点开销的方法是 try_from
,因为它在运行时进行了一些检查,与 unsafe
方法 new_unchecked
相比。
在调试模式下,存在一些开销,一方面是为了检查值的有效性,另一方面是因为可能不遵守 inline
指令。
任何其他开销都被认为是bug,应该报告。
特性
std
:默认启用,提供所有f32
和f64
方法。serde
:为所有12种类型实现Serialize
和Deserialize
。libm
:当禁用std
功能时,使用来自num-traits
的Float
特性和libm
来实现缺失的方法。当同时启用std
和libm
功能时,使用std
实现。compiler_hints
:默认启用,将在所有debug_assert
后添加core::hint::unreachable_unchecked
。ensure_no_undefined_behavior
:在发布模式下会触发panic!
,而不是冒险出现未定义的行为。这将覆盖compiler_hints
功能,并为new_unchecked
增加一点开销。可以通过任何父crate启用此功能来确保没有未定义的行为。
工作原理
对于每个操作,在编译时crate会确定可能的最严格的结果类型。
例如,如果你将一个 PositiveFinite
和一个 StrictlyNegativeFinite
相乘,结果将是一个 Negative
。
接受另一个浮点数作为参数的方法也将返回最严格的可能类型,这取决于两种类型。对于那些无法根据参数类型指定返回类型的trait的方法,将创建一个新的trait:Hypot
、Min
、Max
、Copysign
、DivEuclid
和 Atan2
。
主要限制
- 无法修复浮点数怪癖,例如
0.0 == -0.0
- 无法修复诸如以下奇特的函数
sqrt(-0.0)
返回-0.0
而不是NaN
min(-0.0, 0.0)
返回-0.0
而不是0.0
(同样适用于max
)frac(-0.0)
返回0.0
而不是-0.0
因为这会引入运行时开销,并可能引入与现有代码的一些不兼容性。
Rust版本
当发布新版本时,此crate会使用Rust beta进行测试
- Rust beta
- Rust稳定版
- Rust 1.70.0
此外,每周在GitHub actions上运行nightly
、beta
和stable
的测试。
由于在Cargo.toml
中使用dep
,最低支持的Rust版本为1.70.0。
测试
测试在GitHub actions和CircleCI上的不同架构上运行。
要运行所有测试
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
类似的crate
- checked-float 用于创建不变性强制浮点数包装器的crate
- decorum Decorum是一个Rust库,它为浮点数表示提供总排序、等价性、哈希和约束。Decorum不需要std。
- eq-float 具有总排序(通过设置NAN == NAN)的浮点数包装器。
- fix_float 允许在浮点数上实现有用的特性和数据结构的固定浮点类型
- float-derive 允许为包含浮点数的类型派生Eq和Hash的crate
- float-ord 浮点数的总排序。
- nanbox NaN装箱实现。
- noisy_float 包含当设置为非法值(如NaN)时引发panic的浮点数类型。
- num-order 为各种
u32
、f64
、num_bigint::BigInt
等数字类型提供数值一致的core::cmp::Eq
、core::cmp::Ord
和core::hash::Hash
实现。 - ordered-float 提供了针对 f64 及其相关类型上的 Ord 和 Eq 实现的几个包装类型。
- partial-min-max 提供了与
PartialOrd
一起工作的min
和max
函数。 - real_float 提供了检查正确性和实现完全排序的浮点类型。
- result_float 提供了不能存储 NaN 的浮点类型。
- totally-ordered 无依赖,不依赖 std 的 f32/f64 完全排序。
- unsigned-f64 是一个围绕 f64 的包装器,保证类型级别上的值始终为非负。
这些 crate 提供或检查的功能
✔️: 提供,❌: 不提供,❓: 未知
(可能需要向右滚动才能看到所有列:“生产就绪”,“避免 panic!
”,“最小开销”,“Eq/Ord”,“Hash”,“NaN”,“Inf”,“Zero”,“Positive”,“Negative”)
Crates | 生产就绪 | 避免 panic! |
最小开销 | 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 |
❌ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ |
(注意:“生产就绪”是一个主观衡量标准)
¹: 可手动检查
完整文档
在 docs.rs 上。
依赖
~0.3–0.9MB
~20K SLoC