rust - 如何有效地将数字n转换为设置了那么多位数的整数?例如8-> 0b11111111?

标签 rust

您如何有效地将数字n转换为设置了这么低的低有效位的数字?也就是说,对于8位整数:

0 -> 00000000
1 -> 00000001
2 -> 00000011
3 -> 00000111
4 -> 00001111
5 -> 00011111
6 -> 00111111
7 -> 01111111
8 -> 11111111
似乎微不足道:
fn conv(n: u8) -> u8 { (1 << n) - 1 }
但这不是:
thread 'main' panicked at 'attempt to shift left with overflow', src/main.rs:2:5
playground
这是因为在Rust中,不能将N位的数字左移N位。在许多体系结构上,这是未定义的行为。u8::MAX >> (8 - n)也不起作用,因为右移具有相同的溢出限制。
那么,在没有查找表或条件表的情况下实现此目标的有效方法是什么?或者这是不可能的,因此必须采取一种类似的实现:
fn conv2(n: u8) -> u8 {
    match 1u8.checked_shl(n.into()) {
        Some(n) => n - 1,
        None => u8::MAX,
    }
}

最佳答案

对于任何小于最大 native 整数大小的位宽度,最有效的方法几乎肯定是使用较宽的类型进行计算,然后将其范围缩小为输出类型:

pub fn conv(n: u8) -> u8 {
    ((1u32 << n) - 1) as u8
}
rustc 1.50 at -C opt-level=1 or higher将其呈现为四个没有分支或间接指示的指令,这可能是x86-64的最佳选择。
example::conv:
        mov     ecx, edi
        mov     eax, 1
        shl     eax, cl
        add     al, -1
        ret
在大多数(全部?)32位或更大的平台上,使用u8u16而不是u32进行数学运算不会获得任何好处;如果必须使用32位寄存器,则最好也使用整个寄存器(这也是certain integer methods only accept u32 的原因)。
即使您编写match,也可能不会将其编译为分支。在我的测试中,以下功能
pub fn conv(n: u32) -> u64 {
    match 1u64.checked_shl(n) {
        Some(n) => n - 1,
        _ => !0,
    }
}
编译时不带分支,只有cmov指令,并使用(软件仿真的)u128pretty similar看起来等效。在假设match是一个问题之前,请务必先进行概要分析。

关于rust - 如何有效地将数字n转换为设置了那么多位数的整数?例如8-> 0b11111111?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66287458/

相关文章:

rust - 如何为 dyn Trait 对象的 Vec 获取 fmt::Debug 的实现?

struct - 为什么我不能多次调用变异函数?

c - C 语言中 Scala 的 Option 或 Rust 的 Result 错误处理

rust - BTreeSet.range 替代

rust - 在遍历 Vec 的元素时使用 powi

rust - Rayon 如何防止在线程之间使用 RefCell<T>、Cell<T> 和 Rc<T>?

rust - 为什么不能在同一结构中存储值和对该值的引用?

macros - 宏可以将数组/向量扩展为多个索引参数吗?

rust - 由于实验功能无法安装沙沙声

rust - 什么优化让我的 Rust 程序变得如此快?