小数转分数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/WUDAIJUN/article/details/8825733
// 小数转分数.cpp : 定义控制台应用程序的入口点。
//
/*
*	DJ.W 2013.4.19
*	问题描述:给定一个有限小数或无限循环小数,用分母最小的分数形式将其表示出来
*	输入	:任意小数 循环节用()括起来 如: 1.33(3)
*	输出	:4/3
*	算法思想:《编程之美》P148
*				提示; 设分数Y = 0.(c1c2c3...cm)
*						=> Y = c1c2c3...cm / (10^m - 1)
*	注:本算法还有一些问题:
		1.未考虑负数情况 这个可以预先判断 再调用本算法 最后结果加上负号即可
		2.未考虑溢出 当整个输入小数的数字位数大于9时(也就是整个输入除去"()"和"."剩下的位数),就可能会产生溢出。
					 当位数大于等于11时,必定会产生溢出。这在后面算法会看到。这可以通过使用64位整数或者自建一个
						大数数据结构来改进
*/

#include "stdafx.h"
#include<iostream>
using namespace std;
#include<string>

//设一个小数为a1a2a3...ak.b1b2b3...bn(c1c2c3...cm)
//a1a2a3...ak代表整数部分 b1b2b3...bn代表有限小数部分 c1c2c3...cm代表循环节。
typedef struct _decimal
{
	int limipart;	//将a1a2a3...ak.b1b2b3...bn * 10^n 得到的整数a1a2a3...akb1b1b3...bn
	int limipart_n; //有限小数部分的位数n
	int looppart;   //化整后的无限循环节部分c1c2c3...cm
	int looppart_len;//循环节的位数m
}decimal, *pdecimal;

//求a^n
int fact(const int a, int n)
{
	if(n == 0)
		return 1;
	return a*fact(a, n-1);
}

//求x y 的最大公约数
int gcd(int x, int y)
{
	return (!y)? x:gcd(y, x%y);
}

//分析小数字符串的有效性 如果有效,将信息提取到传入的decimal结构体中
bool AnalysisDecimals(const string& str, pdecimal pdec)
{
	int loopstart=-1, loopend=-1;	//循环节起始和终止位置
	int dotpos = -1;				//小数点位置
	for (size_t i=0; i<str.size(); i++)
	{
		//如果出现"1234567890()."之外的字符 则返回false
		if (!isdigit(str.at(i)) && str.at(i)!='(' && str.at(i)!=')' && str.at(i)!='.')
			return false;

		//如果是小数点
		if(str.at(i) == '.')
		{
			if(dotpos == -1) //第一次出现小数点 记录
				dotpos = i;
			else	//出现一个以上小数点 false
				return false;
		}

		else if (str.at(i) == '(')
		{
			//如果'('之前没有小数点 false
			if(dotpos == -1)
				return false;

			//第一次出现'('则记录
			if(loopstart == -1)
				loopstart = i;
			else//出现一个以上'(' false
				return false;
		}

		else if (str.at(i) == ')')
		{
			//如果')'不是最后一个字符或者紧接着'(' false
			if(i != str.size()-1 || i==loopstart+1)
				return false;

			//之前已经出现'(' 则记录 否则false
			if(loopstart != -1 && loopend == -1)
				loopend = i;
			else
				return false;
		}
	}
	//如果出现了'('而未出现')' false
	if(loopstart != -1 && loopend == -1)
		return false;
	else if(loopstart == -1) //没有循环节
	{
		pdec->looppart = 0;
		pdec->looppart_len = 0;

		if (dotpos == -1)	//没有小数点 即为整数
		{
			pdec->limipart_n = 0;
			pdec->limipart = atoi(str.c_str());
		}
		else				//有小数点
		{
			string strlimit = str;
			pdec->limipart_n = strlimit.size() - strlimit.find('.')-1;
			strlimit.erase(dotpos, 1);
			pdec->limipart = atoi(strlimit.c_str());
		}
	}
	else //有循环节和小数点
	{
		string strlimipart = str.substr(0, loopstart);
		pdec->limipart_n = strlimipart.size() - strlimipart.find('.')-1;
		strlimipart.erase(dotpos, 1);
		pdec->limipart = atoi(strlimipart.c_str());
		
		string strlooppart = str.substr(loopstart+1, loopend-loopstart-1);
		pdec->looppart = atoi(strlooppart.c_str());
		pdec->looppart_len = strlooppart.size();
	}

	return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
	string str;
	decimal dec;
	
	while(1)
	{
		cout<<"输入小数,循环节用'()'括起来:"<<endl;
		cin>>str;
		while(!AnalysisDecimals(str, &dec))
		{
			cout<<"你输入的小数格式有误!请确认后重新输入!"<<endl;
			cout<<"输入小数:";
			cin>>str;
		}

		int demon, numer, commdiv; //分子分母和最大公约数
		if(dec.looppart == 0)//如果没有循环节 则直接用a1a2a3...akb1b2b3...bn / 10^n
		{
			demon = fact(10, dec.limipart_n);	//分母为 10^n
			numer = dec.limipart;				//分子为 a1a2a3...akb1b2b3...bn
		}
		else	//有循环节 那么目标分数为: ((a1a2a3...akb1b2b3...bn)*(10^m) + b1b2b3...bn) / ((10^m-1) * 10^n)	
		{		//注意: 在这个通用公式中,((a1a2a3...akb1b2b3...bn)*(10^m)将有可能导致溢出
			int x = fact(10, dec.looppart_len)-1;
			demon = x * fact(10, dec.limipart_n);
			numer = dec.limipart * x + dec.looppart;
		}
		//求得分子分母最大公约数
		commdiv = demon>numer ? gcd(demon,numer):gcd(numer,demon);

		cout<<"最精确的分数为:";
		cout<<numer/commdiv<<"/"<<demon/commdiv<<endl;
	}
	getchar();
	getchar();
	return 0;
}

测试输出:

注意 最后一个小数已经产生溢出。

猜你喜欢

转载自blog.csdn.net/WUDAIJUN/article/details/8825733