Как умножать/делить/складывать/вычитать числа разных типов?
Я работаю с вторым изданием справочника по Rust и решил попробовать создать классический конвертер градусов Цельсия в Фаренгейты.
Вот мой код:
fn c_to_f(c: f32) -> f32 {
return ( c * ( 9/5 ) ) + 32;
}
При компиляции этого кода с помощью cargo build
возникает ошибка времени компиляции:
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}`
Как новичок в Rust, я интерпретирую это как то, что нельзя умножать числа с плавающей точкой и целые числа. Я решил эту проблему, сделав все свои константы числами с плавающей точкой:
fn c_to_f(c: f32) -> f32 {
return ( c * ( 9.0 / 5.0 ) ) + 32.0;
}
Тем не менее, у меня остались сомнения. Поскольку я пришёл из языков C/C++/Java/Python, мне было удивительно узнать, что нельзя просто выполнять арифметические операции над числами разных типов. Правильно ли это — просто преобразовывать их в один и тот же тип, как я сделал в данном случае?
1 ответ(ов)
TL;DR: as
— это самый распространенный способ преобразования между примитивными числовыми типами, но его использование требует размышлений.
fn c_to_f(c: f32) -> f32 {
(c * (9 as f32 / 5 as f32)) + 32 as f32
}
В этом примере разумнее сразу использовать литералы с плавающей запятой:
fn c_to_f(c: f32) -> f32 {
(c * (9. / 5.)) + 32.
}
Настоящая проблема заключается в том, что выполнение арифметики с перемешанными типами несколько усложнено.
Когда вы умножаете1 T
на T
, вы обычно ожидаете получить результат типа T
, по крайней мере, для базовых типов.
Однако при смешивании типов возникают некоторые сложности:
- смешивание знаков (signedness),
- смешивание точности (precision).
Так, например, какой идеальный результат у выражения i8 * u32
? Наименьший тип, который может вместить все значения i8
и u32
, — это i64
. Должен ли он быть результатом?
В качестве другого примера, какой идеальный результат у f32 * i32
? Наименьший тип, который может вместить все значения f32
и i32
, — это f64
. Должен ли он быть результатом?
Идея такого расширения мне кажется довольно запутанной. Это также имеет влияние на производительность (операции с f32
могут выполняться значительно быстрее, чем операции с f64
, особенно при векторизации).
Из-за этих проблем Rust на данный момент требует от вас быть четким: какой тип вы хотите использовать для вычисления? Какой тип имеет смысл в вашей конкретной ситуации?
А затем приводите типы должным образом, используя as
, и учитывайте, какой режим округления применять (.round()
, .ceil()
, .floor()
или .trunc()
при переходе от числа с плавающей запятой к целочисленному).
1 Сложение, вычитание и деление работают аналогичным образом.
Какое наибольшее целочисленное значение в JavaScript, при котором не теряется точность?
Как выполнить целочисленное деление и отдельно получить остаток в JavaScript?
Почему println! не работает в юнит-тестах Rust?
`/` против `//` для деления в Python
Можно ли пометить функцию как устаревшую?