题目
解读
其实这题是一题数学题,核心是电脑储存浮点数的方式。
从我们的科学记数法说起。对于任意一个实数(十进制),都可以用A*10^B 来表示。但是电脑只能用二进制来储存东西(0,1),所以在计算机就变成了m*2^e。m我们称之为尾数,e称之为阶码。同时我们也就得到等式:
A*10^B = m*2^e
但还没完,就这样还远远不够。
下面先求对于位数下 m 和 e 的最大值
对于小数,10进制可以用 a1*10^(-1) + a2*10^(-2) + ... + an*10^(-n)的方式表示。2进制的情况显然相似(对于用 i 位进行储存):
m = 2^(-1) + 2^(-2) + ... + 2^(-1 - i)
= 1 - 2^(-1 - i)
e 则好办多了:
e = 2^j - 1
题目给出了0 ≤ M ≤ 9,1 ≤ E ≤ 30(M,E分别为尾数和阶码的位数),所以最多共有300种表示方法,进而联想到打表。用两个二维数组,分别储存对应位数时的尾数和阶码的最大值,分别记为M[i][j]和E[i][j]。
然后接下来到计算对应 M 和 E 的值。还记得刚刚的等式吗?对,我们需要借助那个。对两边取十为底的对数lg:
lg(A) + B = lg(m) + e*lg(2) = t
由于A小于10,所以lg(A)就是 t 的小数部分,自然 B 就是 t 的整数部分。
所以,我们的 E[i][j] 只需在计算出 t 之后取整即可。M[i][j] 则为:
10^( t - E[i][j] )
即可。(取10的幂是为了方便与A比较,就无需计算lg(A)了 )
搞定了M和E,就到读入 A 和 B 了。
因为输入的更像是一个字符串,所以考虑先当成字符串储存再进行处理。观察结构可以把e换成空格,然后用函数sscanf()进行输入。
最后进行遍历匹配即可。不过因为浮点数自身精确度的问题,会存在误差,所以还需设一个误差范围。此题误差设为 1e-4 效果最好。
代码
#include <stdio.h>
#include <math.h>
#include <string.h>
//多层循环用函数更容易跳出
void solve(double m, int e);
double M[12][33]; //储存尾数表
long long E[12][33]; //储存阶数表
int main() {
#ifdef TEST
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif // TEST
//打表
int i, j;
char str[25];
for (i = 0; i <= 9; ++i) for (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]);
}
//读入处理
while (scanf("%s", str) == 1 && strcmp(str, "0e0")) {
double A; int B;
* (strchr(str, 'e')) = ' ';
sscanf(str, "%lf %d", &A, &B);
while (A < 1) A *= 10, B -= 1;
solve(A, B);
}
return 0;
}
void solve(double m, int e) {
for (int i = 0; i <= 9; i++)
for (int j = 1; j <= 30; j++)
if (e == E[i][j] && (fabs(m - M[i][j]) < 1e-4 || fabs(m / 10 - M[i][j]) < 1e-4)) {
printf("%d %d\n", i, j);
return;
}
}