编程之美:第二章 数字之魅 2.6精确表达浮点数

/*
精确表达浮点数:
在计算机中,使用float或double来存储小数不能得到精确值。希望得到精确值,最好用分数形式来表示小数。有限小数或者无限循环小数可以转化为分数
例如:
0.9 = 9/10
0.333(3) = 1/3(括号中的数字表示是循环节)
一个小数可以用好几种分数形式来表示。如:
0.333(3) = 1/3 = 3/9
给定一个有限小数或者无限循环小数,你能否用分母最小的分数形式来返回这个小数呢?如果输入为循环小数,循环节用括号标记出来。下面是一些可能的输入数据,
如0.3,0.30,0.3(000),0.3333(3333)、......

分析:
先从最简单的情况入手:所有小数可以分解成一个整数和一个纯小数之和,不妨只考虑大于0,小于1的纯小数,且暂时不考虑分子和分母的约分。
题目中输入的小数,要么为有限小数X = 0.a1a2...an,要么为无限循环小数X=0.a1a2...an(b1b2...bm),X表示式中的字母a1a2...an,b1b2...bm都是0~9的数字,括号
部分(b1b2...bm)表示循环节,
对于有限小数X = 0.a1a2...an来说,X = (a1a2..an)/10^n
对于无限循环小数X = 0.a1a2...an(b1b2...bm)来说,复杂部分在于小数点后同时又非循环和循环部分
X = 0.a1a2...an(b1b2...bm)
->10^n*X = a1a2...an.(b1b2...bm)
->10^n*X = a1a2...an + 0.(b1b2...bm)
->X = (a1a2...an + 0.(b1b2...bm)) / 10^n
对于整数部分a1a2...an,不需要二外处理,只需要把小数部分转化为分数形式再加上这个整数即可。对于无限循环部分,采用下面方式处理:
令Y = 0.(b1b2...bn),那么
10^m * Y = b1b2...bm.(b1b2...bm)
->10^m * Y = b1b2...bm + 0.(b1b2...bm)
->10^m * Y - Y = b1b2...bm
->Y = b1b2...bm/(10^m-1)
将Y带入得到
X = (a1a2...an+Y)/10^n
  = (a1a2...an + b1b2...bm/(10^m-1))/10^n
  =( (a1a2...an*(10^m-1) + b1b2...bm) )/(10^(m-1)  * 10^n );
注意:分母未必是最简的,应该对分子和分母进行约分,即双方同时除以两者的最大公约数即可
A/B = (A/Gcd(A,B)) / (B/Gcd(A,B))

输入:
0.3(33)
0.285714(285714)
输出:
1/3
2/7

整个思路应该是:将输入的数字当成字符串,以找到第一个"("和第一个一个")"作为切分
切分为0.3 (33),计算出.后面的数字位数n,和()中间的数字位数m
利用公式
求公约数
*/

/*
关键:
1 求公约数是有大小的顺序的,必须确保第一个参数大于第二个参数
2 对于无限循环小数X = 0.a1a2...an(b1b2...bm)来说,复杂部分在于小数点后同时又非循环和循环部分
X = 0.a1a2...an(b1b2...bm)
->10^n*X = a1a2...an.(b1b2...bm)
->10^n*X = a1a2...an + 0.(b1b2...bm)
->X = (a1a2...an + 0.(b1b2...bm)) / 10^n
对于整数部分a1a2...an,不需要二外处理,只需要把小数部分转化为分数形式再加上这个整数即可。对于无限循环部分,采用下面方式处理:
令Y = 0.(b1b2...bn),那么
10^m * Y = b1b2...bm.(b1b2...bm)
->10^m * Y = b1b2...bm + 0.(b1b2...bm)
->10^m * Y - Y = b1b2...bm
->Y = b1b2...bm/(10^m-1)
3 X = (a1a2...an+Y)/10^n
  = (a1a2...an + b1b2...bm/(10^m-1))/10^n
  =( (a1a2...an*(10^m-1) + b1b2...bm) )/(10^(m-1)  * 10^n );
注意:分母未必是最简的,应该对分子和分母进行约分,即双方同时除以两者的最大公约数即可
A/B = (A/Gcd(A,B)) / (B/Gcd(A,B))
4 lMolecule = lMolecule * lRadius + (long long)(*pCh - '0');//注意,这里始终是乘以10
5 

*/

#include <stdio.h>
#include <string.h>
#include <assert.h>
const int MAXSIZE = 10000;

long long gcd(long long a,long long b)
{
	return b == 0 ? a : gcd(b,a%b);
}

void swap(long long* pNum1,long long* pNum2)
{
	long long lTemp = *pNum1;
	*pNum1 = *pNum2;
	*pNum2 = lTemp;
}

long long power_recursion(int n)//计算10^n的值
{
	if(n == 1)
	{
		return 10;//10^1 = 10 
	}
	long long lRes = power_recursion(n/2);
	lRes *= lRes;
	if((n & 1) == 1)//如果是奇数
	{
		lRes *= 10;
	}
	return lRes;
}

void fraction(char* str)
{
	if(!str)
	{
		return;
	}
	char* pDot = strchr(str,'.');
	char* pLeftBracket = strchr(str,'(');
	char* pRightBracket = strchr(str,')');
	assert(pDot && pLeftBracket && pRightBracket);
	int iOutN = pLeftBracket - pDot - 1;//括号外的小数位数,例如0.3(33),求得是0.3中的3占一位
	int iInM =pRightBracket - pLeftBracket - 1;//求得是括号内的小数卫视,例如0.3(33),求得是(33)中的33占两位
	char* pCh;
	long long lMolecule = 0;
	long long lRadius = 10;
	for(pCh = pDot + 1 ; pCh != pLeftBracket ; pCh++)//下面计算0.a1a2...an(b1b2...bm)中的a1a2...an
	{
		lMolecule = lMolecule * lRadius + (long long)(*pCh - '0');//注意,这里始终是乘以10
	}
	long long lDenominator = 0; //下面计算b1b2...bm
	for(pCh = pLeftBracket + 1 ; pCh != pRightBracket ; pCh++)
	{
		lDenominator = lDenominator * lRadius + (long long)(*pCh - '0');
	}
	long long lM  = power_recursion(iInM);
	lM--;
	long long lN = power_recursion(iOutN);
	long long lUp = lMolecule * lM + lDenominator;
	long long lDown = lM*lN;
	long long lGcd = gcd(lDown,lUp);
	//printf("%d%s%d\n",lUp/lGcd,'//',lDown/lGcd);//注意转义字符
	printf("%d",lUp/lGcd);
	printf("/");
	printf("%d\n",lDown/lGcd);
}

void process()
{
	char str[MAXSIZE];
	while(EOF != scanf("%s",str))
	{
		fraction(str);
	}
}

int main(int argc,char* argv[])
{
	process();
	getchar();
	return 0;
}

    原文作者:天地一扁舟
    原文地址: https://blog.csdn.net/qingyuanluofeng/article/details/47187169
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞