官术网_书友最值得收藏!

建議3-3:使用分數(shù)來精確表達浮點數(shù)

在上面的建議3-1與建議3-2中,已經(jīng)明確闡述,由于計算機的字長有限,浮點數(shù)能夠精確表示的數(shù)是有限的。因此,在C語言中使用float或者double類型來存儲小數(shù)是不能得到精確值的。雖然在建議3-2的最后提出使用整數(shù)的方法來解決浮點數(shù)的精確計算問題,但這種方案不具備代表性和通用性。因此,本建議將向你介紹第二種精確表示浮點數(shù)的解決方法,即用分數(shù)來表示浮點數(shù)。

對于一個浮點數(shù),我們可以簡單地把它分解成整數(shù)和純小數(shù)兩個部分來分開表示。比如浮點數(shù)據(jù)10.234,它的整數(shù)部分是10,純小數(shù)部分是0.234。而對于純小數(shù)部分,受計算機字長的限制,存儲時會因為舍入規(guī)則而導(dǎo)致失去精確性。因此,這個時候就可以將純小數(shù)部分使用分數(shù)來表示。比如,對于有限小數(shù),我們可以這樣來表示:

由此,我們可以很簡單地推導(dǎo)出它的表示形式。對于有限小數(shù)X=0.a1a2…an(其中,a1,a2,…,an為0~9之間的數(shù)字),它的分數(shù)表示形式如下:

上面闡述了有限小數(shù)的表示形式,但對于無限循環(huán)小數(shù)如何表示呢?

其實仍然可以使用上面的這種形式來表示。但無限循環(huán)小數(shù)比有限小數(shù)多了一個循環(huán)部分,因此我們可以用如下方式來表示:

其中,(3)表示循環(huán)體。即,對于無限循環(huán)小數(shù)X=0.a1a2…an(b1b2…bm)(其中,a1,a2,…,an與b1,b2,…,bm都是0~9的數(shù)字,a1a2…an表示非循環(huán)部分,括號部分(b1b2…bm)表示循環(huán)部分),它的表現(xiàn)形式如下:

至于循環(huán)部分(b1b2…bm),我們可以這樣處理,即令Y=0.b1b2…bm,那么:

將Y代入X,即:

也就是:

例如,對于小數(shù)0.3(3),根據(jù)上述方法轉(zhuǎn)化為分數(shù)應(yīng)為:

下面,我們通過一個示例程序演示一下如何在C語言程序中使用這種表達方式將浮點數(shù)表示為分數(shù)形式。值得注意的是,這里的精度只能達到long int類型,再大就會發(fā)生溢出。如代碼清單1-20所示。

代碼清單1-20 分數(shù)表達浮點數(shù)示例


#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
long dtol( double d );
long  gcd(long  a, long  b);
void convertdata(char *str);
struct
{
    long  zhengshu;
    long  xiaoshu;
    long  xunhuan;
    long  fenmu;
    long  fenzi;
}fenshu;
union udtol
{
    double d;
    long l;
};
long dtol( double d )
{
    union udtol to;
    to.d = d + 6755399441055744.0;
    return to.l;
}
long gcd(long  a, long  b)
{
    if(!b)
    {
            return a;
    }
    else
    {
            return gcd(b, a % b);
    }
}
void convertdata(char *str)
{
    int n = 0;
    int m = 0;
    int len=0;
    int len_1 = 0;
    int len_2 = 0;
    int len_3 = 0;
    char *p1;
    char *p2;
    char *p3;
    int i=0;
    int j=0;
    int z=0;
    long  gcb=0;
    fenshu.zhengshu =0;
    fenshu.xiaoshu =0;
    fenshu.xunhuan = 0;
    len = strlen(str);
    len_1 = len;
    p1 = strchr(str, '.');
    p2 = strchr(str, '(');
    p3 = strchr(str, ')');
    if(p1)
    {
            len_1 = p1 - str;
    }
    if(!p2)
    {
            len_2 = len - len_1 - 1;
    }
    if(p3)
    {
            len_2 = p2 - p1 - 1;
            len_3 = p3 - p2 - 1;
    }
    n = len_2;
    m = len_3;
    for(i = 0; i < len_1; i++)
    {
            fenshu.zhengshu *= 10;
            fenshu.zhengshu += str[i] - '0';
    }
    for(j = 0; j < len_2; j++)
    {
            fenshu.xiaoshu *= 10;
            fenshu.xiaoshu += str[len_1+1+j] - '0';
    }
    for(z = 0; z < len_3; z++)
    {
            fenshu.xunhuan *= 10;
            fenshu.xunhuan += str[len_1+len_2+2+z] - '0';
    }
    fenshu.fenmu =dtol((pow(10.0, (double)(m)) - 1.0)
                     * (pow(10.0, (double)(n))));
    fenshu.fenzi = dtol(fenshu.xiaoshu
                     * (pow(10.0, (double)(m)) - 1.0)
                     + fenshu.xunhuan);
    gcb = gcd(fenshu.fenzi, fenshu.fenmu);
    fenshu.fenmu /= gcb;
    fenshu.fenzi /= gcb;
}
int main(void)
{
    for(;;)
    {
            char str[200] = "";
            printf("請輸入要轉(zhuǎn)換的數(shù)(如25.444(234)):");
            scanf("%s",&str);
            convertdata(str);
            printf("整數(shù)部分:%ld--小數(shù)部分:%ld--循環(huán)部分:%ld\n",
                fenshu.zhengshu,fenshu.xiaoshu,fenshu.xunhuan);
            printf("所得分數(shù)為:%ld/%ld\n",
                fenshu.fenzi,fenshu.fenmu);
    printf("-------------------------------------------\n");
    }
    return 0;
}

代碼清單1-20的運行結(jié)果如圖1-32所示。

在圖1-32中,通過分數(shù)的形式表示浮點數(shù),從而避免浮點數(shù)因為舍入誤差而導(dǎo)致的不精確性問題。有興趣的朋友可以在這個示例代碼的基礎(chǔ)繼續(xù)深入研究,從而使該方案能夠用于實際的開發(fā)環(huán)境中。

圖1-32 代碼清單1-20的運行結(jié)果

主站蜘蛛池模板: 高邑县| 龙口市| 抚远县| 神农架林区| 浏阳市| 大理市| 扶余县| 义马市| 金阳县| 玉龙| 海门市| 巴东县| 枝江市| 贵阳市| 潍坊市| 宝山区| 东明县| 重庆市| 台南市| 醴陵市| 调兵山市| 裕民县| 奉新县| 浮山县| 华亭县| 宝鸡市| 油尖旺区| 井陉县| 唐海县| 嘉荫县| 东兰县| 裕民县| 丹阳市| 黄浦区| 淄博市| 通山县| 金门县| 盱眙县| 青铜峡市| 沽源县| 泸州市|