28行代码AC——习题3-12 浮点数(UVA 11809 - Floating-Point Numbers)——解题报告
勵(lì)志用少的代碼做高效的表達(dá)
題目(提交)鏈接→UVA-11809
算是個(gè)數(shù)學(xué)題吧,雖然在AOAPC上面給放到象征水題的第三章里面了。
這個(gè)題基本就是幫著你復(fù)習(xí)了一遍浮點(diǎn)數(shù)的存儲(chǔ)方式了。浮點(diǎn)數(shù)在計(jì)算機(jī)里是分三部分表示的,最前面一位表示符號(hào),后面一部分是尾數(shù),最后一部分是階碼,表示方法類似于科學(xué)記數(shù)法,不過(guò)是二進(jìn)制的,尾數(shù)是M階碼是E的話那么表示起來(lái)就是M × 2^E了。然后對(duì)于M還有一個(gè)要求,就是1/2 ≤ M < 1,所以用二進(jìn)制表示M的話就應(yīng)該是0.1XX……,用計(jì)算機(jī)表示的時(shí)候就把最前面的“0.1”這個(gè)永遠(yuǎn)不變的部分給省略掉,只表示可能變化的部分。階碼部分則是只用二進(jìn)制表示E。
上面的圖就給出了一個(gè)例子,前面的0表示是正數(shù)。后面8位表示尾數(shù)m,這里是0.111111111(注意后面是9個(gè)1,因?yàn)轭^一個(gè)省略了)。之后那個(gè)0表示分割,最后面6位表示e的二進(jìn)制為111111。所以這個(gè)數(shù)就是,用十進(jìn)制表示就是。
在計(jì)算機(jī)中用二進(jìn)制表示M和E的時(shí)候如果位數(shù)不同,那么它們所能表示的最大值也不同。現(xiàn)在給你所能夠表示的最大的浮點(diǎn)數(shù)的值,讓你倒回去求M和E分別有多少位。輸入格式為AeB,表示最大浮點(diǎn)數(shù)為,并且0 < A < 10,并且保證輸出的結(jié)果中0 ≤ M ≤ 9且1 ≤ E ≤ 30。輸入以0e0表示結(jié)束,0e0本身不計(jì)算。
這個(gè)如果直接去算的話相當(dāng)麻煩,當(dāng)E很大的時(shí)候數(shù)會(huì)直接超出上限。這個(gè)時(shí)候可以反過(guò)來(lái)想,最大的時(shí)候M和E的每一位肯定都是1,并且又有0 ≤ M ≤ 9且1 ≤ E ≤ 30的限定,所以一共只有300種情況,自然就想到了打表,先用二重循環(huán)枚舉M和E可能出現(xiàn)位數(shù)的所有情況打一張表,然后輸入的時(shí)候倒回去找即可。
假設(shè)當(dāng)前一層M和E的值為m和e,它們的位數(shù)分別為i和j。
首先計(jì)算m的值,用二進(jìn)制表示的話,m的值為0.11…,也就是m = 2^(-1) + 2^(-2) + … + 2^(-1 - i)(i比實(shí)際1的個(gè)數(shù)少1個(gè)),也就是m = 1 - 2^(-1 - i)。
接下來(lái)就是計(jì)算e的值,不難得出,e = 2^j - 1。
那么也就有m * 2^e = A * 10^B,似乎可以直接計(jì)算了。然而,直接這樣算的話是不行的,因?yàn)楫?dāng)e太大的話(e最大可以是1073741823,注意這還只是2的指數(shù)),等號(hào)左邊的數(shù)就會(huì)超出上限,所以要想繼續(xù)算下去,就得自己去想辦法再寫出滿足要求的類來(lái),這顯然太麻煩了。所以,這個(gè)時(shí)候我們對(duì)等式兩邊同時(shí)取對(duì)數(shù),這個(gè)時(shí)候就有 log10(m) + e × log10(2) = log10(A) + B。因?yàn)榇藭r(shí)m和e的值都是確定的,所以不妨令等式左邊為t,也就有t = log10(A) + B。
這個(gè)時(shí)候就有問(wèn)題了,A和B怎么算。
寫題解的時(shí)候突然意識(shí)到了這個(gè)問(wèn)題,讀題的時(shí)候很多人,包括我,都把AeB默認(rèn)為了科學(xué)記數(shù)法,在ACM協(xié)會(huì)群里面討論的時(shí)候很多人也都說(shuō)這是科學(xué)計(jì)數(shù)法。先來(lái)看如果是科學(xué)記數(shù)法的時(shí)候應(yīng)該怎么辦。
如果是科學(xué)記數(shù)法的話,那么對(duì)于A,就有1 ≤ A < 10。那么0 < log10(A) < 1。所以t的小數(shù)部分就是log10(A),整數(shù)部分就是B,即B = ?t?,A = 10^(t - B)。那么接下來(lái),我們只需要開出兩個(gè)二維數(shù)組來(lái),分別記錄對(duì)應(yīng)i和j下A和B的大小,之后從輸入里提取出A和B的大小,去二維數(shù)組里面查找對(duì)應(yīng)的i和j即可。
這種辦法在UVA上面是可以直接AC的,但是我卻感覺這題這樣A了有點(diǎn)數(shù)據(jù)太水的感覺,秉著處女座+強(qiáng)迫癥死磕到底的精神,我們看下哪里有問(wèn)題。
其實(shí)回頭讀下題,我們發(fā)現(xiàn)科學(xué)記數(shù)法1 ≤ A < 10的條件是我們腦補(bǔ)出來(lái)的,題目里面根本沒有提及,只是簡(jiǎn)單交待0 < A < 10。也就是說(shuō),對(duì)于確定的M和E的位數(shù),十進(jìn)制的表示可以有多種,例如樣例中的5.699141892149156e76,下面的數(shù)據(jù)應(yīng)當(dāng)也是完全可能的,而且結(jié)果應(yīng)當(dāng)與樣例的結(jié)果是相同的(當(dāng)然是在保證精度可以計(jì)算出結(jié)果的前提下):
0.569914189214915e77 0.056991418921491e78 0.005699141892149e79 0.000569914189214e80帶著這個(gè)想法我分別拿著上面的數(shù)據(jù)去UVA toolkit和uDebug上試了試,UVA toolkit依舊能夠輸出“5 8”的結(jié)果來(lái),但是uDebug告訴我我的輸入不合法……果真是我想多了么……
不過(guò)這個(gè)問(wèn)題也好辦,還是看上面的數(shù)據(jù),忽略掉后面幾位精度丟失的問(wèn)題的話,上面的幾個(gè)數(shù)完全可以通過(guò)“A *= 10, B -= 1”或者“A /= 10, B += 1”的操作來(lái)相互轉(zhuǎn)化。那么對(duì)于0 < A < 1的A的值,我們就可以通過(guò)“A *= 10, B -= 1”的操作來(lái)使其滿足科學(xué)記數(shù)法的條件。
另外,在查表的時(shí)候還應(yīng)該注意精度的問(wèn)題,15位有效數(shù)字對(duì)于double來(lái)說(shuō)精度似乎也不夠,而且計(jì)算出所需要的整數(shù)值其實(shí)需要的精度也沒有那么高,所以這里的精度就只用到了1e-4的程度。
代碼:
#include <bits/stdc++.h> using namespace std; int main() {double M[20][40];long long E[20][40];// 打表for(int i = 0; i <= 9; ++i) for(int j = 1; j <= 30; ++j) {double m = 1 - pow(2, -1 - i), e = pow(2, j) - 1;double t = log10(m) + e * log10(2);E[i][j] = t, M[i][j] = pow(10, t - E[i][j]);}// 輸入并輸出結(jié)果string in;while(cin >> in && in != "0e0") {// 處理輸入for(string::iterator i = in.begin(); i != in.end(); ++i) if(*i == 'e') *i = ' ';istringstream ss(in);double A; int B;ss >> A >> B;// 在打好的表中尋找答案for(int i = 0; i <= 9; ++i) for(int j = 1; j <= 30; ++j) if(B == E[i][j] && (fabs(A - M[i][j]) < 1e-4) {cout << i << ' ' << j << endl;break;}} }撥云見日,未來(lái)可期
總結(jié)
以上是生活随笔為你收集整理的28行代码AC——习题3-12 浮点数(UVA 11809 - Floating-Point Numbers)——解题报告的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 21行代码AC——习题3-7 DNA序列
- 下一篇: 计算机网络 数据段、报文、IP数据报、数