我有一个usize
确实击中了非常大的值。我还以isize
的形式对其应用了增量。在不损失任何精度的情况下应用增量的最佳方法是什么?
fn main() {
let mut big_indexer: usize = 4295032832; // 2^32 + 2^16
let delta: isize = -65792; // 2^16 + 2^8
let big_indexer = (big_indexer as isize) + delta // Can't do this b/c overflow
let big_indexer = big_indexer + (delta as usize) // Can't do this b/c lose negative number
// This is ugly
if delta < 0 {
let big_indexer -= delta.abs() as usize;
} else {
let big_indexer += delta.abs() as usize;
}
}
有两种方法:
isize
范围内(例如选择锈std
)usize
并亲自签署(显然是我的首选)。但是实施取决于您;例如,您可以使用bool
来告诉您偏移量是差值还是加法,或者使用枚举:
fn foo(n: usize, offset: usize, sub: bool) -> Option<usize> {
(if sub {
usize::checked_sub
} else {
usize::checked_add
})(n, offset)
}
enum OffSet {
Neg(usize),
Pos(usize),
}
fn bar(n: usize, offset: OffSet) -> Option<usize> {
match offset {
OffSet::Pos(offset) => n.checked_add(offset),
OffSet::Neg(offset) => n.checked_sub(offset),
}
}
fn main() {
let n = 4295032832; // 2^32 + 2^16
let offset = 65792; // 2^16 + 2^8
let sub = true;
assert_eq!(Some(n - offset), foo(n, offset, sub));
assert_eq!(Some(n - offset), bar(n, OffSet::Neg(offset)));
}
这一点都不难看;您只需要使用一些特征来隐藏逻辑,然后就可以使用它。
取决于您的机器,usize is either 32 bit or 64 bit与isize相同,除了第一位是符号位。因此,不知道索引器的上限,安全的方法是将两个值都转换为i128
:
let big_indexer = (big_indexer as i128) + delta as i128
Stargateur's answer通常是很好的建议,但让我们假设您不能只是重写API以消除isize
,也不能限制usize
的范围。在这种情况下,您可以将delta
强制转换为usize
并显式使用换行算法:
// DON'T COPY THIS LINE unless you read the caveat below first
big_indexer = big_indexer.wrapping_add(delta as usize);
这适用于问题中的示例,但有一个“大”警告:如果delta
和big_indexer
均为正,并且它们的总和将溢出,则会自动换行。如果您保证使用的数字在范围内,那很好。如果需要将isize
添加到usize
和检测溢出,则返回“丑陋的” if
表达式。
fn add_offset(big_indexer: usize, delta: isize) -> Option<usize> {
if delta < 0 {
big_indexer.checked_sub(delta.wrapping_abs() as usize)
} else {
big_indexer.checked_add(delta as usize)
}
}
如果您经常使用换行算法,则可能需要使用std::num::Wrapping
结构以使其更加方便。