浮點數的表示
通常,浮點數表示為:
其中,S取值為0或者1,用來決定浮點數的符號;M是一個二進位制定點小數,稱為尾數,一般用定點原碼小數表示;E是一個二進位制頂點整數,稱為階碼或者指數,用移碼錶示。R是基數(隱含),可以約定為2、4、16等
浮點數的表示範圍
原碼是關於原點對稱的,故浮點數的範圍也是關於原點對稱的,如圖2.10所示
- 運算結果大於最大正數時稱為正上溢,小於絕對值最大負數時稱為負上溢,正上溢和負上溢統稱上溢。資料一旦產生上溢,計算機必須中斷運算操作,進行溢位處理。
- 當運算結果在0至最小正數之間時稱為正下溢,在0至絕對值最小負數之間時稱為負下溢,正下溢和負下溢統稱下溢。資料下溢時,浮點數值趨於零,計算機將其當作機器零處理。
浮點數的規格化
為了在浮點數運算過程中儘可能多地保留有效數字的位數,使有效數字儘量佔滿尾數數位,必須在運算過程中對浮點數進行規格化操作。所謂規格化操作,是指透過調整一個非規格化浮點數的尾數和階碼的大小,使非零浮點數在尾數的最高數位上保證是一個有效值。
-
左規:當運算結果的尾數的最高數位不是有效位,即出現 \(\pm\) 0.0...0x...x的形式時,需要進行左規。左規時,尾數每左移一位、階碼減1(基數為2時)。左規可能要進行多次。
-
右規:當運算結果的尾數的有效位進到數點前面時,需要進行右規,右規只需進行一次。將尾數右移一位、階碼加1(基數為2時)。規時,階碼增加可能導致溢位。
IEEE754標準
單精度格式中包含 1 位符號 s、8 位階碼e和 23 位尾數,:雙精度格式包含1位符號 s、11位階碼e和 52 位尾數 1。基數隱含為2:尾數用原碼錶示。對於規格化的二進位制浮點數,尾數的最高位總是 1,為了能使尾數多表示一位有效位,將這個1隱藏,稱為隱藏位,因此 23 位尾數實際表示了 24 位有效數字。IEEE 754 規定隱藏位1的位置在小數點之前,例如,\((12)_{10} = (1100)_{2}\),將它規格化後結果為 \(1.1 \times 2^{3}\),其中整數部分的“1”將不儲存在 23 位尾數內。
單精度與雙精度浮點數都採用隱藏尾數最高位的方法,因而使浮點數的精度更高。
在 IEEE 754 標準中,指數用移碼錶示,但偏置值並不是通常n位移碼所用的\(2^{n-1}\),而是 \(2^{n-1}-1\)因此,單精度和雙精度浮點數的偏置值分別為 127 和 1023。在儲存浮點數階碼之前,偏置值要先加到階碼真值上。
所以,在IEEE754標準中,規格化單精度浮點數的真值為:
規格化雙精度浮點數的真值為
浮點數能表示的範圍
這個表的意思應該是絕對值的最大值與最小值
階碼為全0或者全1時的特殊意義
定點、浮點表示的區別
-
數值的表示範圍
- 若定點數和浮點數的字長相同,則浮點表示法所能表示的數值範圍遠大於定點表示法。
-
精度
- 雖然擴大了數的表示範圍,但精度降低了。對於字長相同的定點數和浮點數來說,精度卻降低了
-
數的運算
- 浮點數包括階碼和尾數兩部分,運算時不僅做尾數的運算,還要做階碼的運算,而且運算結果要求規格化,所以浮點運算遠比定點運算複雜
-
溢位問題
- 在定點運算中,當運算結果超出數的表示範圍時,發生溢位;在浮點運算中,運算結果超出尾數表示範圍卻不一定溢位,只有規格化後階碼超出所能表示的範圍時,才發生溢位。
浮點數的機器數
最後我們附上一個將浮點數轉為二進位制的C++程式,結合真值來看,可以對浮點數的編碼有更好的理解
#include <iostream>
#include <bitset>
int main() {
float num;
std::cout << "Please enter a floating-point number: ";
std::cin >> num;
union FloatInt {
float f;
unsigned int i;
} data;
data.f = num;
// Using union to access the internal representation of the floating-point number
std::cout << "The machine representation of the floating-point number " << num << " (in hexadecimal): "
<< std::hex << data.i << std::endl; // Print the hexadecimal form
// Convert the integer to a 32-bit binary string
std::bitset<32> binaryRepresentation(data.i);
std::string binaryString = binaryRepresentation.to_string();
// Splitting into sign, exponent, and mantissa parts
std::string sign = binaryString.substr(0, 1);
std::string exponent = binaryString.substr(1, 8);
std::string mantissa = binaryString.substr(9, 23);
// Printing the sign, exponent, and mantissa separately
std::cout << "The machine representation of the floating-point number " << num << " (in binary): "
<< sign << ";" << exponent << ";" << mantissa << std::endl;
return 0;
}