Adding float to integer

To add integer and float numbers together, I understood, there are 2 options, either casting or impl trait.

For casting I used the below code that worked fine:

let x = 1;
let y = x as f32 + 3.0;

If I removed the as f32 casting, I get the below error:

The trait std::ops::Add<{float}> is not implemented for {integer}

How can I do this implementation?

trait

It's not possible for you to implement external traits (like Add) for external types (like floats and integers). This case could only be added directly in the standard library, specifically in core.

But Rust generally discourages mixing types in arithmetic. In C and C++, there are type promotion rules that let this work implicitly, but Rust wants you to be explicit about such type conversions.

6 Likes

Rust has u32 and double, you can add together like this:

fn main() {
    let n: u32 = 15;
    let x: f64 = 6.5;
    let result = x + f64::from(n);
}
2 Likes

The auto conversion of type is dangerous sometime. Here is a simple C example:

#include <stdio.h>

int main() {
    const float bw = 1./3.;

    int d1 = (int) (4./bw);
    int d2 = (int) (4 /bw);
    printf("4/(1/3) is: %d or %d ??\n", d1, d2);

    return 0;
}

4/(1/3) is: 11 or 12 ??

5 Likes

How the 11 poped up, it should be 12 only¡¡

First, remember that 1./3 can't be perfectly represented in a float. GDB tells me that bw is rounded up slightly to 0.333333343. So when we divide "4" by bw, we should actually expect to get something slightly less than 12.

http://c0x.coding-guidelines.com/6.3.1.8.html

First, if the corresponding real type of either operand is long double, the other operand is converted, without change of type domain, to a type whose corresponding real type is long double.

Otherwise, if the corresponding real type of either operand is double, the other operand is converted, without change of type domain, to a type whose corresponding real type is double.

Otherwise, if the corresponding real type of either operand is float, the other operand is converted, without change of type domain, to a type whose corresponding real type is float.

In computing d1, the literal 4. is a double, so bw is converted to double for the division. This computes 11.999999642372142, which is then truncated by (int) to 11.

In computing d2, the literal 4 is an int, so that is converted to float to match bw. Since this has less precision, it rounds up to exactly 12.0000... -- and then (int) gives 12 as well.

2 Likes

I found this type-autoconversion rounding bug in a C software on GitHUB, and I made the simple example. In Rust the type-conversions are explicit, not automatic.

fn main() {
    let bw: f32 = 1./3.;

    let d1: i32 = (4./bw) as i32;
    let d2: i32 = (4 as f32/bw) as i32;
    println!("4/(1/3) --> both are 12: {} {}", d1, d2);
}

Since of this C-bug, I don't like the automatic type conversion, if the good result is important.

1 Like

I wouldn't actually call this example a C bug, per se, but more of a general issue of expecting too much accuracy from floating point. It's just that C's promotion rules tend to hide the actual types used for a given computation.

The Rust equivalent to C's type promotions would be this, which also gives you 11:

    let d1: i32 = (4. / bw as f64) as i32;

Note that Rust lets the type of 4. be inferred by context, either f32 or f64, whereas it's always a double in C.

While this computation in f32 and float gives the exact result you want, it's somewhat lucky that imprecise floating point rounded that way. If you try with 1./7., you'll get 27 in both cases.

3 Likes

My advice would be: don't use floats.

Except for numerical software, stuff like games etc. and other minor exceptions, floats should probably never be used for anything.

Use integers instead. Decide on the precision you want and go with that.

3 Likes

Thanks.
In this C code it looks 1./3 will be rounded correctly if defined double, so as @dpc mentioned, beter to avoid using float.

By "float" I believe he was referring to floating-point types in general. So both float and double. All floating-point types lose precision and will result in weird bugs like this when you convert them to integers. The problem is just less pronounced with double since it has higher precision.

4 Likes

On the topic of floating point precision, there are also crates (and C libraries) that implement arbitrary precision arithmetic.

A few examples, num in pure Rust and gmp which provides bindings to gmplib. Use one of these libraries if you need accurate arithmetic results. Floating point numbers are good for approximations.

3 Likes