types - 为什么 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
解决方案
为什么大多数操作都需要相同类型的操作数?
我们很容易看出,2i32 + 2i64
应该是4i64
,但对于 CPU,2i32
是2i64
完全不同且完全不相关的东西。+
CPU 内部实际上只是一个硬件,通常支持两个 32 位输入或两个 64 位输入,但不支持一个 32 位输入和一个 64 位输入。因此,为了将 an 添加i32
到i64
中,必须先将较短的数字符号扩展为 64 位,然后才能将两个值插入 ALU。
大多数整数和浮点算术运算通常也是如此:必须进行转换才能对不匹配的类型进行数学运算。在 C 中,编译器通常将两个操作数提升为可以表示两个值的最小类型;这些隐式转换称为“整数提升”或“通常的算术转换”,具体取决于上下文。然而,在 Rust 中,编译器大多只知道相同类型的操作,因此您必须通过决定如何转换操作数来选择您想要的操作类型。喜欢 Rust 的人通常认为这是一件好事。¹
为什么这不适用于u64::pow
?
并不是所有的算术运算,即使是那些在硬件中实现的,都接受相同类型的参数。在硬件中(尽管不是在 LLVM 中),移位指令通常会忽略移位参数的高位(这就是为什么在 C 中移位超过整数的大小会调用未定义的行为)。LLVM 提供了将浮点数提高到整数幂的powi
指令。
这些操作是不同的,因为输入是不对称的,设计人员经常利用这些不对称性来使硬件更快、更小。u64::pow
但是,在. _ _ 考虑到这一点,很明显,要求指数是 a是完全没有必要的:正如Schwern 的回答所指出的那样,它完全能够包含所有可能的幂,a可以以任何准确的希望提高到,所以额外的 32比特将毫无意义。u64
u32
u64
好的,为什么u32
?
最后一句话同样适用于u16
,甚至u8
—— a u64
can't contains pow(2, 255)
,所以使用u32
似乎几乎一样浪费。但是,也有实际的考虑。许多调用约定在寄存器中传递函数参数,因此在 32 位(或更大)平台上,您不会看到比这更小的任何优势。许多 CPU 也不支持本机 8 位或 16 位算术,因此无论如何都必须对参数进行符号扩展以实现我之前链接的平方乘幂算法。简而言之,我不知道具体为什么u32
选择,但这样的事情可能已经成为决定的因素。
¹ C 的规则在某种程度上受到历史的阻碍,并支持广泛的历史硬件。Rust 仅针对 LLVM,因此编译器无需担心底层硬件是否具有原始 8 位add
指令;它只是发出add
并让 LLVM 担心它是被编译成原始指令还是用 32 位指令模拟。这就是为什么char
+在 C 中,而+在Rust 中。char
int
i8
i8
i8
推荐阅读
- r - 如何在 igraph 中生成非循环随机图?
- ruby-on-rails - ActionController::RoutingError (没有路线匹配 [POST] "/cars/1/consumptions/new"):
- ios - Swift / iOS - 推送新视图控制器时保留 UINavigationItem
- bash - 使用 Ubuntu 的多机 Vagrant - Sinatra 和 PostgreSQL
- c# - 透明的自定义图形用户控件:在运行时变黑
- sql-server - Presto 运行速度比 SQL Server 慢
- c# - 在使用 C#.NET 的方法中正确并行化许多小任务
- c# - 将行数据从 LINQ 中的分组数据转换为列
- mstest - 如何配置 SpecFlow 以使用 MS Test v2 中的 [DoNotParallelize] 属性?
- excel - 将 Excel 行单元格放在彼此下方的一列中,其序列号重复