Why do most operations require operands of the same type?
us很容易看出2i32 + 2i64
应该是4i64
,但对于CPU来说,2i32
和2i64
是完全不同且完全不相关的东西.CPU内部的+
实际上只是一块硬件,通常支持两个32位输入或两个64位输入,但不支持一个32位输入和一个64位输入.因此,为了将i32
和i64
相加,必须将较短的数字符号扩展到64位,然后才能将这两个值插入ALU.
大多数整数和浮点算术运算通常也是如此:必须进行转换才能对不匹配的类型进行计算.在C语言中,编译器通常将两个操作数升级为可以表示两个值的最小类型;根据上下文,这些隐式转换称为"integer promotions" or "usual arithmetic conversions".然而,在Rust中,编译器基本上只知道相同类型的操作,因此必须通过决定如何转换操作数来 Select 所需的操作类型.喜欢 rust 病的人通常认为这是件好事.¹
Why doesn't this apply to u64::pow
?
并非所有的算术运算,即使是硬件实现的算术运算,都接受相同类型的参数.在硬件中(虽然不是在LLVM中),移位指令通常会忽略移位参数的高位(这就是为什么在C中,移位超过整数的大小会调用未定义的行为).LLVM提供powi
instructions,将浮点数提升为整数幂.
这些操作是不同的,因为输入是不对称的,设计师经常利用这些不对称性使硬件更快、更小.不过,在u64::pow
的情况下,它是not,由一条硬件指令实现:it's just written in plain Rust.记住这一点,很明显,要求指数为u64
是完全没有必要的:正如Schwern's answer所指出的,u32
完全能够包含u64
的所有可能的幂,因此额外的32位将毫无意义.
OK, why u32
?
最后一句话同样适用于u16
甚至u8
——u64
不能包含pow(2, 255)
,所以使用u32
看起来几乎是浪费.然而,也有一些实际考虑.许多调用约定在寄存器中传递函数参数,因此在32位(或更大)的平台上,您将看不到比它小的任何优势.许多CPU也不支持本机8位或16位算术,因此无论如何都必须对参数进行符号扩展,以实现我之前链接的平方算法的幂运算.简言之,我不知道为什么 Select u32
,但这类事情可能已经成为决定的因素.
CharStyle_C的规则在某种程度上受到历史的阻碍,并支持各种历史硬件.Rust只针对LLVM,因此编译器不需要担心底层硬件是否有原始的8位add
指令;它只发出add
个指令,让LLVM担心它是否会被编译成一条基本指令,还是会被32位指令模拟.这就是为什么char
+char
在C中是int
,而i8
+i8
在 rust 中是i8
.