Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get an error when adding an integer to a floating point?

Tags:

I started learning Rust. I tried this program:

fn main() {      let a = 5;      let b = 5.5;      let k = a + b;      println!("{}", k); } 

and it is showing this error:

error[E0277]: cannot add a float to an integer  --> src/main.rs:4:16   | 4 |      let k = a + b;   |                ^ no implementation for `{integer} + {float}`   |   = help: the trait `std::ops::Add<{float}>` is not implemented for `{integer}` 

Is the code wrong?

like image 219
Jøê Grèéñ Avatar asked Sep 24 '16 14:09

Jøê Grèéñ


1 Answers

The technically correct answer is: because no one has written impl Add<f64> for i32 {}.

The cheeky answer is: because Rust doesn't want you to shoot yourself in the foot.

The longer, potentially more useful answer is...

In computers, integers and floating point numbers both have a limited range, ultimately driven by the number of bits that we use to represent them. In Rust, the default type of an integer that isn't otherwise constrained is a i32, and the default type of a floating point that isn't otherwise constrained is a f64.

Integral types don't allow you to have a fractional part, and floating point types have a limited number of integers they can exactly represent. If Rust let you add these two types, it would be making a decision for you about which piece of data was less important, which is not really the kind of thing you want your systems programming language to do!

Here are the options I can see:

  1. Raise an error, forcing the programmer to pick which data type they need.
  2. Automatically convert both numbers to an integer, discarding any potential fractional values.
  3. Automatically convert both numbers to floating point, improperly representing larger integral values.

Of those choices, only an error is reasonable.

There's also the potential to introduce a type that can precisely handle arbitrary precision. Unfortunately, those types are no longer "cheap" for the processor to operate on, so you'd have to trade off performance.

If the programmer wishes to perform some conversion, then you can cast the value using as or From:

f64::from(a) + b; 
a + b as i32  

See also:

  • How do I convert between numeric types safely and idiomatically?

Veedrac adds:

[this answer gives] the impression that 0u32 + 0u64 should work, but Rust doesn't do any numeric promotions, even if promotion would be lossless. Also, i32f64 is a lossless promotion, since f64 has a 52-bit mantissa.

While these types of widening promotions would indeed be lossless, they would involve implicitly increasing your memory requirements. What used to only take 32 bits now takes 64 bits, for example. Beyond the memory requirements, there's also semantic considerations. If a value should only require a u8 (0-255), then it doesn't make sense to increase it by a value that might be beyond that range. Knowing that it is appropriate to do such a transformation falls solely on the programmer.

Using From can ensure that you only use lossless number conversion.

like image 94
Shepmaster Avatar answered Oct 11 '22 17:10

Shepmaster