最近在翻看一本数学分析教材中关于实数的基本理论的章节,其中对有理数的小数表示方法给出了严密的定义和详细的讨论。看后很有收获,然后突然就想到了这么个问题,如何编个程序将有理数的分数表示转换为小数表示,并且对于无限循环小数还要给出循环节来。
花了半天时间将程序写好调通。把程序放在这里做个记录。
首先我先写了个只能处理正的真分数的程序。这是最简单的情况。不用判断正负号,也不用考虑整数部分。虽然说是任意进制,但是程序只支持到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; }
#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]
经验证,这个结果没有问题。