如何获取包含浮点数的迭代器的最小值或最大值?

luk*_*uke 3 floating-point iterator rust

我理解为什么浮点数没有实现,Ord但是当我想要懒惰并使用迭代器时,这对我没有特别的帮助.

是否有一种解决方法或一种简单的方法来获取包含浮点数的迭代器的最小值/最小值/ min_by?

我知道可以排序(这很慢)或将其包装在另一种类型中并实现所需的交易(这是冗长的),但我希望有一些更优雅的东西.

小智 10

从 Rust 1.62.0 开始,名为 float 的内置全序比较方法.total_cmp()现已稳定。这实现了 IEEE 754 中定义的总排序,每个可能的f64位值都被明确排序,包括正零和负零,以及所有可能的 NaN。请注意,有些 NaN 排序高于 Infinity,有些 NaN 排序低于 -Infinity,因此在存在 NaN 的情况下“最大值”可能会令人困惑,但它会是一致的。

浮动仍然不会实现Ord,所以它们不会直接排序,但样板已被削减为单行,没有任何外部导入或恐慌的机会:

fn main() {
    let mut a: Vec<f64> = vec![2.0, 2.5, -0.5, 1.0, 1.5];
    
    let maximum = *a.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
    println!("The maximum value was {maximum}.");

    a.sort_by(f64::total_cmp);
}
Run Code Online (Sandbox Code Playgroud)

  • 我可以确认这段代码无法编译,正确的解决方案是 `let Maximum = *a.iter().max_by(|a, b| a.total_cmp(b)).unwrap();` (2认同)

huo*_*uon 9

Floats有自己的方法minmax一致处理NaN的方法,所以你可以折叠迭代器:

use std::f64;

fn main() {
    let x = [2.0, 1.0, -10.0, 5.0, f64::NAN];

    let min = x.iter().fold(f64::INFINITY, |a, &b| a.min(b));
    println!("{}", min);
}
Run Code Online (Sandbox Code Playgroud)

打印-10.

如果您想要不同的NaN处理,可以使用PartialOrd::partial_cmp.例如,如果您希望传播NaN,请折叠:

use std::f64;
use std::cmp::Ordering;

fn main() {
    let x = [2.0, 1.0, -10.0, 5.0, f64::NAN];

    let min = x.iter().fold(f64::INFINITY, |a, &b| {
        match PartialOrd::partial_cmp(&a, &b) {
            None => f64::NAN,
            Some(Ordering::Less) => a,
            Some(_) => b,
        }
    });
    println!("{}", min);
}
Run Code Online (Sandbox Code Playgroud)


She*_*ter 7

如果您知道您的数据不包含 NaN,则通过展开比较来断言该事实:

fn example(x: &[f64]) -> Option<f64> {
    x.iter()
        .cloned()
        .min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
}
Run Code Online (Sandbox Code Playgroud)

如果您的数据可能包含 NaN,则需要专门处理这种情况。一种解决方案是说所有 16,777,214 个 NaN 值彼此相等,并且始终大于或小于其他数字:

use std::cmp::Ordering;

fn example(x: &[f64]) -> Option<f64> {
    x.iter()
        .cloned()
        .min_by(|a, b| {
            // all NaNs are greater than regular numbers
            match (a.is_nan(), b.is_nan()) {
                (true, true) => Ordering::Equal,
                (true, false) => Ordering::Greater,
                (false, true) => Ordering::Less,
                _ => a.partial_cmp(b).unwrap(),
            }
        })
}
Run Code Online (Sandbox Code Playgroud)

有许多可用的 crate 可用于为您提供代码所需的任何语义。


你应该使用partial_cmp(b).unwrap_or(Ordering::Equal),因为它提供了不稳定的结果时,NaN是存在的,但它会导致读者以为他们的处理方式:

use std::cmp::Ordering;
use std::f64;

fn example(x: &[f64]) -> Option<f64> {
    x.iter()
        .cloned()
        .min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
}

fn main() {
    println!("{:?}", example(&[f64::NAN, 1.0]));
    println!("{:?}", example(&[1.0, f64::NAN]));
}
Run Code Online (Sandbox Code Playgroud)
fn example(x: &[f64]) -> Option<f64> {
    x.iter()
        .cloned()
        .min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
}
Run Code Online (Sandbox Code Playgroud)