原题链接:https://vjudge.net/problem/UVA-11809
分类:字符串、数组
备注:思维、数学
前言:明明前面的题都挺友善的,怎么到了你这就拉了跨呢。该说真不愧是本章的Big_Boss,对于我这样的新人来说很难了,只能抄抄别人的代码才能生存这样子。
题意
给出若干组十进制浮点数,以AeB形式输入,表示 A × 1 0 B A\times10^B A×10B, 0 < A < 10 0<A <10 0<A<10,保证A由1位整数和15位小数构成。以0e0表示输入结束。要求计算将该十进制浮点数转化为二进制浮点数后所需的尾数和阶码分别要多少位。若以M表示尾数的位数,E表示阶码的位数,则保证 9 ≥ M ≥ 0 9\geq M \geq0 9≥M≥0, 30 ≥ E ≥ 1 30\geq E \geq1 30≥E≥1。
Sample Input
5.699141892149156e76
9.205357638345294e18
0e0
Sample Output
5 8
8 6
上图绿色部分为尾数,紫色部分为阶码,前面的0表示正号,如果是1则表示负号,注意在尾数部分,符号位后面省略了1个1。该图表示的浮点数为 0.1111111112
×
\times
× 2111111 。因为尾数部分省略了1个1,实际的尾数一共9个1,因此题目还保证:对于尾数m,有
1
2
≤
m
≤
1
\frac{1}{2}\leq m \leq 1
21≤m≤1。
下面以m和e分别表示实际的尾数和阶码,M和E分别表示尾数位数和阶码位数。
思路
- M一共10种可能,E一共30种可能,则输出的答案一共有300种情况,可以尝试打表来解题。
- 计算每组M,E能表示的最大二进制数,即每一位上的数都为1,则对于每一个十进制数都会处于两个大小相邻的二进制数之间。
尾数 = 0.1111 ⋯ \cdots ⋯ = 2-1+2-2+ ⋯ \cdots ⋯+2-1-M = 1-2-1-M
阶码 = 21111 ⋯ \cdots ⋯ = 2E-1
打表得到的每个二进制数为(1-2-1-M) × \times × 22E-1 即 m × 2 e m\times2^e m×2e。
显然,对于同一个E,随着M递增, m × 2 e m\times2^e m×2e递增。易证,对于阶码位数E和E+1,后者与最小M得出的数会大于前者与最大M得出的数,即 22E+1-1 × \times × 0.5 > > > 22E-1 × \times × (1-2-10)。
- 至此,我们发现要直接计算的话,数可能太大了,于是可以考虑对数的应用。
- 将AeB变成log10(A)+B, m × 2 e m\times2^e m×2e变成log10(m)+e·log10(2)。
- 按顺序先让M从0到9遍历完,再E++,发现打表的二进制数出现大于AeB时,则找到了所求的M和E。
- 特别注意打表要加上1e-5或者1e-4以保证精度准确,但是目前我也不知道为什么这样就行了…
- 另外这题我觉得麻烦的还有输入,我看别人的要么是string,要么是%17lfe%d,这章如果是小白在学的话怎么可能搞这样的东西。用字符输入,注意回车和小数点。
代码如下:
#include<stdio.h>
#include<math.h>
const double EPS = 1e-4;
double A, s[15][35]; int B;
int read()
{
A = 0, B = 0;
double k = 1; char ch = getchar();
while (ch != 'e')
{
if (ch != '.' && ch != '\n')
{
A += (ch - '0') * k;
k *= 0.1;
}
ch = getchar();
}
scanf("%d", &B);
if (fabs(A - 0) < EPS && B == 0)return 0;
return 1;
}
int main(void)
{
for (int E = 1; E <= 30; E++)//先打表
for (int M = 0; M <= 9; M++)
{
double m = log10(1 - 1.0 / (1 << (M + 1)));//只有整数可以进行位运算
double e = ((1 << E) - 1) * log10(2);
s[M][E] = m + e + EPS;//对浮点数的精度要修正
}
while (read())
{
while (A < 1) { A *= 10; B--; }
double tmp = log10(A) + B;
int flag = 0;
for (int E = 1; E <= 30; E++)
{
for (int M = 0; M <= 9; M++)
if (s[M][E] > tmp)
{
printf("%d %d\n", M, E);
flag = 1;
break;
}
if (flag)break;
}
}
return 0;
}