是将字节转换为浮动安全还是可能产生未定义的行为?

ide*_*n42 3 floating-point undefined-behavior rust

是否存在字节序列,当转换为f32或者f64在Rust中产生未定义的行为时?我将非有限值(例如NaN,Infinity等)计为有效浮点值.

对此答案的评论暗示可能存在从原始字节转换浮点数的问题.

E_n*_*ate 7

Rust引用提供了一个很好的未定义行为发生的情况列表.其中,与问题关系最密切的是:

原始类型中的值无效,即使在私有字段/本地中也是如此:

  • 悬空/空引用或框
  • bool中除false(0)或true(1)以外的值
  • 枚举中未包含在类型定义中的判别式
  • char中的值,它是一个代理项或者是char :: MAX
  • str中的非UTF-8字节序列

而且,未列出浮点类型.这是因为根据IEEE 754-2008 binary32和binary64浮点类型,任何位序列(32位,f3264位f64)都是浮点值的有效状态.它们可能不正常(其他类别为零,次正常,无限不是数字),但仍然有效.

在尽管最终的,应该永远是另一种方式左右transmute.特别是,byteordercrate提供了一种从字节流中读取数字的安全且直观的方法.

use byteorder::{ByteOrder, LittleEndian}; // or NativeEndian

let bytes = [0x00u8, 0x00, 0x80, 0x7F];
let number = LittleEndian::read_f32(&bytes);
println!("{}", number);
Run Code Online (Sandbox Code Playgroud)

操场


好吧,实际上有一个非常特殊的边缘情况,其中将位转换为浮点可以产生信令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的历史中有一段时间不确定.