最近在翻看一本数学分析教材中关于实数的基本理论的章节,其中对有理数的小数表示方法给出了严密的定义和详细的讨论。看后很有收获,然后突然就想到了这么个问题,如何编个程序将有理数的分数表示转换为小数表示,并且对于无限循环小数还要给出循环节来。
花了半天时间将程序写好调通。把程序放在这里做个记录。
首先我先写了个只能处理正的真分数的程序。这是最简单的情况。不用判断正负号,也不用考虑整数部分。虽然说是任意进制,但是程序只支持到36进制,如果需要更多进制,可以修改程序。
void properfrac2str(int numerator, int denominator, int radix, char str[], int strl)
{
const char table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int *num_array;
int find_loop = 0;
int loop_from, loop_to;
int i = 0, j;
if(numerator == 0 || strl <= 1)
{
str[0] = 0;
return;
}
num_array = (int *) malloc(strl * sizeof(int));
num_array[0] = numerator;
for (i = 0; i < strl - 3; i++)
{
numerator *= radix;
str[i] = table[numerator / denominator];
numerator %= denominator;
if(numerator == 0)
{
str[i + 1] = 0;
break; //finish
}
num_array[i + 1] = numerator;
for(j = 0; j < i; j++)
{
if( numerator == num_array[j] ) // Recurring decimal
{
loop_from = j;
loop_to = i;
//printf("\nloop from %d to %d\n", loop_from, loop_to);
find_loop = 1;
break;
}
}
if(find_loop == 1)
{
str[loop_to + 2] = ']';
str[loop_to + 3] = 0;
for(j = loop_to; j >= loop_from; j --)
{
str[j + 1] = str[j];
}
str[loop_from] = '[';
break;
}
}
free(num_array);
}
一个简单的测试代码如下:
void properfrac2str(int numerator, int denominator, int radix, char str[], int strl);
int main()
{
char str[100];
properfrac2str(1, 71, 10, str, 99);
printf("0.%s\n", str);
return 0;
}
结果显示为: 0.[01408450704225352112676056338028169]
[] 内为循环小数的循环节。
然后是处理任意分数,无非就是将分数分解为整数部分和真分数部分,还要判断下正负号。难度不大,下面是代码。
int frac2str(int numerator, int denominator, int radix, char *str, int strl)
{
char strIntPart[33];
int l;
int intPart;
int sign = 1;
if(denominator == 0)
{
fprintf(stderr, "ERROR: denominator is 0\n");
return -2;
}
if( (numerator ^ denominator) < 0)
{
sign = -1;
numerator = abs(numerator);
denominator = abs(denominator);
}
// reduction of a fraction
l = gcd(numerator, denominator);
numerator /= l;
denominator /= l;
// seperate the fraction to an integer part and a proper fraction
intPart = numerator / denominator;
numerator = numerator % denominator;
itoa(intPart, strIntPart, radix);
strcat(strIntPart, ".");
l = strlen(strIntPart);
if(sign == -1)
{
str[0] = '-';
str ++;
strl --;
}
if(strl < l)
{
fprintf(stderr, "ERROR:string is too short to hold the result!\n");
return -1;
}
strcpy(str, strIntPart);
// printf("%s", str);
if(numerator != 0 && strl - l != 0)
{
properfrac2str(numerator, denominator, radix, str + l, strl - l);
}
return 0;
}
其中用到了求两个整数的最大公因数的函数,代码如下:
int gcd(int a, int b)
{
int temp;
while(a != 0)
{
temp = b % a;
b = a;
a = temp;
}
return b;
}
还用到了将整数转化成字符串的函数 itoa, 代码如下:
#define FALSE 0
#define TRUE 1
/**
* 把一整数转换为字符串
* @para num 待转化的整数
* @para str 保存转换后得到的字符串
* @para radix 基数
* @ret 指向生成的字符串
**/
char* itoa ( int num, char* str, int radix )
{
const char table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char* ptr = str;
int negative = FALSE;
if ( num == 0 ) //num=0
{
*ptr++ = '0';
*ptr = '\0'; // don`t forget the end of the string is '\0'!!!!!!!!!
return str;
}
if ( num < 0 ) //if num is negative ,the add '-'and change num to positive
{
*ptr++ = '-';
num *= -1;
negative = TRUE;
}
while ( num )
{
*ptr++ = table[num % radix];
num /= radix;
}
*ptr = '\0'; //if num is negative ,the add '-'and change num to positive
// in the below, we have to converse the string
char* start = ( negative ? str + 1 : str ); //now start points the head of the string
ptr--; //now prt points the end of the string
while ( start < ptr )
{
char temp = *start;
*start = *ptr;
*ptr = temp;
start++;
ptr--;
}
return str;
}
最后是用例:
int main()
{
char str[100];
frac2str(-666, 71, 10, str, 99);
puts(str);
return 0;
}
显示结果为:
-9.[38028169014084507042253521126760563]
经验证,这个结果没有问题。