How to define generic trait or function that accept only certain types

Hi,
New to rust. Actually try to learn rust. I have some background in c++.
While read the documentation I try to make small programs to learn faster and more efficiently.
Didn't find a way to compare floating point numbers with std crates and decide to write my own

use std::num;

trait FloatComparator {
    fn equal(self, other : Self) -> bool;
}

impl<T: impl Float>  FloatComparator for T {

    fn equal(self, other : Self) -> bool
    {
        Self::abs(self - other) <= T::EPSILON
    }
}

Not sure if this is the right direction but while internet says that there should be trait Float in std::num my version of rust says that such a thing do not exist.
Can you explain me how to achieve that thing in proper way (compare floating points number)
And also tell me how to define generic trait that accept types that implement certain trits

According to the IEEE 754 floating-point standard, floating-point numbers cannot be compared equal because of NaNs, which according to the standard can't be compared. That's why there is no equal operator for floating point, in Rust or C or any other language that conforms to the standard.

Assuming you know what you're doing by comparing floating point values and dealing with NaN values, you're on the right track. See https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e5e7822638997eecc21341664b39a310. You'll notice that:

  • Float is defined in the num-traits crate;
  • You have to use the full path up-to Float (use std::num if it worked would allow you to use num such as in num::Float);
  • You do not want the impl in the type constraint;
  • Float::epsilon() gives you what you want;
  • the final ; after the comparison makes the result (), remove it to return the value.

Hope this helps.

No, you can compare them but it's not good idea because of the inherit error in them.
The best way to compare two floats is by abs(a - b) <= epsilone where epsilon is the expected error.
Nan are rare occurrence and usually you can check for that. Also if you have nan in your calculation you probably done some terribly wrong

So this doesn't fall apart when a or b or both equal NaN? What does NaN - NaN even mean conceptually?

You can't choose what types generic function will accept. They're always open to any type, and all you can do is to require these types to implement some traits.

There's num-traits that predefines some traits for numeric types, but programming this way can get awkward. Consider creating a macro instead. Rust macros behave more like C++ templates.

It look like you are not so much into computer computer science (no offence).
Fast google search can you lead here. https://floating-point-gui.de/errors/comparison/
The reason for floating point comparison to not work as expected a.k.a a == b and yeas there is complication coming from nans if your algorithm expect nan it should check for them anyway and trow an error because any operation with nan operand is ill and return nan and nan is not very useful result.

I'm not sure if I understand you but i'm pretty sure that I can restrict function parameters to types that implement certain traits. There is syntax like

```
fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{
// implementation
}

This is taken from rust book. I'm not just sure if I can Implement trait the same way but it should be possible

Yes, but you can only restrict with traits, and not by specifying a list of types.

1 Like

Yes that perfect. It's even better than c++ equivalent. Do you know if and how I can define trait and make generic implementation that affect certain traits ?

I mean you can create a trait like Float and use it as a bound, but (apart from some clever tricks) you can't stop anyone from implementing Float for their string type if they want to. And the language and the compiler will behave as if anyone could implement that trait on anything at any time.

You'd do well not to draw premature conclusions. You're wrong, I have a formal background in CompSci.

Regardless, that doesn't mean that using arithmetic (which is explicitly defined on numbers) on something that is explicitly not a number (that's what NaN means, btw) is a sound thing to do. Or, you know, even a sane thing to do.

To be clear, the floating point specification says that NaN - NaN = NaN. You could think of it as "invalid value - invalid value still gives an invalid value"

That's my point exactly. The standard defines something that is conceptually completely nonsensical.

Which is why I'm starting to think that it wouldn't be a bad thing if the standard were revised. Or rewritten from scratch.

If you want to be able to check floats for equality, because you know you won't be dealing with NaNs, check out the noisy_float crate.

1 Like

Please understand my point, which is not that I want to compare non-NaN floats. In fact I regard that as a solved issue.

The point is that that issue is caused by something more fundamental, which is the standard itself. Now I understand why they added NaN in the first place (which is to allow the division 0/0 to return some kind of result) but it causes issues down the line because NaN isn't even what it's used as nonetheless: a number.

1 Like

dude I don't wanna be rude but you have to understand that nan is 1) checkable and 2) you should check for it because any operation with nan is not valid operation not only ==, that include +,-, <, > and so on.
The reason that == is not good idea is not nans but rounding error

That's perfect. Did you know if I can make generic implementation of trait that effect anything that have Float or whatever. This is the main thing that I want to learn. If you know can you place simple example

I understand all that perfectly.

Indeed, which just points to another design flaw in the standard.

Hundreds of professional PhD numerical analysts who contributed to or reviewed the IEEE 754 Floating Point standard would not agree with you. There is no ONE solution that satisifes all needs. If your experience with floating point predates the IEEE 754 standard, or included the early days of not-quite-conforming implementations, you probably would view the standard as a great advance on the state of the art, as I do.

Some of the predecessors to IEEE 754 were unbelievably bad. The worst was probably the hexadecimal-base floating point of the IBM 360 series, in which even trivial algorithms like Newton-Raphson iteration to compute a square root could fail to converge.

It is true that, relative to current technology, the handling of NaNs in IEEE 754 leaves a lot to be desired. However, so do other technologies of that prior era, including most of the languages that Rust can supercede.

1 Like