#nan #floats #compile-time #non-zero #no-std

无 std typed_floats

编译时类型检查的浮点数处理类型

3 个版本 (稳定版)

1.0.1 2024 年 4 月 2 日
1.0.0 2024 年 3 月 9 日
0.7.4 2024 年 2 月 18 日
0.7.3 2023 年 11 月 29 日
0.1.23 2023 年 7 月 31 日

#279数据结构

Download history 246/week @ 2024-04-29 223/week @ 2024-05-06 59/week @ 2024-05-13 208/week @ 2024-05-20 137/week @ 2024-05-27 480/week @ 2024-06-03 225/week @ 2024-06-10 94/week @ 2024-06-17 56/week @ 2024-06-24 27/week @ 2024-07-01 92/week @ 2024-07-08 80/week @ 2024-07-15 69/week @ 2024-07-22 638/week @ 2024-07-29 305/week @ 2024-08-05 274/week @ 2024-08-12

每月 1,293 次下载
用于 h3o

MIT/Apache 许可协议

295KB
4.5K SLoC

Build Status Tests on GitHub CI Version Documentation License dependency status Minimum Supported Rust Version Miri

此软件包可以帮助您确保所使用的浮点数类型,无需 panic!(除非以不安全的方式使用 unsafe 函数 new_unchecked)。

无开销:所有检查都在编译时进行。(只有 try_from 在运行时增加少许开销)

所有类型都拒绝 NaN。

简而言之

如果您

  • 希望在编译时知道一个浮点数是否可以为负、正、零、有限,并确保它不是 NaN,而无需 panic!。

  • 需要浮点数上的 Ord、Eq 或 Hash。

此软件包提供的 12 种类型

以及它们的正负对应类型

(负数类型拒绝 +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>),你可以使用模块 tf64tf32,它们公开了别名。

何时使用

当处理浮点数时

当你处理浮点数时,这个crate可以帮助你确保你不会不小心使用NaNInfinity。实现的方法和函数返回尽可能严格的数据类型,这样你知道何时真正需要检查NaNInfinity

当编写库时

在你的公共API中使用此crate提供的类型之一可以帮助用户避免错误,并限制函数需要执行的检查。

它还有助于使API更简单,因为你不必处理和记录所有可能的带有NaNInfinity的情况,例如。

例如以下函数

fn fast_inv_sqrt(x: StrictlyPositiveFinite) -> StrictlyPositive;

它确保

  • 对于API实现者:参数x既不是NaN也不是Infinity,并且是严格正数
  • 对于用户:结果是既不是NaN也是严格正数,但可能是Infinity

在该示例中

  • API实现者不必检查参数xNaNInfinity<= 0
  • 如果用户想以不同的方式处理结果并且不能使用无效参数调用该函数,则用户只需检查结果是否为Infinity

API

底层类型上大多数方法和特质都可用在此crate的类型上。

大多数常量也是可用的,其中最合适的类型是 TypedFloat(除明显原因外的 NAN),在 tf64tf32 模块中(当常量来自 core::f64::constscore::f32::consts 时,分别在 tf64::conststf32::consts)。这些模块的命名方式是为了避免与原始类型 f32f64 发生冲突或混淆。

⚠️ 与原始类型 f32f64 一样,对于本库的所有类型,-0.0 == +0.0true。为了方便比较,添加了 is_positive_zerois_negative_zero 方法。

实现的特征

转换:core::convert::From / core::convert::TryFrom

(根据情况实现 FromTryFrom 特性)

比较: core::cmp::PartialOrdcore::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 种类型都实现了在 f32f64 上可用的方法,除了

  • 已弃用和仅限夜间构建的方法
  • 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设计时考虑到了运行时的最小开销,包括内存、速度和二进制文件大小。

它甚至可能比直接使用原语 f32f64 更快,因为它可能通过使用编译器提示避免了某些检查。

唯一会增加一点开销的方法是 try_from,因为它在运行时进行了一些检查,与 unsafe 方法 new_unchecked 相比。

在调试模式下,存在一些开销,一方面是为了检查值的有效性,另一方面是因为可能不遵守 inline 指令。

任何其他开销都被认为是bug,应该报告。

特性

  • std:默认启用,提供所有 f32f64 方法。
  • serde:为所有12种类型实现 SerializeDeserialize
  • libm:当禁用 std 功能时,使用来自 num-traitsFloat 特性和 libm 来实现缺失的方法。当同时启用 stdlibm 功能时,使用 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:HypotMinMaxCopysignDivEuclidAtan2

主要限制

  • 无法修复浮点数怪癖,例如 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上运行nightlybetastable的测试。

由于在Cargo.toml中使用dep,最低支持的Rust版本为1.70.0。

测试

测试在GitHub actionsCircleCI上的不同架构上运行。

要运行所有测试

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 为各种 u32f64num_bigint::BigInt 等数字类型提供数值一致的 core::cmp::Eqcore::cmp::Ordcore::hash::Hash 实现。
  • ordered-float 提供了针对 f64 及其相关类型上的 Ord 和 Eq 实现的几个包装类型。
  • partial-min-max 提供了与 PartialOrd 一起工作的 minmax 函数。
  • 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