使用浮点数和delta的assert_eq!

37

在 Rust 中,对于两个浮点数和一个误差范围 delta 的断言,是否有一种首选的方法?

比如说...

let a = 3.0;
let b = 2.9999999999;
assert_eq!(a, b, 0.0001); // Imaginary syntax where a ~= b, within 0.0001

12
谨慎使用“绝对误差范围(epsilon)”进行浮点数比较。参考链接:http://floating-point-gui.de/errors/comparison/。 - Shepmaster
4个回答

17

16

还有一个approx crate,可以让您做这些事情:

relative_eq!(1.0, 1.0, epsilon = f64::EPSILON);
relative_eq!(1.0, 1.0, max_relative = 1.0);
relative_eq!(1.0, 1.0, epsilon = f64::EPSILON, max_relative = 1.0);

10

没有内置的可以实现这个功能,但你可以创建自己的宏。

以下是一种"绝对误差"版本的实现,参考了这篇文章

macro_rules! assert_delta {
    ($x:expr, $y:expr, $d:expr) => {
        if !($x - $y < $d || $y - $x < $d) { panic!(); }
    },
}

具体来说,宏assert_delta如果xy的差以及yx的差都大于或等于d("delta"或"epsilon"值,即公差)时会引发 panic。

这种方法不好,因为选择一个固定的 epsilon 只是因为它“看起来很小”,但当被比较的数字非常小时,它实际上可能过大。比较会返回“true”来表示两个相当不同的数字相等。而当数字很大时,epsilon 可能比最小的舍入误差更小,使得比较总是返回“false”。

鉴于先前的实现在各种情况下都会出现问题,一般情况下,您不应该使用它。您可能需要实现一个更健壮的宏,例如检查“相对误差”的宏。


1
我认为这个不起作用。考虑 x=2.0, y=1.0, e=0.0001。我们期望出现恐慌情况。x - y 大约是 1,不小于 epsilon,所以 or 的第一部分是 false,我们测试第二部分。or 的第二部分是 y - x,大约是 -1,绝对小于 epsilon,所以条件返回 true。然后,我们否定条件,最终分支是 false,没有触发恐慌情况,尽管两个数字的绝对差大于 1。我有什么遗漏吗? - ggorlen
3
这绝对是错误的实现。应该使用&&而不是||。 - Andraž Tori

6

还有另一个完整的组件assert_approx_eq解决了这个问题,比float-cmp更好。

use assert_approx_eq::assert_approx_eq;

let a = 3f64;
let b = 4f64;

assert_approx_eq!(a, b); // panics
assert_approx_eq!(a, b, 2f64); //does not panic
assert_approx_eq!(a, b, 1e-3f64); // panics

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接