版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}
测试输出:
注意 最后一个小数已经产生溢出。