Saikub
Saikub

Reputation: 47

Comparing Structs with floating point numbers in rust

My tests fail when using floating point numbers f64 due to precision errors.

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 });
}

I get the following error:

---- 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

How to test for structs with floating numbers like f64 ?

Upvotes: 0

Views: 2322

Answers (1)

Saikub
Saikub

Reputation: 47

If the comparing were to be done with numbers without struct,

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

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

But with structs we need to take extra measures. First need to derive with PartialOrd to allow comparing with other structs.

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

next create a struct for comparison

let audio_epsilon = Audio { amp: f64:EPSILON };

now I can compare regularly (with assert! not assert_eq!)

assert!(c - d < audio_epsilon)

An other solution is to implement PartialEq manually:

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

Upvotes: 3

Related Questions