- 編寫高質(zhì)量代碼:改善C程序代碼的125個建議
- 馬偉 著
- 1365字
- 2019-01-01 01:33:11
建議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é)果
- Java 9 Concurrency Cookbook(Second Edition)
- Mastering OpenCV Android Application Programming
- Effective C#:改善C#代碼的50個有效方法(原書第3版)
- Java應(yīng)用開發(fā)技術(shù)實例教程
- C語言程序設(shè)計案例精粹
- Microsoft Dynamics AX 2012 R3 Financial Management
- 基于SpringBoot實現(xiàn):Java分布式中間件開發(fā)入門與實戰(zhàn)
- Cocos2d-x Game Development Blueprints
- Python Interviews
- Clojure for Java Developers
- QPanda量子計算編程
- HTML5+CSS3+JavaScript 從入門到項目實踐(超值版)
- Oracle Data Guard 11gR2 Administration Beginner's Guide
- Clojure for Finance
- Effective C++:改善程序與設(shè)計的55個具體做法(第三版)中文版(雙色)