编程之美读书笔记-精确表达浮点数

来源:互联网 发布:人工智能 高考志愿 编辑:程序博客网 时间:2024/05/15 00:27

题目:在计算机中,使用float或者double来存储小数是不能得到精确值的。如果你希望得到精确计算结果,最好是用分数形式来表示小数。有限小数或者无限循环小数都可以转化为分数。比如:
0.9=9/10
0.333(3)=1/3(括号中的数字表示是循环节)
当然一个小数可以用好几种分数形式来表示。如:0.333(3)=1/3=3/9
给定一个有限小数或者无限循环小数,你能否以分母最小的分数形式来返回这个小数呢?如果输入为循环小数,循环节用括号标记出来。下面是一些可能的输入数据,如0.3、0.30、0.3(000)、0.3333(3333)、……
解析:拿到这样一个问题,我们往往会从最简单的情况入手,因为所有的小数都可以分解成一个整数和一个纯小数之和,不妨只考虑大于0,小于1的纯小数,而且暂时不考虑分子和分母的约分,先设法将其表示为分数形式,然后再进行约分。题目中输入的小数,要么为有限小数X=0.a1a2…an,要么为无限循环小数X=0.a1a2…an(b1b2…bm),X的表示式中的字母a1a2…an,b1b2…bm都是0~9的数字,括号部分表示循环节,我们需要处理的就是以上两种情况。
对于有限小数X=0.a1a2…an来说,这个问题比较简单,X就等于(a1a2…an)/10^n。 
对于无限循环小数X=0.a1a2…an(b1b2…bm)来说,其复杂部分在于小数点后同时有非循环部分和循环部分,我们可以做如下的转换:
X=0.a1a2…an(b1b2…bm)
=>10^n*X=a1a2…an.(b1b2…bm)
=>10^n*X=a1a2…an+0.(b1b2…bm)
=>X=(a1a2…an+0.(b1b2…bm))/10^n
对于整数部分a1a2…an,不需要做额外处理,只需要把小数部分转化为分数形式再加上这个整数即可。对于后面的无限循环部分,可以采用如下方式进行处理: 
令Y=0.b1b2…bm,那么
10^m*Y=b1b2…bm.(b1b2…bm)
=>10^m*Y=b1b2…bm+0.(b1b2…bm)
=>10^m*Y-Y=b1b2…bm
=>Y=b1b2…bm/(10^m-1)
将Y代入前面的X的等式可得:
X=(a1a2…an+Y)/10^n
=(a1a2…an+b1b2…bm/(10^m-1))/10^n
=((a1a2…an)*(10^m-1)+(b1b2…bm))/((10^m-1)*10^n)
至此,便可以得到任意一个有限小数或无限循环小数的分数表示,但是此时分母未必是最简的,接下来的任务就是让分母最小,即对分子和分母进行约分,这个相对比较简单。
例如,对于小数0.3(33),根据上述方法,可以转化为分数:
0.3(33)
=(3*(10^2-1)+33)/((10^2-1)*10)
=(3*99+33)/990
=1/3
对于小数0.285714(285714),我们也可以算出:
0.285714(285714)
=(285714*(10^6-1)+285714)/((10^6-1)*10^6)
=(285714*999999+285714)/999999000000
=285714/999999
=2/7

#include<cmath>#include<iostream>using namespace std;int gcd(int x, int y){if (x < y) return gcd(y, x);if (y == 0) return x;if (x % 2){if (y % 2) return gcd(y, x - y);else return gcd(x, y >> 1);}else{if (y % 2) return gcd(x >> 1, y);else return gcd(x >> 1, y >> 1) << 1;}}int main(){char fractions[100];while (gets_s(fractions,100)){int dot;                              int infinite = -1;                 int len = strlen(fractions);for (int i = 0; i<len; i++){if (fractions[i] == '.') dot = i;if (fractions[i] == '(') infinite = i;}//有限小数if (infinite == -1){int count = 0;char result[100] = {0};for (int i = 0; i<len; i++){if (fractions[i] != '.'){result[count++] = fractions[i];}}int numerator = atoi(result);int denominator = pow(10.0, len - (dot + 1));int res = gcd(numerator, denominator);numerator = numerator / res;denominator = denominator / res;printf("%d/%d\n", numerator, denominator);}//无限小数else {                           int n = infinite - dot - 1;int m = len - 1 - infinite - 1;int numerator=0, denominator=0;for (int i = dot + 1; i<infinite; i++){numerator += (fractions[i] - 48)*(pow(10.0, n - i + dot));}numerator = numerator*(pow(10.0, m) - 1);for (int i = infinite + 1; i<len - 1; i++){numerator += (fractions[i] - 48)*(pow(10.0, m - i + infinite));}denominator = (pow(10.0, m) - 1)*pow(10.0, n);int res = gcd(numerator, denominator);numerator = numerator / res;denominator = denominator / res;printf("%d/%d\n", numerator, denominator);}}return 0;}


1 0