Rust 参考 https://doc.rust-lang.org/reference/behavior-considered-undefined.html提供了发生未定义行为的情况的良好列表。其中,与问题最密切相关的是:
原始类型中的无效值,即使在私有字段/局部变量中也是如此:
- 悬空/空引用或框
- 布尔值中除 false (0) 或 true (1) 以外的值
- 枚举中的判别式不包含在类型定义中
- char 中的值是代理或高于 char::MAX
- str 中的非 UTF-8 字节序列
尽管如此,浮点类型并未列出。这是因为任何位序列(32 位f32
; 64 位用于f64
) 是浮点值的有效状态,根据 IEEE 754-2008 二进制 32 和二进制 64 浮点类型。他们可能不是normal(其他类是zero, 低于正常的, infinite, or 不是一个数字),但仍然有效。
但最终,应该总是有其他方式 https://doc.rust-lang.org/nomicon/transmutes.html around transmute
。特别是,byteorder
crate 提供了一种安全直观的方法来从字节流中读取数字。
use byteorder::{ByteOrder, LittleEndian}; // or NativeEndian
let bytes = [0x00u8, 0x00, 0x80, 0x7F];
let number = LittleEndian::read_f32(&bytes);
println!("{}", number);
好吧,实际上有一个非常特殊的边缘情况,将位转换为浮点数可能会导致信号 NaN,这在某些 CPU 架构和配置中会触发低级异常。请参阅中的讨论生锈#39271 https://github.com/rust-lang/rust/pull/39271了解详情。目前已知,具体化信号 NaN 不是未定义的行为,并且如果启用浮点异常(默认情况下并非如此),则这不太可能成为问题。
Rust 库团队已经实施的决定是,即使没有任何类型的掩码,转换为浮点数也是安全的。其推理在文档中有很好的描述f32::from_bits https://doc.rust-lang.org/std/primitive.f32.html#method.from_bits:
目前这与transmute::<u32, f32>(v)
在所有平台上。事实证明,它非常便携,原因有二:
- 浮点数和整数在所有支持的平台上具有相同的字节顺序。
- IEEE-754 非常精确地指定了浮点数的位布局。
不过,有一点需要注意:在 IEEE-754 2008 版之前,实际上并未指定如何解释 NaN 信号位。大多数平台(特别是 x86 和 ARM)选择了最终在 2008 年标准化的解释,但有些平台(特别是 MIPS)没有选择。因此,MIPS 上的所有信号 NaN 在 x86 上都是安静的 NaN,反之亦然。
该实现倾向于保留确切的位,而不是试图保留跨平台的信令性。这意味着即使此方法的结果通过网络从 x86 机器发送到 MIPS 机器,以 NaN 编码的任何有效负载也将被保留。
如果此方法的结果仅由生成它们的相同体系结构操纵,则不存在可移植性问题。
如果输入不是 NaN,则不存在可移植性问题。
如果您不关心信号性(很可能),那么就不存在可移植性问题。
一些解析/编码库可能仍在将各种 NaN 转换为绝对安静的 NaN,因为这件事在 Rust 的历史上有一段时间是不确定的。