习题3-12 浮点数(Floating-Point Numbers,UVa11809)

原题链接: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 9M0 30 ≥ E ≥ 1 30\geq E \geq1 30E1

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 21m1

  下面以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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值