本书Structure and Interpretation of Computer Programs的练习1.3要求如下:
定义一个过程,该过程将三个数字作为参数,并返回两个较大数字的平方和。
我设法回答了这个问题,但只针对整数:
use std::cmp;
fn sum_square_largest(x:isize, y:isize, z:isize) -> isize {
x * x + y * y + z * z - min_three(x, y, z) * min_three(x, y, z)
}
fn min_three<T>(v1: T, v2: T, v3: T) -> T where T: Ord {
cmp::min(v1, cmp::min(v2, v3))
}
但是当我将sum_square_largest
函数更改为:
fn sum_square_largest(x:f64, y:f64, z:f64) -> f64 {
x * x + y * y + z * z - min_three(x, y, z) * min_three(x, y, z)
}
它提供以下error: the trait 'core::cmp::Ord' is not implemented for the type 'f64' [E0277]
。
答案 0 :(得分:3)
浮点数不会实现Ord
,因为它们没有总排序。 NaN
与任何值进行比较都是错误的,包括另一个NaN
。
如果您每晚使用Rust,可以使用partial_min,这会使这些案例明确。
您还可以决定在NaN
之类的情况下做什么,然后在f64
上实现包装器类型,并为其实现Ord
,以便它处理情况下。
答案 1 :(得分:0)
如果要避免致命的舍入错误,则需要根据需要编写代码:通过添加三个数字中的两个的平方。否则,如果您有例如x = y = 1和z = -1e100,那么您将遇到灾难性的舍入错误。三个方块的总和四舍五入为1e200,减去相同,结果为0而不是2.
可能更糟糕:如果z = -1e200,那么z * z溢出,三个正方形加起来+ inf,你减去+ inf并得到NaN。
let x1 = if x > y { x } else { y };
let mut x2 = if x > y { y } else { x };
if z > x2 { x2 = z; }
x1 * x1 + x2 * x2;
如果您考虑使用NaN,情况会稍微复杂一些。显然,如果你有两个或三个NaN,结果将是NaN。如果你有一个NaN,你需要决定(a)你不关心,(b)结果应该是NaN还是(c)结果应该是两个数字的平方和NaN的。在情况(b)或(c)中,结果应仅取决于三个值,而不取决于它们的使用顺序。
上述代码涵盖(a)。如果你想要(b),那么你需要确保z如果是NaN则存储到x2中,如果是NaN则x2保持不变。您可以通过将最后一行更改为
来实现此目的if z > x2 || z != z { x2 = z };
如果你想要(c)它有点复杂。
let x1 = if x > y || y != y { x } else { y };
let mut x2 = if x > y || y != y { y } else { x };
if z > x2 || x2 != x2 { x2 = z; }