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。