Rust提供了基于IEEE 754-2008标准的浮点类型。按占据空间大小区分,分别为 f32和f64,其使用方法与整型差别不大。浮点数字面量表示方式有如下几种:
输出结果为:
let f1 = 123.0f64;
let f2 = 0.1f64;
let f3 = 0.1f32;
let f4 = 12E+99_f64;
let f5 : f64 = 2.;
// type f64
// type f64
// type f32
// type f64 科学计数法 // type f64
与整数类型相比,Rust的浮点数类型相对复杂得多。浮点数的麻烦之处在于: 它不仅可以表达正常的数值,还可以表达不正常的数值。
在标准库中,有一个std::num::FpCategory枚举,表示了浮点数可能的状 态:
enum FpCategory {
Nan,
Infinite,
Zero,
Subnormal,
Normal,
}
其中Zero表示0值、Normal表示正常状态的浮点数。其他几个就需要特别解释 一下了。
在IEEE 754标准中,规定了浮点数的二进制表达方式:x=(-1)^s*(1+M) *2^e。其中s是符号位,M是尾数,e是指数。尾数M是一个[0,1)范围内的二进制 表示的小数。以32位浮点为例,如果只有normal形式的话,0表示为所有位数全0, 则最小的非零正数将是尾数最后一位为1的数字,就是(1+2^(-23)) *2(-127),而次小的数字为(1+2(-22))*2^(-127),这两个数字的差距为 2(-23)*2(-127)=2^(-150),然而最小的数字和0之间的差距有 (1+2(-23))*2(-127),约等于2^(-127),也就是说,数字在渐渐减少到0 的过程中突然降到了0。为了减少0与最小数字和最小数字与次小数字之间步长的突 然下跌,subnormal规定:当指数位全0的时候,指数表示为-126而不是-127(和指 数为最低位为1一致)。然而公式改成(-1)s*M*2e,M不再+1,这样最小的数 字就变成2(-23)*2(-126),次小的数字变成2(-22)*2(-126),每两个相 邻subnormal数字之差都是2(-23)*2(-126),避免了突然降到0。在这种状态 下,这个浮点数就处于了Subnormal状态,处于这种状态下的浮点数表示精度比 Normal状态下的精度低一点。我们用一个示例来演示一下什么是Subnormal状态的 浮点数:
fn main() {
// 变量 small 初始化为一个非常小的浮点数
let mut small = std::f32::EPSILON;
// 不断循环,让 small 越来越趋近于 0,直到最后等于0的状态 while small > 0.0 {
small = small / 2.0;
println!(“{} {:?}”, small, small.classify());
}
}
编译,执行,发现循环几十次之后,数值就小到了无法在32bit范围内合理表达 的程度,最终收敛到了0,在后面表示非常小的数值的时候,浮点数就已经进入了 Subnormal状态。
Infinite和Nan是带来更多麻烦的特殊状态。Infinite代表的是“无穷大”,Nan代表 的是“不是数字”(not a number)。
什么情况会产生“无穷大”和“不是数字”呢?举例说明:
fn main() {
let x = 1.0f32 / 0.0;
let y = 0.0f32 / 0.0;
println!(“{} {}”, x, y);
}
编译执行,打印出来的结果分别为inf NaN。非0数除以0值,得到的是inf,0除 以0得到的是NaN。
对inf做一些数学运算的时候,它的结果可能与你期望的不一致:
fn main() {
let inf = std::f32::INFINITY;
println!(“{} {} {}”, inf * 0.0, 1.0 / inf, inf / inf);
}
NaN 0 NaN
NaN这个特殊值有个特殊的麻烦,主要问题还在于它不具备“全序”的特点。示 例如下:
fn main() {
let nan = std::f32::NAN;
println!(“{} {} {}”, nan < nan, nan > nan, nan == nan);
}
false false false
这就很麻烦了,一个数字可以不等于自己。因为NaN的存在,浮点数是不具 备“全序关系”(total order)的。关于“全序”和“偏序”的问题,本节就不展开讲解 了,后面讲到trait的时候,再给大家介绍PartialOrd和Ord这两个trait。