为什么罗斯特的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发布于 2019-07-20 00:09:27
为什么大多数操作都需要相同类型的操作数?
我们很容易看到2i32 + 2i64应该是4i64,但是对于CPU来说,2i32和2i64是完全不同的,完全不相关的东西。CPU内部的+实际上只是一个硬件,它通常支持两个32位输入或两个64位输入,但不支持一个32位输入和一个64位输入。因此,为了将i32添加到i64中,必须将较短的数字进行符号扩展,然后才能将这两个值插入到ALU。
对于大多数整数和浮点算术运算,通常也是如此:必须对不匹配的类型进行转换才能完成数学运算。在C中,编译器通常将两个操作数提升为可以表示两个值的最小类型;根据上下文的不同,这些隐式转换称为“整数提升”或“通常的算术转换”。然而,在Rust中,编译器通常只知道相同类型的操作,因此您必须通过决定如何转换操作数来选择所需的操作类型。喜欢锈病的人一般认为这是一件好事。
为什么这不适用于u64::pow
并非所有算术操作,即使是在硬件中实现的运算,都接受相同类型的参数。在硬件中(虽然不是在LLVM中),shift指令常常忽略shift参数的更高位(这就是为什么在C移位中调用的整数大小大于未定义行为的原因)。LLVM提供了将浮点数提高到整数幂的使用说明。
这些操作是不同的,因为输入是不对称的,设计者常常利用这些不对称来使硬件变得更快、更小。但是,在u64::pow的情况下,它不是用硬件指令它只是用朴素的锈写的实现的。考虑到这一点,很明显,要求指数为u64是非常不必要的:正如施韦恩的回答所指出的那样,u32非常有能力包含u64可以被提升到的所有可能的幂,因此额外的32位是毫无意义的。
好吧,为什么u32
最后一句同样适用于u16,甚至u8 -- u64不能包含pow(2, 255),因此使用u32似乎同样浪费。然而,也有一些实际的考虑。许多调用约定在寄存器中传递函数参数,因此在32位(或更大)平台上,您将不会看到比这更小的任何好处。许多CPU也不支持本地8位或16位算术,所以这个参数无论如何都必须进行符号扩展,以实现我之前链接到的逐平方运算算法。简而言之,我不知道为什么选择u32,但是这样的事情可能已经被考虑到决策中了。
“公约”的规则在某种程度上受到历史的制约,并支持广泛的历史硬件。生锈只针对LLVM,所以编译器不需要担心底层硬件是否有一个原始的8位add指令;它只是发射add,让LLVM担心它是否会被编译成一个原始指令或模拟32位指令。这就是为什么char + char在C中是int,而i8 + i8在Rust中是i8的原因。
发布于 2019-07-19 22:01:36
这是有根据的猜测。
考虑一下,将u64提升到u32的能力已经超过了任何u64。例如,如果我们取2(最小可能的有效u64),并将其提高到2^10或1024,我们得到的东西比u64大。
179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
没有必要允许一个更大的指数,它只是意味着一个更昂贵的计算,更有可能溢出。
如果u64不能安全地降级为u32,那么它绝对不能作为指数使用。出于同样的原因,u128.pow也只取u32指数。
注意:有pow来检测可能的溢出。
https://stackoverflow.com/questions/57119562
复制相似问题