将字节转换为浮点数是否安全,或者可能会产生未定义的行为?

2024-01-27

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

评论给这个答案 https://stackoverflow.com/a/43708483/432509提示从原始字节转换浮点数可能存在一些问题。


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。特别是,byteordercrate 提供了一种安全直观的方法来从字节流中读取数字。

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 的历史上有一段时间是不确定的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将字节转换为浮点数是否安全,或者可能会产生未定义的行为? 的相关文章

随机推荐