C语言将循环小数/有限小数转换为分数

数学基础

早在小学的时候我就对循环小数非常感兴趣,加上初中和高中对循环小数可以说有一定基础研究,因此想到写一个将循环下小数转换为分数的程序,非常有意思,并且对初学者来说,它的输入输出格式的转换也是一大难点。
首先必须明确一点,循环小数必定可以转换为分数,原因在于循环小数总可以分解为不循环的有限部分+循环的无限部分。前者对应一个分数,后者可以写成一个收敛的等比数列的和,也必定是可以转换为一个分数的。例如0.234343434…,有限部分为0.2=1/5,无限部分为0.0343434…=0.034/(1-0.01)=34/990=17/495;0.2343434…=1/5+17/495=116/495。同理,0.879879879…=0.879/(1-0.001)=879/999。
所以任何一个循环小数都可以化为分数,并且通过上述两个例子,我们也对如何转换有了一个初步的了解。

编程思路

  1. 首先我们得设置一下输入的格式,有限部分和循环部分显然是要分开的,为了方便起见,我在程序里设置的是空格分开。 需要的头文件分别是**<math.h>,<stdio.h>,<stdlib.h>,<string.h>**。
  2. 第二,由于是纯数学运算,来不得半点近似,所有的分数运算我们都需要保留分子和分母,所以保存一个小数的变量是两个整形——分子(num)和分母(den),不妨定义一个分数(Decimal)结构体类型,里面包含整形数据的分子和分母。
  3. 虽然说处理的是整形变量,但是由于我们输入是浮点数,扫描会带来麻烦,所以扫描的时候我还是采用%s的格式一股气扫进去的,根据小数点来个划分。这里还用到了<stdlib.h>当中的itoa和atoi函数,这两个函数可以求出正数的长度。当然,你也可以用<math.h>当中的log10()来求取长度,这两种方法在我的程序中皆有涉及。
  4. 对于分数的处理,显然是要用到化简函数了,化简分数无非就是分子分母同时除以它们的最大公约数,因此需要写一个辗转相除法求最大公约数的程序。分数相加的原理也很简单,先对每个分数化简,再按照手算的方法相加,最后调用化简分数的函数对其化简即可。
  5. 计算有限小数部分的方法和循环小数部分的不同,这一点在第一部分里面已经提到,分别获取有限部分的位长以及循环部分的位长很关键。例如转换一个小数0.2343434…,假设输入0.2 34代表了这个循环小数,用scanf("%s%s",str1,str2)函数输入,那么strlen(str2)就是循环部分的位长。有限部分的位长,比循环部分的计算稍微复杂一些,你可以利用strstr函数舍掉小数点前面的部分,当然,小数点前面的部分不代表就没有用,毕竟我们输入的小数有可能大于1,因此按照如下方法处理即可:
float f1;
int integer,dec_length,cir_length;
f1=atof(str1);
integer=(int)f1;//整数部分
dec_length=strlen(strstr(str1,".")+1);//有限小数部分的长度,strstr(str1,".")+1是小数点后的一个数的指针,再取strlen即小数部分的长度
cir_length=strlen(str2);//循环小数部分长度

这几步非常关键,牵涉到我们的分母到底应该怎么写,如果分母有差错,那之后的化简都是白搭。

  1. 这里需要说明的是,由于我们是按照小数点分隔的,没有考虑到负号的情况,所有本程序暂时只支持输入正的小数,当然即使是负小数,也能很容易通过本程序找到答案。

代码

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h> 
struct Decimal{
	int num;
	int den;
};//定义分数结构体 
void simplify(int *num,int *den);//化简一个分数的分子和分母
int GCD(int a,int b);//求两个数的最大公约数 
int main()
{
	char str1[100],str2[100],int1[10];//存放两个分数的字符串,后续再处理  
	float f1; 
	int integer,dec1,num,den;//integer是整个小数的整数部分,dec1是有限部分的小数部分(不含小数点的),num和den分别是最终结果分数的分子和分母
	struct Decimal Dec,Cir;//小数部分对应的分数结构体(有限部分+循环部分) 
	printf("*******小数转换为分数的实验*********\n");
	printf("请输入一串小数,如有循环节,请和有限部分用空格隔开,如没有循环节,请用0代替。\n");
	printf("例如0.2 34代表0.234343434......,3.8753 0代表3.8753\n");//对输入的一个简单说明
	scanf("%s%s",str1,str2);//这里不能用%f扫入,否则将不知道小数长度 
	f1=atof(str1);//将输入的字符转换为浮点数 
	integer=(int)f1;//取整数部分
	dec1=atoi(strstr(str1,".")+1);
	itoa(integer,int1,10);//integer转为字符串,方便计算长度 
	Dec.num=dec1;//有限小数的小数部分(注意是整形的,指的是不含小数点的小数部分)
	Dec.den=pow(10,strlen(str1)-strlen(int1)-1);
	Cir.num=atoi(str2);
	Cir.den=pow(10,strlen(str2))-1;
	Cir.den*=pow(10,(int)(log10(dec1)+1));
	if(Cir.num==0)
	{
		simplify(&Dec.num,&Dec.den);
		num=Dec.num+integer*Dec.den;
		den=Dec.den;
		simplify(&num,&den);
		if(integer!=0)printf("转换为带分数的结果为:%d+%d/%d\n",integer,Dec.num,Dec.den);
		printf("转换为分数的结果为:%d/%d\n",num,den); 
		return 0;
	} //如果循环小数是0,直接转换有限部分即可,否则需要将循环部分加以转换,并与有限部分的分数相加 
	simplify(&Dec.num,&Dec.den);
	simplify(&Cir.num,&Cir.den);
	den=Dec.den*Cir.den;
	num=Dec.num*Cir.den+Dec.den*Cir.num;
	if(integer!=0)printf("转换为带分数结果为:%d+%d/%d\n",integer,num,den);
	num+=den*integer;
	printf("转换为分数结果为:%d/%d\n",num,den); 
	return 0;
} 
int GCD(int a,int b)
{
	//利用辗转相除法求两个数的最大公约数 
	if(a==1||b==1)return 1;
	int t;
	if(a<b){t=a;a=b;b=t;}//保证a是大数
	while(b>0)
	{
		t=b;//t临时存放小数
		b=a%b;//小数是上一次两个数相除的余数
		a=t;//大数是上一次相除的小数
	}
	return a;
}
void simplify(int *num,int *den)
{
	int gcd=GCD(*num,*den);
	*num=*num/gcd;
	*den=*den/gcd;
}

那么好,我们来输入一个循环小数进行验证,如果位数比较多的话,出来的应该是个变态的答案,当然你要相信自己是对的:
程序执行画面

下面我们用Win10自带的计算器验证一下(科学模式)
科学计算器输出结果
说明我们的结果没问题,大功告成。

发布了27 篇原创文章 · 获赞 195 · 访问量 9万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览