Rust基础(05) 类型转换
类型转换
as
语法: let a =b as type
举例: let a = 8i32 as u32
整形之间
- 位数相同的整形之间(如 i32->u32): no-op(二进制值完全不变,只改变数据类型)
- 高位转低位(i32->u8): 截断
- 低位转高位(i8->u32):
- 原类型为无符号: 补零
- 原类型为有符号: 添加符号
浮点与整形
- 浮点->整形: 去尾
- 整形->浮点: 整形的浮点表示
浮点&浮点
- f32->f64: 完美转换
- f64->f32: 最接近的可能值
几个解释
NOP
no-op 指数据指向的二进制值完全不发生变化,只改变了其在 Rust 中的数据类型.
let a = -8i8;
let a = a as u8;
如上代码希望将一个值为-8
的 i8
类型转换为 u8
类型,结果为248
.这是因为 -8i8
的二进制值为b1111_1000
,将其按照无符号整形换算为10进制则变成:248
.虽然a
的数据类型发生了改变,但是其指向的二进制值却并没有发生改变--你也可以说其指向的二进制值没有改变,这是对这个值的解释发生了改变.
而 no-op 发生在相同位数的整形之间: i8<->u8
, i16<->u16
, i32<->u32
, i64<->u64
, i128<->u128
.
PS: 关于这一段的具体计算参考文末的源码,反码,补码部分
截断
当一个高位的整形转化为低位的整形的时候就会发生截断.
let a = 200i16;
let a = a as i8;
如上代码希望将一个值为 200
的 i16
类型转换为 i8
类型,结果为-56
.之所以结果-56
是因为发生了截断,那么到底是如何截断的呢?200i16
的二进制值为 b0000_0000_1100_1000
,u8
位8位,所以截取后8位为 b1100_1000
,然后按照有符号整形转化为10进制则为-56
.
补位
由于是从低位数向高位数转换,所以并不存在任何损失(如同把容量为8L的水杯中的水倒入容量为16L的水杯当中).因此从低位数向高位数转换只需要"扩容",所以只需要在多出来的位数上填0即可,对于有符号整形需要按照符号位填充(正数填0,负数填1).如-8i8->-8i16
就会从b1111_1000
变成b1111_1111_1111_1000
,而8i8->8i16
则是从b0000_1000
变为b0000_0000_0000_1000
.
transmute
as
只允许进行安全转化,上面的所有操作虽然有可能损失原有值的精度,但是并不存在安全隐患.而 std::mem::transmute
则允许你进行一些不安全的操作.如下面的代码
use std::mem; // 要使用 transmute 必须导入此包
fn main() {
// 此操作必须在 unsafe 代码快中完成
unsafe{
let a =[0u8, 1u8, 0u8, 0u8];
let b: u32 = mem::transmute(a);
println!("{}", b);
}
}
补充(源码,反码,补码的计算)
PS: 以下均以有符号8位二进制数为例(最高位为符号位,1表示负数,0表示正数)
计算方式
正数的源码,反码,补码都是其本身.负数遵循如下计算规则:
把一个负数(-13)的绝对值(13)直接换算成二进制(0000_1101
),将最高位(符号位)改为1(负),得到的二进制数(1000_1101
)便是源码
.
把源码的最高位(符号位)之外的所有位按位取反(0->1,1->0),得到的二进制数(1111_0010
)便是补码.
把补码加1得到的二进制数(1111_0011
)便是补码.
上文提到的例子
-8i8
的源码: 1000_1000
,反码: 1111_0111
,补码: 1111_1000
.所以当将其转变为u8
的时候,最高位从原来的符号位转变为普通的数位,即 2^7
,所以将其转变为 u8
之后数值才会是 248
.
为何是补码
你很容易发现上面提到的NOP,截断,补位都是在操作补码,而非源码.这是为何呢?废话,当然是因为计算机里面就是这样存的,那为何要存补码而非源码呢?为了解决减法问题.
当计算 5 - 3
的时候实际计算的就是5 + (-3)
,使用源码计算就是: b0000_0101 + b1000_0011 = b1000_1000 = -8
,也就是说5 - 3 = -8
,这是不是不合理?所以这个时候的实际计算是要考虑符号位的--当两数符号位都是0,直接相加;当两数符号位都是1,用除符号位之外的部分相加,最后补充符号位;当两数一个符号位是1另一个符号位是0则需要用绝对值大的减绝对值小的,且不让符号位参与运算.是不是很操蛋?所以科学家发明了补码,并用补码进行运算,所以5 - 3 = 5 + (-3) = b0000_0101 + b1111_1101 = b0000_0010(溢出的位被移除) = 2
,这样直接让符号位参与运算且不需要关注符号位到底是什么是不是很爽?(位计算机学家们疯狂打 CALL)