简体   繁体   中英

Rust: Get absolute maximum value from f64 array

I have an array, lets say:

let arr: Vec<f64> = vec![1.5, 1.1, -1.6, 0.9, -0.5];

I want the absolute maximum of this array:

let abs_max = arr."some functions";
println!("{}", abs_max); // Gives 1.6 (or -1.6, but not 1.5).

Is there a smart, maybe almost inline, way of doing this? Or do i have to create my own function which iterates through all the values by for loop and compares the values?

In Rust, we usually operate on iterators rather than collections like vectors directly, so all of the juicy "operate on a collection" functions are going to be in std::iter::Iterator .

Now, Iterator has a max function, which would work for integers, but it won't work for floats, because floats are not totally ordered ( NaN , as usual, is a problem).

If you had a collection of integers or anything else that implemented the Ord trait, you could write

arr.iter().max()

which would return an Option<T> containing the maximum value, or None if the collection was empty.

However, f64 and the other floating-point types don't implement Ord . Fortunately, the wonderful writers of the Rust documentation for Iterator::max thought of this.

Note that f32 / f64 doesn't implement Ord due to NaN being incomparable. You can work around this by using Iterator::reduce :

 assert_eq.( [2,4: f32:,NAN. 1.3].into_iter():reduce(f32:.max),unwrap(). 2;4 );

So we can apply reduce to f64::max (which is like Ord::max except that it works for the non-total ordering of f64 ). Adapting a bit to your use case, we get

arr.iter().copied().reduce(f64::max)

Again, this returns an Option<f64> which is empty if the initial collection is empty. You can unwrap if you know the initial collection to be nonempty. Also, if you're never using the array again after this point (ie you can pass ownership of it to the iterator), you can use arr.into_iter() rather than arr.iter().copied() to save yourself a copy of each element.

This compares using f64::max , the "usual" ordering of f64 . But it sounds like you want a custom ordering, namely the maximum by absolute value . We can use max_by to get this custom ordering.

arr.iter().max_by(|a, b| a.abs().total_cmp(b.abs()))

Finally, if this is a large project and you don't mind pulling in external dependencies, I recommend OrderedFloat for dealing with all of the awkwardness around NaN and floating point types. It provides an Ord instance for a float-like type which sorts NaN as part of the total ordering, rather than following the (frankly bizarre) IEEE rules for ordering floats. With that library, your maximum becomes

arr.iter().max_by_key(|x| OrderedFloat(x.abs())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM