1. 程式人生 > 其它 >C語言中的小數(float,double)

C語言中的小數(float,double)

小數分為整數部分和小數部分,它們由點號.分隔,例如 0.0、75.0、4.023、0.27、-937.198 -0.27 等都是合法的小數,這是最常見的小數形式,我們將它稱為十進位制形式。

此外,小數也可以採用指數形式,例如 7.25×102、0.0368×105、100.22×10-2、-27.36×10-3 等。任何小數都可以用指數形式來表示。

C語言同時支援以上兩種形式的小數。但是在書寫時,C語言中的指數形式和數學中的指數形式有所差異。

C語言中小數的指數形式為:

aEn 或 aen

a 為尾數部分,是一個十進位制數;n 為指數部分,是一個十進位制整數;Ee是固定的字元,用於分割尾數部分和指數部分。整個表示式等價於 a×10n

指數形式的小數舉例:

  • 2.1E5 = 2.1×105,其中 2.1 是尾數,5 是指數。
  • 3.7E-2 = 3.7×10-2,其中 3.7 是尾數,-2 是指數。
  • 0.5E7 = 0.5×107,其中 0.5 是尾數,7 是指數。

C語言中常用的小數有兩種型別,分別是 float 或 double;float 稱為單精度浮點型,double 稱為雙精度浮點型。

不像整數,小數沒有那麼多么蛾子,小數的長度是固定的,float 始終佔用4個位元組,double 始終佔用8個位元組。

小數的輸出

小數也可以使用 printf 函式輸出,包括十進位制形式和指數形式,它們對應的格式控制符分別是:

  • %f 以十進位制形式輸出 float 型別;
  • %lf 以十進位制形式輸出 double 型別;
  • %e 以指數形式輸出 float 型別,輸出結果中的 e 小寫;
  • %E 以指數形式輸出 float 型別,輸出結果中的 E 大寫;
  • %le 以指數形式輸出 double 型別,輸出結果中的 e 小寫;
  • %lE 以指數形式輸出 double 型別,輸出結果中的 E 大寫。

下面的程式碼演示了小數的表示以及輸出:

  • #include <stdio.h>
  • #include <stdlib.h>
  • int main()
  • {
  • float a = 0.302;
  • float b = 128.101;
  • double c = 123;
  • float d = 112.64E3;
  • double e = 0.7623e-2;
  • float f = 1.23002398;
  • printf("a=%e \nb=%f \nc=%lf \nd=%lE \ne=%lf \nf=%f\n", a, b, c, d, e, f);
  • return 0;
  • }

執行結果:

a=3.020000e-01
b=128.100998
c=123.000000
d=1.126400E+05
e=0.007623
f=1.230024

對程式碼的說明:

1) %f 和 %lf 預設保留六位小數,不足六位以 0 補齊,超過六位按四捨五入截斷。

2) 將整數賦值給 float 變數時會變成小數。

3) 以指數形式輸出小數時,輸出結果為科學計數法;也就是說,尾數部分的取值為:0 ≤ 尾數 < 10。

4) b 的輸出結果讓人費解,才三位小數,為什麼不能精確輸出,而是輸出一個近似值呢?這和小數在記憶體中的儲存形式有關,很多簡單的小數壓根不能精確儲存,所以也就不能精確輸出,我們將在下節《小數在記憶體中是如何儲存的,揭祕諾貝爾獎級別的設計(長篇神文)》中詳細講解。

另外,小數還有一種更加智慧的輸出方式,就是使用%g。%g 會對比小數的十進位制形式和指數形式,以最短的方式來輸出小數,讓輸出結果更加簡練。所謂最短,就是輸出結果佔用最少的字元。

%g 使用示例:

  • #include <stdio.h>
  • #include <stdlib.h>
  • int main()
  • {
  • float a = 0.00001;
  • float b = 30000000;
  • float c = 12.84;
  • float d = 1.229338455;
  • printf("a=%g \nb=%g \nc=%g \nd=%g\n", a, b, c, d);
  • return 0;
  • }

執行結果:

a=1e-05
b=3e+07
c=12.84
d=1.22934

對各個小數的分析:

  • a 的十進位制形式是 0.00001,佔用七個字元的位置,a 的指數形式是 1e-05,佔用五個字元的位置,指數形式較短,所以以指數的形式輸出。
  • b 的十進位制形式是 30000000,佔用八個字元的位置,b 的指數形式是 3e+07,佔用五個字元的位置,指數形式較短,所以以指數的形式輸出。
  • c 的十進位制形式是 12.84,佔用五個字元的位置,c 的指數形式是 1.284e+01,佔用九個字元的位置,十進位制形式較短,所以以十進位制的形式輸出。
  • d 的十進位制形式是 1.22934,佔用七個字元的位置,d 的指數形式是 1.22934e+00,佔用十一個字元的位置,十進位制形式較短,所以以十進位制的形式輸出。

讀者需要注意的兩點是:

  • %g 預設最多保留六位有效數字,包括整數部分和小數部分;%f 和 %e 預設保留六位小數,只包括小數部分。
  • %g 不會在最後強加 0 來湊夠有效數字的位數,而 %f 和 %e 會在最後強加 0 來湊夠小數部分的位數。

總之,%g 要以最短的方式來輸出小數,並且小數部分表現很自然,不會強加零,比 %f 和 %e 更有彈性,這在大部分情況下是符合使用者習慣的。

除了 %g,還有 %lg、%G、%lG:

  • %g 和 %lg 分別用來輸出 float 型別和 double 型別,並且當以指數形式輸出時,e小寫。
  • %G 和 %lG 也分別用來輸出 float 型別和 double 型別,只是當以指數形式輸出時,E大寫。

數字的字尾

一個數字,是有預設型別的:對於整數,預設是 int 型別;對於小數,預設是 double 型別。

請看下面的例子:

  • long a = 100;
  • int b = 294;
  • float x = 52.55;
  • float y = 18.6;

100 和 294 這兩個數字預設都是 int 型別的,將 100 賦值給 a,必須先從 int 型別轉換為 long 型別,而將 294 賦值給 b 就不用轉換了。

52.55 和 18.6 這兩個數字預設都是 double 型別的,將 52.55 賦值給 x,必須先從 double 型別轉換為 float 型別,而將 18.6 賦值給 y 就不用轉換了。

如果不想讓數字使用預設的型別,那麼可以給數字加上字尾,手動指明型別:

  • 在整數後面緊跟 l 或者 L(不區分大小寫)表明該數字是 long 型別;
  • 在小數後面緊跟 f 或者 F(不區分大小寫)表明該數字是 float 型別。

請看下面的程式碼:

  • long a = 100l;
  • int b = 294;
  • short c = 32L;
  • float x = 52.55f;
  • double y = 18.6F;
  • float z = 0.02;

加上字尾,雖然數字的型別變了,但這並不意味著該數字只能賦值給指定的型別,它仍然能夠賦值給其他的型別,只要進行了一下型別轉換就可以了。

對於初學者,很少會用到數字的字尾,加不加往往沒有什麼區別,也不影響實際程式設計,但是既然學了C語言,還是要知道這個知識點的,萬一看到別人的程式碼這麼用了,而你卻不明白怎麼回事,那就尷尬了。

關於資料型別的轉換,我們將在《C語言資料型別轉換》一節中深入探討。

小數和整數相互賦值

在C語言中,整數和小數之間可以相互賦值:

  • 將一個整數賦值給小數型別,在小數點後面加 0 就可以,加幾個都無所謂。
  • 將一個小數賦值給整數型別,就得把小數部分丟掉,只能取整數部分,這會改變數字本來的值。注意是直接丟掉小數部分,而不是按照四捨五入取近似值。

請看下面的程式碼:

  • #include <stdio.h>
  • int main(){
  • float f = 251;
  • int w = 19.427;
  • int x = 92.78;
  • int y = 0.52;
  • int z = -87.27;
  • printf("f = %f, w = %d, x = %d, y = %d, z = %d\n", f, w, x, y, z);
  • return 0;
  • }

執行結果:f = 251.000000, w = 19, x = 92, y = 0, z = -87

由於將小數賦值給整數型別時會“失真”,所以編譯器一般會給出警告,讓大家引起注意。