UVa11809-Floating-Point Numbers
思路:
看到题目我是懵逼的,先是艰难的理解了double类型的储存方式之后,刚开始准备用直接枚举的方法来算因为是分为阶码和位数来存要求找全为1的这样M最多有十个,E最多有30个这个数就是
∑Mi=12−i⋅2E
因为E最大为
230
所以没法儿存,后来看了别人的方法发现只要两边取对数后就可以算了,就是枚举这个公式然后取对数计算
∑Mi=12−i⋅2E=A⋅10B
下面是一篇我感觉解释的比较清楚的博客
如果每组数都要计算比较找到对应的m和e运算量太大,所以先打表,涉及浮点数表示的一些数学知识。
假设当前一层M和E的值为m和e,它们的位数分别为i和j。
首先计算m的值,用二进制表示的话,m的值为0.11…,也就是m = 2^(-1) + 2^(-2) + … + 2^(-1 -
i)(i比实际1的个数少1个),也就是m = 1 - 2^(-1 - i)。接下来就是计算e的值,不难得出,e = 2^j - 1。
那么也就有m * 2^e = A *
10^B,似乎可以直接计算了。然而,直接这样算的话是不行的,因为当e太大的话(e最大可以是1073741823,注意这还只是2的指数),等号左边的数就会超出上限,所以要想继续算下去,就得自己去想办法再写出满足要求的类来,这显然太麻烦了。所以,这个时候我们对等式两边同时取对数,这个时候就有
log10(m) + e × log10(2) = log10(A) + B。因为此时m和e的值都是确定的,所以不妨令等式左边为t,也就有t
= log10(A) + B。这个时候就有问题了,A和B怎么算。
科学记数法对于A,有1 ≤ A < 10。那么0 < log10(A) <
1。所以t的小数部分就是log10(A),整数部分就是B,即B = ⌊t⌋,A = 10^(t -
B)。那么接下来,我们只需要开出两个二维数组来,分别记录对应i和j下A和B的大小,之后从输入里提取出A和B的大小,去二维数组里面查找对应的i和j即可。
不过奇怪的是M和E用一维数组存就可以了,但是网上大部分都是二维打表的套路,原因嘛大家懂的。
最后那个
1e−5
的误差是我试出来的,
1e−4
~
1e−6
之间是可以AC,用其他值就WA
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
double M[11],A;
int E[33],B;
char s[100];
for(int i=0;i<10;i++)
M[i]=1-pow(0.5,i+1);
for(int i=0;i<31;i++)
E[i]=pow(2,i)-1;
while(scanf("%s",s),strcmp(s,"0e0")==1)
{
for(int i=0;i<strlen(s);i++)
if(s[i]=='e'){s[i]=' ';break;}
sscanf(s,"%lf %d",&A,&B);
for(int i=0;i<10;i++)
{
for(int j=0;j<31;j++)
{
if(fabs((log10(M[i])+E[j]*log10(2))-B-log10(A))<1e-5)
{
printf("%d %d\n",i,j);
goto gbreak;
}
}
}
gbreak:;
}
return 0;
}