是否存在字节序列,当转换为f32
或f64
时,会在Rust中产生未定义的行为?我将非有限值(例如NaN,Infinity等)计为有效浮点值。
对this answer的评论暗示从原始字节转换浮点可能存在一些问题。
答案 0 :(得分:6)
The Rust reference提供了一个很好的未定义行为发生情况列表。其中,与问题关系最密切的是:
原始类型中的值无效,即使在私有字段/本地中也是如此:
- 悬空/空引用或方框
- bool
中除false(0)或true(1)以外的值- 未包含在类型定义中的枚举中的判别式
- char中的一个值,它是一个代理或更高的char :: MAX
- str
中的非UTF-8字节序列
但是,仍未列出浮点类型。这是因为根据IEEE 754-2008 binary32和binary64浮点类型,任何位序列(f32
为32位; f64
为64位)是浮点值的有效状态。它们可能不是正常(其他类零,次正常,无限或不是数字),但仍然有效。
最后,transmute
周围应始终Another Way。特别是,byteorder
包提供了一种安全,直观的方式来从字节流中读取数字。
use byteorder::{ByteOrder, LittleEndian}; // or NativeEndian
let bytes = [0x00u8, 0x00, 0x80, 0x7F];
let number = LittleEndian::read_f32(&bytes);
println!("{}", number);
好吧,实际上有一个非常特殊的边缘情况,其中将位转换为浮点可能会导致信令NaN ,这在某些CPU架构和配置中将触发低级别异常。有关详细信息,请参阅rust#39271中的讨论。目前已知实现信令NaN不是未定义的行为,并且如果启用浮点异常(默认情况下不是这样),则这不太可能是一个问题。
Rust图书馆团队已经实施的决定是,即使没有任何掩蔽,转换到浮动也是安全的。 f32::from_bits
的文档中详细描述了推理:
这与所有平台上的
transmute::<u32, f32>(v)
相同。事实证明这是非常便携的,原因有两个:
- Floats和Ints在所有支持的平台上具有相同的字节顺序。
- IEEE-754非常精确地指定了浮点数的位布局。
然而,有一点需要注意:在2008版IEEE-754之前,实际上没有指定如何解释NaN信令位。大多数平台(特别是x86和ARM)选择了最终在2008年标准化的解释,但有些平台没有(特别是MIPS)。因此,MIPS上的所有信令NaN都是x86上的安静NaN,反之亦然。
这种实现不是试图保留信令跨平台,而是有利于保留确切的位。这意味着即使此方法的结果通过网络从x86计算机发送到MIPS计算机,也会保留以NaN编码的任何有效负载。
如果此方法的结果仅由生成它们的相同体系结构操纵,则不存在可移植性问题。
如果输入不是NaN,则不存在可移植性问题。
如果您不关心信令(非常可能),那么就没有可移植性问题。
一些解析/编码库可能仍然会将所有类型的NaN转换为一个确定安静的NaN,因为这件事在Rust的历史中有一段时间不确定。