首页 > 解决方案 > 为什么 Rust 的 u64.pow 期望 u32?

问题描述

为什么 Rust 的u64原语需要一个u32指数?

error[E0308]: mismatched types
  --> src/protagonists.rs:13:25
   |
13 |         return root.pow(self.secret) % prime;
   |                         ^^^^^^^^^^^ expected u32, found u64
help: you can convert an `u64` to `u32` and panic if the converted value wouldn't fit

https://doc.rust-lang.org/std/primitive.u64.html#pow.v

标签: typesrustintegerinteger-arithmetic

解决方案


为什么大多数操作都需要相同类型的操作数?

我们很容易看出,2i32 + 2i64应该是4i64,但对于 CPU,2i322i64完全不同且完全不相关的东西。+CPU 内部实际上只是一个硬件,通常支持两个 32 位输入或两个 64 位输入,但不支持一个 32 位输入和一个 64 位输入。因此,为了将 an 添加i32i64中,必须先将较短的数字符号扩展为 64 位,然后才能将两个值插入 ALU。

大多数整数和浮点算术运算通常也是如此:必须进行转换才能对不匹配的类型进行数学运算。在 C 中,编译器通常将两个操作数提升为可以表示两个值的最小类型;这些隐式转换称为“整数提升”或“通常的算术转换”,具体取决于上下文。然而,在 Rust 中,编译器大多只知道相同类型的操作,因此您必须通过决定如何转换操作数来选择您想要的操作类型。喜欢 Rust 的人通常认为这是一件好事。¹

为什么这不适用于u64::pow

并不是所有的算术运算,即使是那些在硬件中实现的,都接受相同类型的参数。在硬件中(尽管不是在 LLVM 中),移位指令通常会忽略移位参数的高位(这就是为什么在 C 中移位超过整数的大小会调用未定义的行为)。LLVM 提供了将浮点数提高到整数幂的powi指令。

这些操作是不同的,因为输入是不对称的,设计人员经常利用这些不对称性来使硬件更快、更小。u64::pow但是,. _ _ 考虑到这一点,很明显,要求指数是 a是完全没有必要的:正如Schwern 的回答所指出的那样,它完全能够包含所有可能的幂,a可以以任何准确的希望提高到,所以额外的 32比特将毫无意义。u64u32u64

好的,为什么u32

最后一句话同样适用于u16,甚至u8—— a u64can't contains pow(2, 255),所以使用u32似乎几乎一样浪费。但是,也有实际的考虑。许多调用约定在寄存器中传递函数参数,因此在 32 位(或更大)平台上,您不会看到比这更小的任何优势。许多 CPU 也不支持本机 8 位或 16 位算术,因此无论如何都必须对参数进行符号扩展以实现我之前链接的平方乘幂算法。简而言之,我不知道具体为什么u32选择,但这样的事情可能已经成为决定的因素。


¹ C 的规则在某种程度上受到历史的阻碍,并支持广泛的历史硬件。Rust 仅针对 LLVM,因此编译器无需担心底层硬件是否具有原始 8 位add指令;它只是发出add并让 LLVM 担心它是被编译成原始指令还是用 32 位指令模拟。这就是为什么char+在 C 中,而+在Rust 中。charinti8i8i8


推荐阅读