Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

math - How to multiply/divide/add/subtract numbers of different types?

I'm working through the second edition of the Rust handbook, and decided to try and make the classic Celsius-to-Fahrenheit converter:

fn c_to_f(c: f32) -> f32 {
    return ( c * ( 9/5 ) ) + 32;
}

Compiling this with cargo build will yield the compile-time error:

error[E0277]: the trait bound `f32: std::ops::Mul<{integer}>` is not satisfied
 --> src/main.rs:2:12
  |
2 |     return (c * (9 / 5)) + 32;
  |            ^^^^^^^^^^^^^ the trait `std::ops::Mul<{integer}>` is not implemented for `f32`
  |
  = note: no implementation for `f32 * {integer}`

As a new Rust programmer, my interpretation is that I cannot multiply float and integer types together. I solved this by making all of my constants floating points:

fn c_to_f(c: f32) -> f32 {
    return ( c * ( 9.0/5.0 ) ) + 32.0;
}

This leaves me with reservations. Coming from C/C++/Java/Python, it was surprising to learn that you cannot simply perform arithmetic on numbers of different types. Is the right thing to simply convert them to the same type, as I did here?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

TL;DR: as is the most common way to convert between the primitive numeric types but using it requires thinking.

fn c_to_f(c: f32) -> f32 {
    (c * (9 as f32 / 5 as f32)) + 32 as f32
}

In this example though, it's more reasonable to just use floating point literals to start with:

fn c_to_f(c: f32) -> f32 {
    (c * (9. / 5.)) + 32.
}

The real problem is that doing mixed type arithmetic is a bit complicated.

If you are multiplying1 a T by a T, you generally expect to get a result of type T, at least with the basic types.

When mixing types, however, there are some difficulties:

  • mixing signedness,
  • mixing precision.

So, for example, what is the ideal result of i8 * u32? The smallest type that can encompass the full set of all i8 and u32 values is a i64. Should that be the result?

As another example, what is the ideal result of f32 * i32? The smallest type that can encompass the full set of all f32 and i32 values is a f64. Should that be the result?

I find the idea of having a such widening rather confusing. It also has performance impacts (operations on f32 can be much speedier than operations on f64, once vectorized).

Due to those issues, Rust for now requires you to be explicit: which type do you want the computation to be carried in? Which type makes sense for your particular situation?

And then cast appropriately, using as, and do think about which rounding mode to apply (.round(), .ceil(), .floor() or .trunc() when going from floating point to integral).

1 Adding, Subtracting and Dividing work in similar ways.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...