先來簡單瞭解一下浮點數在計算機中的存儲方式。根據IEEE 754標準,單精度浮點數格式以下(全部位取0):編碼
符號位spa |
指數部分code |
尾數orm |
|||||||||||||||||||||||||||||
0blog |
0ip |
0ci |
0get |
0string |
0it |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
單精度浮點數有32個二進制位,左側是高位,右側是低位。最高位被指定爲符號位,0表明正數,1表明負數。指數部分將是2的冪次,其編碼值(即上表指數部分對應的八個二進制位)規定爲指數的實際值加上偏移值2^7-1=127,這是爲了不負數,將[-127, 128]映射到[0, 255],這樣指數部分編碼就能夠簡單地編排爲[00000000, 11111111]。例如指數部分爲00001000,十進制爲8。那麼其所表明的實際指數是8-127=-119,即要乘上2-119。最後23位尾數是不包含整數位的實際有效小數位。規約數的整數位是1,非規約數的整數位是0。
指數部分的編碼值在[1, 2e-2]內,且尾數部分的整數位是1,這樣的浮點數被稱爲規約形式的浮點數。
指數部分的編碼值爲0,尾數非零,這樣的浮點數被稱爲非規約形式的浮點數。
規約浮點數的尾數∈[1, 2),而非規約浮點數的尾數∈(0, 1)。 須要注意,非規約數指數編碼爲00000000,但指數實際值是-126,而非-127。非規約浮點數被IEEE 754-1985標準採用是由於它的漸進式下溢出,而規約浮點數將致使忽然式下溢出,具體原理再也不展開。
設符號位爲s。sign(s)肯定正負:sign(0)=1,sign(1)=-1;指數部分爲e;尾數部分爲f。用(N)2表示二進制數N。
規約形式:sign(s)*2e-127*(1.f)2
非規約形式:sign(s)*2-126*(0.f)2
類別 |
符號位 |
指數部分 |
尾數 |
數值 |
|||||||||||||||||||||||||||||
正負零 |
0/1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
±0 |
正負無窮 |
0/1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
±∞ |
最大規約數 |
0/1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
±(2−2-23) × 2127 ≈ ±3.40e38 |
最大非規約數 |
0/1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
±(1−2−23) × 2-126 ≈ ±1.18e-38 |
最小規約數 |
0/1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
±2−126 ≈ ±1.18e-38 |
最小非規約數 |
0/1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
±2−23 × 2−126 = ±2−149 ≈ ±1.40e-45 |
NaN |
0/1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
非零 |
NaN |
由浮點數的存儲方式能夠看出計算機所能表示的浮點數是有限的,咱們把所能表示的最大正值稱爲上溢值,而把最接近0的正值稱爲下溢值。由表二咱們看到上溢值爲±3.40e38,下溢值爲±1.40e-45。
Wikipedia上機器精度Machine Epsilon是這樣描述的:「Machine epsilon gives an upper bound on the relative error due to rounding in floating point arithmetic」。由於浮點數是離散的,因此實數的表示存在着偏差。例如圓周率這樣的無限不循環小數不可能精確地由某一個浮點數表示。
咱們須要一些具體的量去刻畫這種偏差,以估計結果的準確性。機器精度即是其中之一:它是全部相對偏差的上限。相對偏差是絕對偏差與精確值的比值的絕對值。例如一個精確的實數x,全部單精度浮點數中與x距離最近的數爲y,絕對偏差爲|y-x|,相對偏差即\(|y-x| \over |x|\),而全部相對偏差的上限即是單精度浮點數的機器精度。
對於32位浮點數,指數8位,尾數爲23位。對於兩個指數實際值爲E的相同的浮點數,若它們尾數部分相差(00000000000000000000001)2,即2-23,易見它們是相鄰的。那麼與它們指數相同的實數x與距x最近的浮點數y之間的距離|y-x|必定小於此相鄰兩浮點數的距離2-23 * 2E。能夠取x=1.0(或者其餘任何數),此時實際指數爲0,因此機器精度是\(2^{-23} \times 2^{0} \over 1.0\)。
標準庫<limits>中的numeric_limits類中包含了許多算數特殊值:
其中numeric_limits<float>中float能夠換成int,double等其它類型。
std::string get_binary(float f) { int index_byte, index_bit; unsigned int byte = 0; char ch, *p; std::string bin_f = ""; p = (char *)(&f); for (index_byte = sizeof(float)-1; index_byte>=0; index_byte--) { ch = *(p+index_byte); //從最高位開始取 byte = ch; //將地址中8個二進制位賦值成十進制數 for (index_bit = 1; index_bit<=8; index_bit++) { if (byte >=128) bin_f += "1"; else bin_f += "0"; //判斷首位是1仍是0 byte <<= 1; //將當前位變成首位 byte &= 255; //確保始終8個二進制位 } } return bin_f; }