--- title : 題解:CAL tags : solution --- ## [109/12/06] CAL p7 - IEEE754 ### 題目 輸入十進位浮點數, 要求以IEEE754標準輸出其二進位格式, 且小數部分只取十位二進位值, 多於十位的部分皆忽略不計(皆為0) - EXAMPLE INPUT : ```cmake # Ex 1 : 35.5 # Ex 2 : 69.625 # Ex 3 : 0.312 # Ex 4 : 0 ``` - EXAMPLE OUTPUT : ```cmake # Ex 1 : 0 10000100 00011100000000000000000 # Ex 2 : 0 10000101 00010110100000000000000 # Ex 3 : 0 01111101 00111111000000000000000 # Ex 4 : 0 00000000 00000000000000000000000 ``` --- ### IEEE754標準 將一小數(decimal)數值以位元儲存的方式, 這種小數由於是近似值,故又稱為「浮點數(float)」, 最基本表示法為單精度浮點數 float(32-bits)和雙精度浮點數 double(64-bits) 本題使用的是float(32bits)形式的IEEE754格式, 其數值分別以以下三部分位元組所組成: ![](https://i.imgur.com/aqhcWxK.png) * sign(記號) : 浮點數正負 * exponent(指數) : 二進位偏移值 (次方數) * fraction(分數值) / mantissa(尾數):二進位尾數 --- ### 以IEEE754格式計算小數 要將一個小數以IEEE754格式換算成一組二進位數值, 可依照以下步驟進行換算: 1. 拆分整數與小數部分 2. 將整數與小數轉換成二進位 --- ### 特殊解題思路 假設如下: 如果我們能直接將一個浮點數(float)轉換成整數(int), 就不需要再以IEEE754標準的方式建構二進位 (其實電腦已經先幫你算好並存到記憶體裡了)。 有一個特殊的結構體為:union (聯集), 可以將其裡面的變數共用於同一個記憶體區間裡面, 此記憶體區間會根據結構體裡面的變數,分配予佔據空間最大的資料型態, 如一個union下有double (8-bytes)、int (4-bytes)、char-(1 byte)時, 其占用的記憶體空間為8-bytes。 顧名思義,當你將一個float值存入結構體union下時, 且union下有一個int變數,其int變數就會是IEEEE754格式的二進位數值。 經過簡單的正負號處理與位元運算後, 可以寫出一個將浮點數輸出成IEEE754二進位格式的程式: ```cpp= #include <iostream> using namespace std; union get_binary_32{ float decimal; int binary; }; int main(){ int i = 31; get_binary_32 data; char bit_set[33] = "00000000000000000000000000000000"; //32個'0',留一個'/0' cin>>data.decimal; if(data.decimal < 0) //負數處理 bit_set[0]++, data.decimal = -data.decimal; do{ //遍歷並以字串儲存二進位 bit_set[i--] += data.binary&1; }while(data.binary>>=1); cout<<bit_set<<"\n"; } ``` 題目要求是要將此二進位拆分成三段數字,分別佔1、8、23位, 並且小數點後的二進位最多只能取到10位, 如果純用union出來的二進位結果,小數點的二進位位數是會多出來的。 回頭稍微審視一下,其實有一個地方已經記錄了第一位小數二進位的位置, 對,沒錯,就是exponent!!! 我們可以把exponent的值拿出來做分析一下: exponent = 首位二進位偏移值 +127 以位元的方式來看,不難得出偏移值(exp)的算式為 exp = ((binary>>23) - 127) (註:前面會先經過去負號的運算,所以binary>>23的值不會被sign影響) 當exp=-1時,為小數部分位元的第一位,並且我們將位元倒過來處理, (從右往左處理會比較方便) 需要去掉的位元位數為23-(10+exp) 設其i為倒置的陣列index, 可得i = 23-(10+exp) = 23-(10+((binary>>23)-127)) 並連同將binary右移i位,就可以完成去小數多餘位數的處理了。 #### Solution Code ( CAL accepted ): ```cpp= #include <iostream> using namespace std; union Get_binary_32{ float decimal; int binary; }; int main(){ int i; Get_binary_32 data; char bit_set[35]="0 00000000 00000000000000000000000"; cin>>data.decimal; if(data.decimal<0) //負數處理 bit_set[0]++, data.decimal = -data.decimal; //去小數多餘位數:binary>>=(i = 23-(10+((data.binary>>23)-127))) data.binary >>= (i = 140 - (data.binary>>23)); while(data.binary && i++<=34) //遍歷並以字串儲存二進位 if(bit_set[34-i] != ' ') bit_set[34-i] += data.binary&1, data.binary>>=1; cout<<bit_set<<"\n"; } ``` ###### posted date: `2020.12.06`