由于我不明白的原因,f32和f64没有实现Order。 因为floating point is hard!简短的版本是浮点数有一个特殊的值NaN -不是一个数字。IEEE浮点数规范规定1 < NaN、1 > NaN和NaN == NaN都是false。 Ord说: 形成total order的类型的Trait。 这意味着比较需要具有 * 总体性 *: a ≤ B或b ≤ a 但是我们刚刚看到浮点数没有这个属性。 所以,是的,您需要创建一个 Package 器类型,以某种方式处理large number of NaN values的比较。也许在你的例子中,你可以Assertfloat值永远不是NaN,然后调用常规的PartialOrd trait。下面是一个例子:
use std::cmp::Ordering;
#[derive(PartialEq,PartialOrd)]
struct NonNan(f64);
impl NonNan {
fn new(val: f64) -> Option<NonNan> {
if val.is_nan() {
None
} else {
Some(NonNan(val))
}
}
}
impl Eq for NonNan {}
impl Ord for NonNan {
fn cmp(&self, other: &NonNan) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
fn main() {
let mut v: Vec<_> = [2.0, 1.0, 3.0].iter().map(|v| NonNan::new(*v).unwrap()).collect();
v.sort();
let r = v.binary_search(&NonNan::new(2.0).unwrap());
println!("{:?}", r);
}
fn main() {
let mut v: Vec<f64> = vec![2.0, 2.5, -0.5, 1.0, 1.5];
v.sort_by(f64::total_cmp);
let target = 1.25;
let result = v.binary_search_by(|probe| probe.total_cmp(&target));
match result {
Ok(index) => {
println!("Found target {target} at index {index}.");
}
Err(index) => {
println!("Did not find target {target} (expected index was {index}).");
}
}
}
6条答案
按热度按时间i7uq4tfw1#
由于我不明白的原因,f32和f64没有实现Order。
因为floating point is hard!简短的版本是浮点数有一个特殊的值NaN -不是一个数字。IEEE浮点数规范规定
1 < NaN
、1 > NaN
和NaN == NaN
都是false
。Ord
说:形成total order的类型的Trait。
这意味着比较需要具有 * 总体性 *:
a ≤ B或b ≤ a
但是我们刚刚看到浮点数没有这个属性。
所以,是的,您需要创建一个 Package 器类型,以某种方式处理large number of NaN values的比较。也许在你的例子中,你可以Assertfloat值永远不是NaN,然后调用常规的
PartialOrd
trait。下面是一个例子:hgb9j2n62#
其中一个slice方法是
binary_search_by
,您可以使用它。f32
/f64
实现了PartialOrd
,所以如果你知道它们永远不可能是NaN
,你可以打开partial_cmp
的结果:x8goxv8g3#
自Rust 1.62.0起,名为
.total_cmp()
的浮点数的内置总排序比较方法现在已经稳定。这实现了IEEE 754中定义的总排序,每个可能的f64
位值都被不同地排序,包括正零和负零,以及所有可能的NaN。浮点数仍然不能实现
Ord
,所以它们不能直接排序,但是样板文件已经被减少到一行,没有任何外部导入或恐慌的机会:6rqinv9w4#
如果您确定浮点值永远不会是NaN,那么可以使用decorum中的 Package 器来表达这种语义。具体来说,类型
Ordered
实现了Ord
,每当程序试图做一些无效的事情时,它就会死机:7rtdyuoh5#
https://github.com/emerentius/ord_subset实现了一个
ord_subset_binary_search()
方法,您可以使用它。从他们的自述:
watbbzwu6#
您可以在newtype上使用nutype和
finite
验证来派生Ord
,而不需要太多的样板文件。finite
验证排除了NaN
和Infinity
,这使得nutype能够安全地导出Ord
:下面是一个例子:
输出:
请注意,
Float::new(val)
返回Result
。传递类似Float::new(0.0/0.0)
的内容将返回错误。