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

时间:2017-05-05 19:20:01

标签: floating-point rust undefined-behavior

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

this answer的评论暗示从原始字节转换浮点可能存在一些问题。

1 个答案:

答案 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);

Playground

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