Weird output 4.2 + 3.1 != 7.3 but 4.1 + 3.2 = 7.3 ?!

While trying with fn-traits unstable feature, I never got 4.2 + 3.1 = 7.3, my code is below:

#![feature(unboxed_closures)]
#![feature(fn_traits)]

struct Adder;

impl FnOnce<(i32, i32)> for Adder {
    type Output = i32;
    extern "rust-call" fn call_once(self, args: (i32, i32)) -> i32 {
      args.0 + args.1
    }
}

impl FnOnce<(f32, f32)> for Adder {
    type Output = f32;
    extern "rust-call" fn call_once(self, args: (f32, f32)) -> f32 {
      args.0 + args.1
    }
}

impl FnOnce<(f64, f64)> for Adder {
    type Output = f64;
    extern "rust-call" fn call_once(self, args: (f64, f64)) -> f64 {
      args.0 + args.1
    }
}

fn main() {
   // let adder = Adder{};
    println!("{}", Adder{}(4, 3));        // gives: 7
    println!("{}", Adder{}(4.2_f32, 3.1_f32));    // gives: 7.2999997
    println!("{}", Adder{}(4.2, 3.1));            // gives: 7.300000000000001
}

Here is the playground

I noticed the same output even with simple operation:

fn main() {
    println!("{}", 4.2_f32 + 3.1_f32);  // 7.2999997 is wrong
    println!("{}", 4.1_f32 + 3.2_f32);  // 7.3 is correct
    println!("{}", 4.2 + 3.1); // 7.300000000000001 is wrong
    println!("{}", 4.1 + 3.2); // 7.3 is correct
}

This is an issue of limited floating point accuracy, it will be here even when adding numbers directly - playground. Check this, for example.

3 Likes

If you have to rely on predictable mathematical accuracy (for example you are doing financial calculations), never use floating point arithmetic (so types prefixed with f in Rust). The way to go is fixed point aritthmetic. There is no native type for fixed point decimal arithmetic, but decimal crate does its job.

To broaden the topic, rationals give you pretty good accuracy as well as predictability and you can do other cool stuff with them. I might write a crate for that sometime.

In this case, (42 + 31)/10 = (41 + 32) / 10, with no room for errors.

http://0.30000000000000004.com/

7 Likes

hajsf,

Nothing weird about that. Even Javascript agrees:

> 4.1 + 3.2 == 7.3
true
> 4.2 + 3.1 == 7.3
false

You have just discovered that you don't understand floating point numbers. Most computers/languages today use the IEEE 754 standard for floating point number representation https://en.wikipedia.org/wiki/IEEE_754 Given that we are representing decimal numbers in binary there are rounding "errors" all over the place. It's just not possible to have exact representations for most real numbers.

If you sit down with pencil an paper out work through those sums using the binary representation you will see why it seems to go wrong.

In general one should never compare floating point numbers for equality. Things that look like they should be equal usually are not, like your example:

> 4.2 + 3.1 == 4.1 + 3.2
false 
1 Like

For an in-depth treatment of how weird IEEE-754 floating point gets and how to handle it properly, I recommend reading through David Goldberg's classic "What Every Computer Scientist Should Know About Floating-Point Arithmetic" guide. Various copies are available online.

5 Likes

If you realy want to compare two floats you can check if their difference is less than the machine's minimal float-step. This value is usually somewhere available as a constant called EPSILON. In Rust is is either std::f32::EPSILON or std::f64::EPSILON. For example:

fn compare_float(f1: f32, f2: f32) -> bool {
    f1 - f2 < 2f32 * std::f32::EPSILON
}
1 Like

As the entry for Rust in the page referred to by @Hyeonu points out, Rust already has rational number support from the num crate.

2 Likes