为什么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

最佳答案

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

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

对于大多数整数和浮点算术运算,通常都是这样:必须进行转换才能对不匹配的类型进行数学运算。在C语言中,编译器通常将两个操作数提升为可以表示两个值的最小类型;根据上下文,这些隐式转换称为"integer promotions" or "usual arithmetic conversions"。但是,在Rust中,编译器通常只知道相同类型的操作,因此您必须通过确定如何转换操作数来选择所需的操作类型。喜欢Rust的人通常认为这是一件好事。¹

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

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

这些操作是不同的,因为输入是不对称的,设计人员经常利用这些不对称性来使硬件更快,更小。但是,对于u64::pow而言,它不是通过硬件指令it's just written in plain Rust来实现的。牢记这一点,很明显,不需要将指数作为u64:正如Schwern's answer所指出的那样,u32不仅能够包含u64可以提高到所有精度的所有可能能力,因此多余的32位将毫无意义。

好吧,为什么要u32呢?

最后一句话对于u16甚至u8同样适用-u64不能包含pow(2, 255),因此使用u32似乎几乎是浪费的。但是,也有实际考虑。许多调用约定在寄存器中传递函数参数,因此在32位(或更大)的平台上,减小该值不会带来任何好处。许多CPU还不支持 native 8位或16位算术,因此无论如何都必须对自变量进行符号扩展才能实现我以前链接的平方乘幂算法。简而言之,我不知道为什么选择u32,但是这种情况可能是决定的原因。

¹C的规则在一定程度上受历史的限制,并支持各种历史硬件。 Rust仅以LLVM为目标,因此编译器无需担心底层硬件是否具有原始的8位add指令;它只是发出add并让LLVM担心它会被编译成原始指令还是被32位指令所模拟。这就是为什么char + char在C语言中是int,而i8 + i8在Rust中是i8的原因。

关于types - 为什么Rust的u64.pow期望使用u32?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57119562/

10-10 18:34
查看更多