方星星 呂永強



摘? 要 C語言的基本數據類型分為:整型、字符型和浮點型,大多C語言教材都概括了整型和字符型數據的編碼及輸入輸出,但并未詳細介紹浮點型數據的編碼及輸入輸出,這導致很多學生不能靈活運用這一知識點。本文為了彌補教材的不足和便于學生更好掌握浮點數的輸入輸出,首先分析了float和double數據的編碼,再歸納出浮點型數據按十進制、二進制和十六進制輸出的三種方法,最后結合內存結構和具體事例對float數據double數據的輸入作了深入分析,并闡述了double數據%f和%lf輸入的區別。
關鍵詞 輸入輸出;%f和%lf;浮點型數據;C語言
中圖分類號:G642? ? 文獻標識碼:B
文章編號:1671-489X(2019)04-0044-03
Abstract The basic data types of C language are divided into integer, character type and floating-point. Most of the C language textbooks summarize the encoding and the input and output of integer and character data, but do not introduce the encoding and the input and output of floating-point data in detail, which leads to the fact that many students cannot use this knowledge flexibly. In order to make up for the deficiency of the textbook and make it easier for students to better master the input and output of floating point numbers, this
paper first analyzes the encoding of float and double data, then summarizes three methods of floating-point data output by decimal, binary and hexadecimal system, and finally makes an in-depth analy-
sis of the input of double data of float data combined with memory structure and specific cases, and explains the difference between the input of double data by %f and by %lf.
Key words input and output; by %f and by %lf; floating-point data; C language
1 前言
浮點數即實數,分為單精度(float或single)、雙精度(double)和長雙精度(long double)三類。精度越高,則該類型表示的數據取值范圍越大,占內存字節數越多。在C語言中,一般只涉及float類型和double類型數據,教材對于兩者的輸入輸出都有所介紹,但對于浮點數的編碼原理、十進制以外的輸出形式、數據輸入的原理等方面的內容提及較少。除此以外,不少教材在描述浮點型數據輸入輸出時,雖然指出double型數據的輸入只能用%f,輸出可以用%f,也可以用%lf,但并未指明原因。以上諸多因素致使學生在學習時有很多困惑。為答疑釋惑,本文從浮點數編碼、浮點數的三種輸出形式、float數據輸入和double數據輸入等四個方面來闡述浮點數的輸入輸出。
2 float和double數據的編碼
浮點數的二進制編碼不同于整型數和字符型數。1985年,為了統一浮點數的存儲格式,IEEE制定了IEEE 754標準。目前,絕大多數計算機都遵守這一標準,極大地改善了各種軟件的可移植性。ANSI C采用該標準對float和double進行編碼。
float數據的編碼? 根據IEEE 754的規定,浮點數在編碼時首先要進行規格化,即用如下形式表示:
規格化數=符號位·尾數×2n其中,符號位為0或1,分別表示正數或負數;尾數是形式為1.XXX…XXX的二進制數,小數點之前為1(但0屬于特殊情況,需要特殊處理);n為指數,二進制形式(習慣上寫成十進制)。
float數據存儲時占四個字節(32位),IEEE 754規定各位的意義及格式如圖1所示。
在存儲時,尾數中的“1.”不存儲,目的是節省存儲空間。另外,階碼等于規格化中的指數加上127,即階碼=指數+127。因為指數可以是負數,取值范圍是-126~127(-127預留為特殊使用),為了便于處理負指數的情況,IEEE 754標準要求指數加上127后存儲。如float數據1.23經過規格化后為1.0011101 01110000 10100100×20(尾數考慮二進制進位),存儲時的指數為0+127=127=01111111,因此,float數據1.23的最終的存儲格式如圖2所示。
double數據的編碼? 雙精度浮點數的規格化過程同float數,但在存儲格式上,雙精度浮點數占八個字節(64位),IEEE 754規定各位的意義及格式如圖3所示。其中符號位、階碼和尾數分別占1、11和52位。另外,階碼=規格化中的指數+1023。
如double數據1.23經過規格化后為1.00111010111000010100 01111010 11100001 01000111 10101110×20(尾數考慮進位),存儲時的指數為0+1023=1023=01111111111,因此,double數據1.23的最終存儲格式如圖4所示。
與單精度相比,雙精度浮點數的階碼和尾數的位數更長,因此,雙精度所能表示的數值范圍更大且精度更高,可提供更多的有效數字位數,float變量只能接收十進制有效位數為7位,而double變量能接收16位有效數字。
3 浮點數的輸出
浮點數有十進制、二進制和十六進制三種輸出形式,但是大多數的教材只介紹了十進制形式的輸出。需要說明的是,十進制形式分為小數形式和指數形式,本文只介紹小數形式輸出。另外,為便于深入分析用%f和%lf輸入浮點數時的真實值,在此延伸介紹浮點數的二進制和十六進制輸出。
十進制輸出(小數形式)? 格式說明:%f是將浮點數以小數形式輸出。float型數據用%f輸出,doule型數據一般用%lf輸出。由于printf是一個可變長度列表的函數,當調用printf函數時,float會自動轉換成double類型,其結果是printf函數無法區分float類型和double類型參數。因此,printf函數中的%f既可以用于float數據輸出,也可以用于double數據輸出。如果變量類型為float,則只輸出四個字節對應的浮點數;如果變量類型為double,會輸出八個字節對應的浮點數。需要說明的是,double變量最多能接收16位有效數字,%f默認只輸出小數點后6位,如果要輸出更多有效位數,要加修飾符.n,即%.nf(.n表示輸出小數的位數)。如%.16f用于輸出小數點后16位。
十六進制輸出? 格式說明:%x一般用于整型數據的十六進制輸出,如果用于浮點數,則是將浮點數的二進制編碼以十六進制輸出。float數的十六進制輸出用%x,double數的十六進制輸出用%llx。如float數1.23和double數1.23按十六進制輸出的結果分別為3f9d70a4和3ff3ae147ae147ae(讀者可自行驗證)。
二進制輸出? 二進制輸出沒有對應的格式說明,需要編寫程序將變量的每個字節按二進制位進行輸出。設計程序時,引入一個共用體,包含字符數組和浮點型變量兩個成員,它們共用四個字節或八個字節的存儲空間,定義形式如下:
union bianma{char ch[4];float a;}? ? //處理double時成員定義為char ch[8];和double a;將字符數組元素按從后到前的順序進行排列,每個元素都以二進制輸出,最終的輸出結果即為該浮點數的二進制輸出。字符數據以二進制形式輸出,需要進行二進制位的移位運算,對應的程序代碼如下:
4 float數據的輸入
文中的浮點型數據輸入僅指十進制小數形式的輸入。float數據的輸入用%f,說明輸入的是十進制小數。格式說明:%f指明輸入的數據按float型處理,系統將此數據按float類型編碼后存入變量占用的四個存儲單元中。存儲時,低地址存儲單元存放數據的低字節,高地址存儲單元用于存放數據的高字節。如執行語句段float a;scanf(“%f”,&a);時,鍵盤輸入1.23,則變量a占用的四個內存單元值如圖7所示(假設變量a的地址為2000H)。
5 double數據的輸入
正確實現double數據的輸入(只考慮十進制小數形式)只能用%lf,如果改用%f,則輸入不當。
用%lf輸入double數? 此用法能實現正確輸入,過程同float數據的輸入。格式說明:%lf指明輸入的數據按double處理,系統先將此數據按double類型編碼,再存入變量占用的八個存儲單元中。如執行語句段double b;scanf(“%f”,&b);時,輸入1.23,則變量b占用的八個內存單元值如圖8所示(假設變量b的地址為2000H)。
用%f輸入double數? 該用法不能正確輸入double數。如執行語句double e;scanf(“%f”,&e);時,輸入1.23,變量e的值為0。這是由于%f指明輸入的數據只按float類型處理,數據按float類型編碼后只存放到e占用的前四個存儲單元中,即低地址存儲單元。而變量e實際占用八個存儲單元,系統自動將0存儲到四個高地址存儲單元,因此,變量e占用的八個內存單元真實值如圖9所示(假設變量e的地址為2000H)。不難得出,這八個字節對應的double數十進制值為0(有效位數為15位),因此,執行語句printf(“%lf”,e);后,得出e的值為0。
6 結語
為便于掌握%f和%lf的用法,本文在分析float和double數據的編碼原理基礎上,提出浮點數的三種輸出方法,再分別介紹float和double數據的輸入,最后總結double數據用%f和%lf進行數據輸入的區別。由于浮點數的編碼較為復雜,C語言又嚴格規定了%f和%lf的用途,因此,浮點數的輸入輸出不同于整型和字符型,不便于掌握。在實際應用中,為避免數據輸入輸出結果不一致,建議float數據輸入輸出統一用%f,double數據輸入輸出統一用%lf。
參考文獻
[1]譚浩強.C程序設計[M].4版.北京:清華大學出版社,2010.
[2]張亞玲.大學計算機基礎:計算思維初步[M].北京:清華大學出版社,2014.
[3]朱亞超.基于IEEE 754的浮點數存儲格式分析研究[J].計算機與信息技術,2006(9):50-52.
[4]田祎,樊景博.C語言中浮點數的表示范圍淺析[J].軟件工程,2016(4):8-10.
[5]IEEE Standard for Binary Floating-Point Arithme-tic.ANSI/IEEE Standard 754-1985[M]//Institute of Electrical and Electronics Engineers.1985.