【问题标题】:Is transmuting bytes to a float safe or might it produce undefined behavior?将字节转换为浮点数是安全的还是会产生未定义的行为?
【发布时间】:2017-05-05 19:20:00
【问题描述】:

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

this answer 的 cmets 提示从原始字节转换浮点数可能存在一些问题。

【问题讨论】:

  • 可能是信号 NaN。

标签: floating-point rust undefined-behavior


【解决方案1】:

The Rust reference 提供了一个很好的列表,其中列出了发生未定义行为的情况。其中,与该问题最密切相关的是:

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

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

仍然没有列出浮点类型。这是因为根据 IEEE 754-2008 binary32 和 binary64 浮点类型,任何位序列(f32 为 32 位;f64 为 64 位)都是浮点值的有效状态。它们可能不是 normal(其他类是 zero、subnormal、infinite 或 不是数字),但仍然有效。

最后,Another Way 应该总是在 transmute 周围。特别是,byteorder crate 提供了一种从字节流中读取数字的安全且直观的方法。

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) 相同。事实证明这是非常便携的,原因有两个:

  • 浮点数和整数在所有支持的平台上都具有相同的字节顺序。
  • IEEE-754 非常精确地指定了浮点数的位布局。

但是有一个警告:在 2008 版 IEEE-754 之前,实际上并未指定如何解释 NaN 信号位。大多数平台(特别是 x86 和 ARM)选择了最终在 2008 年标准化的解释,但有些没有(特别是 MIPS)。因此,MIPS 上的所有信令 NaN 在 x86 上都是安静的 NaN,反之亦然。

与其尝试保留跨平台的信令特性,此实现更倾向于保留确切的位。这意味着任何以 NaN 编码的有效载荷都将被保留,即使此方法的结果是通过网络从 x86 机器发送到 MIPS 机器。

如果此方法的结果仅由产生它们的同一架构操作,则不存在可移植性问题。

如果输入不是 NaN,则不存在可移植性问题。

如果您不关心信令(很可能),那么就没有可移植性问题。

一些解析/编码库可能仍在将各种 NaN 转换为绝对安静的 NaN,因为这在 Rust 的历史上曾有一段时间是不确定的。

【讨论】:

    猜你喜欢
    • 2017-12-17
    • 2022-11-16
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    相关资源
    最近更新 更多