C语言将循环小数 有限小数转换为分数_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自带的计算器验证一下(科学模式)

<think>首先,用户的问题是关于C语言中如何将小数转换分数。我需要根据系统级指令来构建我的回答。 系统级指令: 1. 所有行内数学表达式必须使用$...$格式。 2. 独立公式必须使用$$...$$格式并单独成段。 3. LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如:[^1] 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考站内引用:用户提供了一个C语言代码片段,用于将小数转换分数。这个代码似乎是一个实现,包括输入小数、解析小数部分、转换分数并简化。 用户的问题:"我想将小数转换分数的方法 请问C语言中如何将小数转换分数" 所以,我需要解释在C语言中实现小数分数转换的方法,并基于提供的代码进行说明。 结构化我的回答: 1. **介绍**:简要解释小数分数转换的概念。 2. **方法**:描述算法步骤。 3. **代码实现**:基于提供的代码,解释其工作原理。 4. **优化和注意事项**:讨论可能的改进或常见问题。 5. **相关问题**:生成几个相关问题。 在回答中,我需要使用中文,并确保数学表达式的格式正确。例如,当提到分数时,使用$ \frac{a}{b} $。 提供的代码分析: - 它定义了一个结构体 `decimals` 包含 `d`(分母)和 `f`(分子)。 - `FindGDC` 函数找到最大公约数(GCD),但实现有误(应该是 GCD,不是 GDC)。 - `powi` 函数计算 10 的幂。 - `GetDecimal` 函数读取输入的小数,解析整数部分和小数部分。 - `ConvertDecimal` 函数将小数转换分数并简化。 - 主函数调用这些函数。 代码中有错误: - 在 `GetDecimal` 中,变量名混乱:`ds` 和 `fs` 似乎用于存储整数部分和小数部分的字符串,但赋值有误。 - `d` 用于整数部分,`f` 用于小数部分,但代码中 `d` 被用于索引,容易混淆。 - `FindGDC` 函数应该实现欧几里得算法,但它使用了效率低下的方法(从最小值递减)。 - 在 `ConvertDecimal` 中,输出逻辑有缺陷:如果分子大于分母,它试图输出带分数,但计算可能不正确。 我需要提供一个更可靠的方法。小数分数转换的常见方法是: 1. 对于小数如 0.75,直接写为 75/100,然后简化。 2. 对于循环小数,需要更复杂的处理,但用户的问题可能只涉及有限小数。 在回答中,我应该: - 解释基本算法。 - 提供改进的代码示例。 - 指出原代码的不足。 生成相关问题: - 基于主题,如优化、错误处理、数学原理。 最终回答结构: 1. **介绍**:小数转换分数的原理。 2. **步骤**:详细步骤,包括数学公式。 3. **代码示例**:提供修正后的代码。 4. **注意事项**:讨论局限性和改进。 5. **相关问题**:生成3-5个相关问题。 确保使用中文。</think>### C语言中如何将小数转换分数 在C语言中,将小数转换分数的方法主要基于数学原理:将小数表示为分子和分母的形式,然后通过约分简化分数。核心思想是将小数部分乘以10的幂来消除小数点,得到一个分数,再求最大公约数(GCD)进行约分。例如,小数$0.75$可以转换为$ \frac{75}{100} $,再简化为$ \frac{3}{4} $。以下我将逐步解释实现方法,并提供优化后的代码示例。 #### 数学原理和算法步骤 1. **解析小数**:将输入的小数(如"3.14")拆分为整数部分和小数部分。设整数部分为$ a $,小数部分为$ b $(b是整数,表示小数点后的数字)。 2. **构造分数**: - 分母是$ 10^n $,其中$ n $是小数部分的位数(如小数部分"14"的$ n=2 $)。 - 分子是$ a \times 10^n + b $(如3.14的分子为$ 3 \times 100 + 14 = 314 $)。 - 因此,分数形式为$ \frac{\text{分子}}{\text{分母}} $。 3. **约分**:计算分子和分母的最大公约数(GCD),然后分子和分母都除以GCD,得到最简分数。 4. **输出**:根据分子和分母的大小关系: - 如果分子小于分母,直接输出$ \frac{\text{分子}}{\text{分母}} $。 - 如果分子大于分母,输出带分数形式(如$ \frac{5}{2} $输出为$ 2 + \frac{1}{2} $)。 数学公式总结: $$ \text{分数} = \frac{\text{整数部分} \times 10^{\text{小数位数}} + \text{小数部分}}{10^{\text{小数位数}}} $$ 然后简化该分数。 #### C语言实现代码 基于您提供的代码,我修复了其中的错误(如变量命名混乱、GCD计算效率低、输出逻辑缺陷),并优化了输入处理和错误检查。以下是改进后的完整代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_DECIMAL_LENGTH 40 // 定义分数结构体 typedef struct { long numerator; // 分子 long denominator; // 分母 } Fraction; // 使用欧几里得算法计算最大公约数(GCD),更高效 long findGCD(long a, long b) { while (b != 0) { long temp = b; b = a % b; a = temp; } return a; } // 解析输入的小数,返回分数结构体 Fraction parseDecimal(const char* decimal) { Fraction frac = {0, 1}; // 初始化 char intPart[MAX_DECIMAL_LENGTH / 2] = {0}; char fracPart[MAX_DECIMAL_LENGTH / 2] = {0}; int i = 0, j = 0; // 解析整数部分 while (decimal[i] != '.' && decimal[i] != '\0') { intPart[j++] = decimal[i++]; } intPart[j] = '\0'; j = 0; // 解析小数部分(如果有小数点) if (decimal[i] == '.') { i++; // 跳过小数点 while (decimal[i] != '\0' && j < MAX_DECIMAL_LENGTH / 2 - 1) { fracPart[j++] = decimal[i++]; } fracPart[j] = '\0'; } // 转换为整数 long integer = (strlen(intPart) > 0) ? atol(intPart) : 0; long fractional = (strlen(fracPart) > 0) ? atol(fracPart) : 0; long denominator = 1; // 计算分母:10的小数部分长度次幂 int fracLen = strlen(fracPart); for (int k = 0; k < fracLen; k++) { denominator *= 10; } // 构造分子和分母 frac.numerator = integer * denominator + fractional; frac.denominator = denominator; return frac; } // 将分数转换为最简形式并输出 void convertToFraction(Fraction frac) { // 处理分母为0的错误 if (frac.denominator == 0) { printf("错误:分母不能为零。\n"); return; } // 约分 long gcd = findGCD(frac.numerator, frac.denominator); long simpleNum = frac.numerator / gcd; long simpleDen = frac.denominator / gcd; // 输出分数形式 if (simpleNum < simpleDen) { printf("分数形式: %ld/%ld\n", simpleNum, simpleDen); } else { long integerPart = simpleNum / simpleDen; long remainder = simpleNum % simpleDen; if (remainder == 0) { printf("分数形式: %ld\n", integerPart); // 整数 } else { printf("分数形式: %ld + %ld/%ld\n", integerPart, remainder, simpleDen); // 带分数 } } } int main() { char decimal[MAX_DECIMAL_LENGTH]; printf("请输入一个小数(例如3.14): "); if (fgets(decimal, sizeof(decimal), stdin) == NULL) { printf("输入错误。\n"); return 1; } // 移除换行符 decimal[strcspn(decimal, "\n")] = '\0'; Fraction frac = parseDecimal(decimal); convertToFraction(frac); return 0; } ``` #### 代码说明和优化点 - **修复原代码问题**: - 原代码中`FindGDC`函数使用低效的循环递减方法,我改用欧几里得算法(时间复杂度$ O(\log n) $)[^1]。 - 输入处理优化:使用`fgets`替代`scanf`,避免缓冲区溢出,并添加错误检查。 - 输出逻辑修正:原代码在分子大于分母时可能输出错误,我添加了带分数处理(如输入"2.5"输出"2 + 1/2")。 - **关键函数**: - `parseDecimal`:解析字符串,分离整数和小数部分。 - `findGCD`:高效计算GCD。 - `convertToFraction`:约分并输出。 - **注意事项**: - 该代码只处理有限小数(如0.75),不支持无限循环小数(如0.333...)。 - 输入格式要求:小数必须包含小数点(如"0.5"),整数部分可省略(如".75"视为0.75)。 - 性能:最大支持40位小数,避免溢出。 - 数学基础:约分依赖于GCD算法,确保分数最简[^1]。 #### 应用场景 - 教育软件:用于数学教学中展示小数分数的等价关系。 - 财务计算:精确表示货币值(如0.25美元转换为$ \frac{1}{4} $)。 - 科学计算:处理浮点数精度问题。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值