编程之美--2.6 精确表达浮点数

/**
 * 本题目内容如下:
 * 在计算机中,有时使用float或double来存储小数是不能得到精确值的. 如果你需要得到精确计算结果,最好是用分数形式来表示小数。有限小数或者无限循环小数都可以转化为分数。
 * eg: 0.9 = 9/10;
 * 0.33(3) = 1/3(括号里的数字表示的的是循环节)
 * 当然一个小数可以用好几种弄分数形式来表示. 如:
 * 0.33(3) = 1 / 3 = 3 / 9;
 * 给定一个有限小数或无限循环小数, 你能否异分母最小的分数形式来返回这个小数呢?
 * 如果输入为循环小数, 循环节用括号标记出来.
 */


/**
 * 在此处假定, 输入的数字以回车为结束. 由于输入中带有括号, 所以可以把数字看成是字符串, 以字符的形式读入.
 * 新创建一个结构体用于表示分数, 结构体字段有两个即可: 一个分子, 一个分母;
 * 如此一来, 对于有限小数, 本题便转换成了求两个数字的最小公约数了.
 * 对于无限循环小数, 本题相对来说复杂一些, 需要寻求一种方法来计算出其循环节部分对应的分数形式
 */


#include
#include


struct fraction {
int numerator; // 分子
int denominator; // 分母
};


int power(int base, int power) {
int i = 0;
int product = 1;
for (; i < power; ++i)
product *= base;
return product;
}


int ctoi(char ch[], int length) {
int i;
int target = 0;
for (i = length - 1; i >= 0; --i) {
target += (ch[length - i - 1] - '0') * power(10, i);
}
return target;
}


int Substract(int x, int y){
int min, max, temp;
if (x > y) {
max = x;
min = y;
} else  {
max = y;
min = x;
}
while(max - min != 0) {
temp = max - min;
if (temp > min)
max = temp;
else {
max = min;
min = temp;
}
}
return min;
}


// 此函数中用于转化有限小数, 首先将小数部分转化为整数, 然后求小数部分与10^ld的最大公约数, 上下约分, 得到分数形式
struct fraction LimitedDecimals(char integer[], int li, char decimal[], int ld) {
struct fraction frac;
int numerator;
int denominator;
int divisor;
numerator = ctoi(decimal, ld);
denominator = power(10, ld);
divisor = Substract(numerator, denominator);
numerator /= divisor;
denominator /= divisor;
numerator += ctoi(integer, li) * denominator;
frac.numerator = numerator;
frac.denominator = denominator;
return frac;
}


// 此函数中用于转化无限循环小数
// integer为整数部分, decimal为小数不循环部分, cycle为循环节
// 将循环节部分转换成小数之后, 由于其位于小数点右侧ld位处, 因此需要除以10^ld, 再加上decimal转换成的分数部分, 然后再加上整数部分即可.
// 循环节转换算法:
// Y = 0.(C1C2…Cm)
// Y * 10^m = (C1C2…Cm).C1C2…Cm = C1C2…Cm + 0.(C1C2…Cm)
// Y * 10^m - Y = C1C2…Cm
// 所以, Y = C1C2…Cm / (10^m - 1)
struct fraction InfiniteLoopDecimal(char integer[], int li, char decimal[], int ld, char cycle[], int lc) {
struct fraction frac;
int numerator; // 分子
int denominator; // 分母
int numerator1, numerator2;
int denominator1, denominator2;
int divisor;
// 将循环部分转换成分数
numerator1 = ctoi(cycle, lc); // 循环部分分子
denominator1 = (power(10, lc) - 1) * power(10, ld); // 循环部分分母
numerator2 = ctoi(decimal, ld);
denominator2 = power(10, ld);
denominator = denominator1 * denominator2;
numerator = numerator1 * denominator2 + numerator2 * denominator1;
divisor = Substract(numerator, denominator);
numerator /= divisor;
denominator /= divisor;
numerator += ctoi(integer, li) * denominator;
frac.numerator = numerator;
frac.denominator = denominator;
return frac;
}


int main() {
// 分别定义三个字符串数组, 分别用于存储小数的整数部分、小数部分以及循环节
char integer[100]; // 整数部分
char decimal[100]; // 小数部分
char cycle[100]; // 循环节
int li, ld, lc; // 分别记录整数部分、小数部分和循环节的长度
char ch;
struct fraction frac;
while ((ch = getchar()) != EOF) {
li = 0;
ld = 0;
lc = 0;
while (ch != '.' && ch != '\n') {
integer[li++] = ch;
ch = getchar();
}
integer[li] = '\0';
if (ch == '\n') {
printf("这是个整数, %d\n", ctoi(integer, li));
continue;
}
while ((ch = getchar()) != '(' && ch != '\n') {
decimal[ld++] = ch;
}
decimal[ld] = '\0';
if (ch == '(') {
while((ch = getchar()) != ')') {
cycle[lc++] = ch;
}
cycle[lc] = '\0';
}
while (ch != '\n')
ch = getchar();
if (lc == 0) {
printf("这是个有限小数: ");
frac = LimitedDecimals(integer, li, decimal, ld);
printf("%d/%d\n", frac.numerator, frac.denominator);
continue;
}
printf("这是个无限循环小数: ");
frac = InfiniteLoopDecimal(integer, li, decimal, ld, cycle, lc);
printf("%d/%d\n", frac.numerator, frac.denominator);
}
return 0;
}

你可能感兴趣的:(编程之美--2.6 精确表达浮点数)