4 个版本
0.1.3 | 2024年5月13日 |
---|---|
0.1.2 | 2024年5月12日 |
0.1.1 | 2024年5月4日 |
0.1.0 | 2024年5月4日 |
#18 在 #extend
30KB
482 行
approx_derive
approx-derive
通过两个 derive 宏 AbsDiffEq
和 RelativeEq
扩展了流行的 approx
库。这允许快速为这些类型生成与 approx
库中提供的宏一起使用的实现。
文档
访问 docs.rs 查看文档。
lib.rs
:
此库为 AbsDiffEq 和 RelativeEq 特性提供了 derive 宏。
这些 derive 宏仅实现了两种特性,使用 ...<Rhs = Self>
。宏通过查看第一个结构体字段或用户指定的任何类型来推断 [AbsDiffEq] 特性的 EPSILON
类型。
以下示例解释了一个可能的用例。
use approx_derive::AbsDiffEq;
// Define a new type and derive the AbsDiffEq trait
#[derive(AbsDiffEq, PartialEq, Debug)]
struct Position {
x: f64,
y: f64
}
// Compare if two given positions match
// with respect to geiven epsilon.
let p1 = Position { x: 1.01, y: 2.36 };
let p2 = Position { x: 0.99, y: 2.38 };
approx::assert_abs_diff_eq!(p1, p2, epsilon = 0.021);
在这种情况下,生成的代码可能如下所示
const _ : () =
{
#[automatically_derived] impl approx :: AbsDiffEq for Position
{
type Epsilon = <f64 as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self :: Epsilon {
<f64 as approx::AbsDiffEq>::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
<f64 as approx::AbsDiffEq>::abs_diff_eq(
&self.x,
& other.x,
epsilon.clone()
) &&
<f64 as approx::AbsDiffEq>::abs_diff_eq(
&self.y,
&other.y,
epsilon.clone()
) && true
}
}
};
AbsDiffEq derive 宏反复调用 abs_diff_eq
方法来检查所有字段是否匹配。
字段属性
跳过字段
有时,我们只想比较某些字段,而完全忽略其他字段。
#[derive(AbsDiffEq, PartialEq, Debug)]
struct Player {
hit_points: f32,
pos_x: f32,
pos_y: f32,
#[approx(skip)]
id: (usize, usize),
}
let player1 = Player {
hit_points: 100.0,
pos_x: 2.0,
pos_y: -650.345,
id: (0, 1),
};
let player2 = Player {
hit_points: 99.9,
pos_x: 2.001,
pos_y: -649.898,
id: (22, 0),
};
approx::assert_abs_diff_eq!(player1, player2, epsilon = 0.5);
字段类型转换
由多个字段组成且具有不同数值类型的结构体,在没有额外提示的情况下无法推导。毕竟,我们应该指定如何处理这种类型不匹配。
#[derive(AbsDiffEq, PartialEq, Debug)]
struct MyStruct {
v1: f32,
v2: f64,
}
我们可以使用 #[approx(cast_field)]
和 #[approx(cast_value)]
属性来实现这一目标。
#[derive(AbsDiffEq, PartialEq, Debug)]
struct MyStruct {
v1: f32,
#[approx(cast_field)]
v2: f64,
}
现在第二个字段将被转换为推断的 epsilon 值的类型(f32
)。我们可以通过测试是否通过此过程会丢失 f64::MIN_POSITIVE
的大小变化来检查这一点。
let ms1 = MyStruct {
v1: 1.0,
v2: 3.0,
};
let ms2 = MyStruct {
v1: 1.0,
v2: 3.0 + f64::MIN_POSITIVE,
};
approx::assert_relative_eq!(ms1, ms2);
静态值
我们可以为单个字段强制设置静态 EPSILON
或 max_relative
值。
#[derive(AbsDiffEq, PartialEq, Debug)]
struct Rectangle {
#[approx(static_epsilon = 5e-2)]
a: f64,
b: f64,
#[approx(static_epsilon = 7e-2)]
c: f64,
}
let r1 = Rectangle {
a: 100.01,
b: 40.0001,
c: 30.055,
};
let r2 = Rectangle {
a: 99.97,
b: 40.0005,
c: 30.049,
};
// This is always true although the epsilon is smaller than the
// difference between fields a and b respectively.
approx::assert_abs_diff_eq!(r1, r2, epsilon = 1e-1);
approx::assert_abs_diff_eq!(r1, r2, epsilon = 1e-2);
approx::assert_abs_diff_eq!(r1, r2, epsilon = 1e-3);
// Here, the epsilon value has become larger than the difference between the
// b field values.
approx::assert_abs_diff_ne!(r1, r2, epsilon = 1e-4);
结构体属性
默认 Epsilon
【AbsDiffEq】特性和质允许为其关联的 EPSILON
类型指定默认值。我们可以在结构体级别指定此值来控制此值。
#[derive(AbsDiffEq, PartialEq, Debug)]
#[approx(default_epsilon = 10)]
struct Benchmark {
cycles: u64,
warm_up: u64,
}
let benchmark1 = Benchmark {
cycles: 248,
warm_up: 36,
};
let benchmark2 = Benchmark {
cycles: 239,
warm_up: 28,
};
// When testing with not additional arguments, the results match
approx::assert_abs_diff_eq!(benchmark1, benchmark2);
// Once we specify a lower epsilon, the values do not agree anymore.
approx::assert_abs_diff_ne!(benchmark1, benchmark2, epsilon = 5);
默认最大相对偏差
与【默认 Epsilon】类似,我们还可以选择默认的最大相对偏差。
#[derive(RelativeEq, PartialEq, Debug)]
#[approx(default_max_relative = 0.1)]
struct Benchmark {
time: f32,
warm_up: f32,
}
let bench1 = Benchmark {
time: 3.502785781,
warm_up: 0.58039458,
};
let bench2 = Benchmark {
time: 3.7023458,
warm_up: 0.59015897,
};
approx::assert_relative_eq!(bench1, bench2);
approx::assert_relative_ne!(bench1, bench2, max_relative = 0.05);
Epsilon 类型
当不指定任何内容时,宏将从第一个结构体字段的类型推断 EPSILON
类型。在某些场景下,这可能会出现问题,这就是为什么我们还可以手动指定此类型。
#[derive(RelativeEq, PartialEq, Debug)]
#[approx(epsilon_type = f32)]
struct Car {
#[approx(cast_field)]
produced_year: u32,
horse_power: f32,
}
let car1 = Car {
produced_year: 1992,
horse_power: 122.87,
};
let car2 = Car {
produced_year: 2000,
horse_power: 117.45,
};
approx::assert_relative_eq!(car1, car2, max_relative = 0.05);
approx::assert_relative_ne!(car1, car2, max_relative = 0.01);
依赖关系
~270–720KB
~17K SLoC