对于浮点数,如何实现三个中两个最大数的平方和?
How can I implement the sum of squares of two largest numbers out of three for floating-point numbers?
本书 Structure and Interpretation of Computer Programs 的练习 1.3 提出以下问题:
Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.
我已经设法回答了这个问题,但仅限于整数:
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]
.
这是什么?我如何定义这个函数来处理浮点数?
浮点数没有实现 Ord
,因为它们没有总排序。 NaN
与任何值进行比较都是错误的,包括另一个 NaN
.
如果你在使用 nightly Rust,你可以使用 partial_min,这使得这些情况变得明确。
你也可以决定在 NaN
这样的情况下要做什么,然后在 f64
上实现一个包装器类型,并为它实现 Ord
,这样它处理那个案子。
如果您想避免致命的舍入错误,您需要按要求编写代码:将三个数字中的两个的平方相加。否则,如果您有例如 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; }
本书 Structure and Interpretation of Computer Programs 的练习 1.3 提出以下问题:
Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.
我已经设法回答了这个问题,但仅限于整数:
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]
.
这是什么?我如何定义这个函数来处理浮点数?
浮点数没有实现 Ord
,因为它们没有总排序。 NaN
与任何值进行比较都是错误的,包括另一个 NaN
.
如果你在使用 nightly Rust,你可以使用 partial_min,这使得这些情况变得明确。
你也可以决定在 NaN
这样的情况下要做什么,然后在 f64
上实现一个包装器类型,并为它实现 Ord
,这样它处理那个案子。
如果您想避免致命的舍入错误,您需要按要求编写代码:将三个数字中的两个的平方相加。否则,如果您有例如 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; }