Проблема, с которой я столкнулся недавно, требует выполнения целочисленных операций с границей на основе битов целочисленного типа.
Например, используя целое число i32
для выполнения add
операции, вот фрагмент псевдокода, чтобы представить идею:
sum = a + b
max(min(sum, 2147483647), -2147483648)
// if the sum is larger than 2147483647, then return 2147483647.
// if the sum is smaller than -2147483648, then return -2147483648.
Для этого я наивно написал уродливый код:
fn i32_add_handling_by_casting(a: i32, b: i32) -> i32 {
let sum: i32;
if (a as i64 + b as i64) > 2147483647 as i64 {
sum = 2147483647;
} else if (a as i64 + b as i64) < -2147483648 as i64 {
sum = -2147483648;
} else {
sum = a + b;
}
sum
}
fn main() {
println!("{:?}", i32_add_handling_by_casting(2147483647, 1));
println!("{:?}", i32_add_handling_by_casting(-2147483648, -1));
}
Код работает хорошо; но мое шестое чувство подсказало мне, что использование преобразования типов проблематично. Таким образом, я попытался использовать традиционную обработку паники (исключений), чтобы справиться с этим ... но я застрял с приведенным ниже кодом (результат паники не может обнаружить переполнение или переполнение):
use std::panic;
fn i32_add_handling_by_panic(a: i32, b: i32) -> i32 {
let sum: i32;
let result = panic::catch_unwind(|| {a + b}).ok();
match result {
Some(result) => { sum = result },
None => { sum = ? }
}
sum
}
fn main() {
println!("{:?}", i32_add_handling_by_panic(2147483647, 1));
println!("{:?}", i32_add_handling_by_panic(-2147483648, -1));
}
Подводя итог, у меня 3 вопроса:
- Подходит ли мое решение для преобразования типов в язык строгой типизации? (Если возможно, мне нужно объяснение, почему это действительно или недействительно.)
- Есть ли другой лучший способ справиться с этой проблемой?
- Может ли паника обрабатывать разные исключения отдельно?
saturating_add
? - person L. F.   schedule 03.09.2020as
может быть проблематичным, поскольку может изменить значение. Чтобы исправить это, используйте преобразование, которое не меняет значение:From
. Например, запишите первое условие какif i64::from(a) + i64::from(b) > i64::from(i32::MAX)
. (То есть, если бы не было очевидно, что проще просто использовать арифметику с насыщением.) - person trentcl   schedule 03.09.2020From
- еще одно новое для меня знание. Если вы не возражаете, не могли бы вы привести пример того, как значение изменяется послеas
преобразования типа? - person Kir Chou   schedule 03.09.2020-10_i32 as u32
- это4294967286
, а1_000_000_000 as i16
- это-13824
. Если вы ошиблись с шириной или подписью, вы можете просто молча получить неправильное значение.From
не имеет этой проблемы, потому что целые числа реализуютFrom
только при преобразовании без потерь. (Для преобразований, которые подвержены ошибкам, вы можете использоватьTryFrom
, который возвращаетResult
, с которым вы можете справиться любым подходящим способом.) - person trentcl   schedule 03.09.2020