将结构与 rust 中的浮点数进行比较

Posted

技术标签:

【中文标题】将结构与 rust 中的浮点数进行比较【英文标题】:Comparing Structs with floating point numbers in rust 【发布时间】:2021-08-29 06:20:17 【问题描述】:

由于精度错误,使用浮点数 f64 时我的测试失败。

Playground:

use std::ops::Sub;

#[derive(Debug, PartialEq, Clone, Copy)]
struct Audio 
    amp: f64,


impl Sub for Audio 
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output 
        Self 
            amp: self.amp - other.amp,
        
    


#[test]
fn subtract_audio() 
    let audio1 = Audio  amp: 0.9 ;
    let audio2 = Audio  amp: 0.3 ;

    assert_eq!(audio1 - audio2, Audio  amp: 0.6 );
    assert_ne!(audio1 - audio2, Audio  amp: 1.2 );
    assert_ne!(audio1 - audio2, Audio  amp: 0.3 );

我收到以下错误:

---- subtract_audio stdout ----
thread 'subtract_audio' panicked at 'assertion failed: `(left == right)`
  left: `Audio  amp: 0.6000000000000001 `,
 right: `Audio  amp: 0.6 `', src/lib.rs:23:5

如何测试带有f64 之类的浮点数的结构?

【问题讨论】:

这能回答你的问题吗? Is floating point math broken? 您的问题不在于您的Sub 实现,而在于PartialEq 的派生实现。最好手动实现,测试该值是否在您期望的公差范围内。 @eggyal 我了解浮点数,谢谢。你会说实施PartialEq 比我发布的答案更好吗?谢谢。 派生的PartialEq 实现对于包含浮点数的结构来说是毫无用处的,并且可能会导致意外且难以追踪的错误——所以我绝对建议删除它。如果由于其他原因该结构仍然需要实现PartialEq,那么无论如何您都需要手动执行...之后您的原始assert_eq 将按预期工作。如果您没有任何其他理由实施PartialEq,那么我想这取决于您使用哪种方法,但我认为实施该特征可以更清楚地捕捉意图。 当然,如果您在比较过程中的容差取决于上下文,那么实现PartialEq 可能是个坏主意。 【参考方案1】:

如果用没有结构的数字进行比较,

let a: f64 = 0.9;
let b: f64 = 0.6;

assert!(a - b < f64:EPSILON);

但是对于结构,我们需要采取额外的措施。 首先需要使用PartialOrd 进行派生,以便与其他结构进行比较。

#[derive(Debug, PartialEq, PartialOrd)]
struct Audio ...

接下来创建一个结构体进行比较

let audio_epsilon = Audio  amp: f64:EPSILON ;

现在我可以定期比较(assert! 而不是assert_eq!

assert!(c - d < audio_epsilon)

另一种解决方案是手动实现PartialEq

impl PartialEq for Audio 
    fn eq(&self, other: &Self) -> bool 
        (self.amp - other.amp).abs() < f64::EPSILON
    

【讨论】:

Nb,除非您知道 LHS 始终大于 RHS(因此减法始终为正),否则您需要在与您的比较之前取其 绝对值所需的公差。 @eggyal -5--5 == 0,除非你在这里谈论可能的溢出是可能的,但我选择保持简单,因为 f64 永远不会发生这种情况。但是是的,生产代码可能希望避免任何风险。 @Stargateur:如果c(或self.amp)小于d(或other.amp),那么c - d(或self.amp - other.amp)将是负数,因此必然小于与f64::EPSILON 相比,无论它们有多大差异。 @Stargateur:仅供参考,我回滚了您的最新编辑,因为.abs() 非常好。 请注意,f64:EPSILON 可能不适合在这里使用。例如如果中间值明显大于 1,或者涉及大量中间计算,则精度会降低。请注意,考虑到问题中的值,这很好,但通常可能不会达到您的预期

以上是关于将结构与 rust 中的浮点数进行比较的主要内容,如果未能解决你的问题,请参考以下文章

如何解析八进制字符串作为Rust中的浮点数?

与霓虹内在函数中的浮点数比较

java中的浮点数相加

Lua和C++中的浮点数的比较

Lua和C++中的浮点数的比较

C中的浮点数与预期略有不同