为什么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
答案 0 :(得分:5)
这是有根据的猜测。
请考虑将u64提升到u32已经超出了溢出任何u64的能力。例如,如果我们取2(最小的有效u64)并将其提高到2 ^ 10或1024,则得到的结果要比u64大。
1797693134862315907729305190789024733617976978942306572734300811577326758055009631327084773224075360211201138798713933576587897688144166224928474306394741243777678934248654852763022196012460941194594529529520820857688381506823424628814739131105408272371633705-230842637-825
允许更大的指数没有意义,这只是意味着更昂贵的计算更可能溢出。
如果不能将u64安全地降级为u32,则不能确定地将其 用作指数。 u128.pow出于相同的原因也只需要使用u32指数。
注意:这里有overflowing_pow来检测可能的溢出。
答案 1 :(得分:5)
我们很容易看出2i32 + 2i64
应该是4i64
,但是对于CPU,2i32
和2i64
完全不同,并且完全无关的东西。 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
不仅具有包含{ {1}}可能会提高到任何精度的希望,因此多余的32位将毫无意义。
u64
?最后一句话对于u32
甚至是u16
都是正确的-u8
不能包含u64
,因此使用pow(2, 255)
似乎与浪费。但是,也有实际考虑。许多调用约定在寄存器中传递函数参数,因此在32位(或更大)的平台上,减小该值不会带来任何好处。许多CPU还不支持本机8位或16位算术,因此无论如何都必须对自变量进行符号扩展才能实现我之前链接的平方乘幂算法。简而言之,我不知道为什么选择u32
,但是这种情况可能是决定的原因。
¹C的规则在一定程度上受历史的束缚,并支持广泛的历史硬件。 Rust仅以LLVM为目标,因此编译器无需担心基础硬件是否具有原始的8位u32
指令。它只是发出add
并让LLVM担心它会被编译成原始指令还是被32位指令模拟。这就是为什么add
+ char
在C语言中是char
,而int
+ i8
在Rust中是i8
的原因。