日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

C语言的本质(4)——浮点数的本质与运算

發(fā)布時間:2025/4/14 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言的本质(4)——浮点数的本质与运算 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

C語言的本質(zhì)(4)——浮點(diǎn)數(shù)的本質(zhì)與運(yùn)算

?

C語言規(guī)定了3種浮點(diǎn)數(shù),float型、double型和long double型,其中float型占4個字節(jié),double型占8個字節(jié),longdouble型長度要大于等于double型,本文檔將以float型為例進(jìn)行介紹,double型和long double型只是比float型位數(shù)長,原理都是一樣的。

?

float型可以表示的范圍是-3.402823466e38~3.402823466e38,而作為同為4個字節(jié)的定點(diǎn)數(shù)卻只能表示-2147483648~2147483647的范圍,使用同樣的內(nèi)存空間,浮點(diǎn)數(shù)卻能比定點(diǎn)數(shù)表示大得多的范圍,這是不是太神奇了?既然浮點(diǎn)數(shù)能表示這么大的范圍,那么我們?yōu)楹尾皇褂酶↑c(diǎn)數(shù)來代替定點(diǎn)數(shù)呢?

?

先不說浮點(diǎn)數(shù)實(shí)現(xiàn)起來比較復(fù)雜,有些處理器還專門配置了硬件浮點(diǎn)運(yùn)算單元用于浮點(diǎn)運(yùn)算,主要原因是浮點(diǎn)數(shù)根本就無法取代定點(diǎn)數(shù),因?yàn)榫葐栴}。魚和熊掌不可兼得,浮點(diǎn)數(shù)表示了非常大的范圍,但它失去了非常準(zhǔn)的精度。在說明精度問題前,我們先了解一下浮點(diǎn)數(shù)的格式。

?

ANSI/IEEEStd 754-1985標(biāo)準(zhǔn)

IEEE 754是最廣泛使用的二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn),被許多CPU與浮點(diǎn)運(yùn)算器所采用。IEEE754規(guī)定了多種表示浮點(diǎn)數(shù)值的方式,在本文檔里只介紹32bits的float浮點(diǎn)類型。它被分為3個部分,分別是符號位S(sign bit)、指數(shù)偏差E(exponent bias)和小數(shù)部分F(fraction)。

其中S位占1bit,為bit31。S位為0代表浮點(diǎn)數(shù)是正數(shù),S位為1代表浮點(diǎn)數(shù)是負(fù)數(shù),比如說0x449A522C的S位為0,表示這是一個正數(shù),0x849A522C的S位為1,表示這是一個負(fù)數(shù)。

E位占8bits,為bit23~bit30。E位代表2的N次方,但需要減去127,比如說E位為87,那么E位的值為2(87-127)=9.094947017729282379150390625e-13。

F位占23bits,為bit0~bit22。F位是小數(shù)點(diǎn)后面的位數(shù),其中bit22是2-1=0.5,bit21是2-2=0.25,以此類推,bit0為2-23=0.00000011920928955078125。但F位里隱藏了一個1,也就是說F位所表示的值是1+(F位bit22~bit0所表示的數(shù)值),比如說F位是0b10100000000000000000001,只有bit22、bit20和bit0為1,那么F位的值為1+(2-1+2-3+2-23),為1.62500011920928955078125。

?

?綜上所述,從二進(jìn)制數(shù)換算到浮點(diǎn)數(shù)的公式為:(-1)S×2E-127×(1+F)。但還有幾個特殊的情形:

?

1、若E位為0并且F位也為0時表示浮點(diǎn)數(shù)0,此時浮點(diǎn)數(shù)受S位影響,表現(xiàn)出+0和-0兩種0,但數(shù)值是相等的。比如二進(jìn)制數(shù)0x00000000表示+0,二進(jìn)制數(shù)0x80000000表示-0。

2、若E位為0并且F位不為0時浮點(diǎn)數(shù)為(-1)S×2-126×F,注意,E位的指數(shù)是-126,而不是0-127=-127,而且F位是0.xx格式而不是1.xx格式,比如0x00000001的浮點(diǎn)數(shù)為2-126×2-23=1.4012984643248170709237295832899e-45,而不是20-121×(1+2-23)。一旦E為不為0,從0變?yōu)?,不是增加2倍的關(guān)系,因?yàn)楣礁淖兞恕?/p>

3、若E位為255并且F位不為0時表示非數(shù)值,也就是說是非法數(shù),例如0x7F800001。

4、 若E位為255并且F位為0時表示無窮大的數(shù),此時浮點(diǎn)數(shù)受S位影響,例如0x7F800000表示正無窮大,0xFF800000表示負(fù)無窮大。當(dāng)我們使用1個數(shù)除以0時,結(jié)果將被記作0x7F800000。

???????

浮點(diǎn)型在多個處理器間通信時,傳遞的數(shù)值是它的二進(jìn)制數(shù),比如說1234.5678這個浮點(diǎn)數(shù)的二進(jìn)制數(shù)是0x449A522B,如果使用串口發(fā)送的話,就會發(fā)現(xiàn)串口里發(fā)送的是0x44、0x9A、0x52和0x2B這4個數(shù)(發(fā)送的順序也可能是逆序,這與約定的字節(jié)序有關(guān),與浮點(diǎn)格式無關(guān)),接收端接收到這4個數(shù)字后再組合成0x449A522B,按照IEEE 754的定義被解析成1234.5678,這樣就實(shí)現(xiàn)浮點(diǎn)數(shù)通信了。如果兩個處理器所使用的浮點(diǎn)數(shù)規(guī)則不同,則無法傳遞浮點(diǎn)數(shù)。

???????

浮點(diǎn)數(shù)的換算

下面來看看浮點(diǎn)數(shù)與二進(jìn)制數(shù)如何轉(zhuǎn)換。

1、二進(jìn)制數(shù)換算成浮點(diǎn)數(shù):

?

假如在內(nèi)存中有一個二進(jìn)制數(shù)為0x449A522C,先將十六進(jìn)制轉(zhuǎn)換成二進(jìn)制,如下:

0100?0100? 1001? 1010?0101? 0010? 0010?1100

?

按照SEF的格式分段,如下:

0?10001001? 00110100101001000101100

?

這個數(shù)值不是特殊的情形,可以按照公式(-1)S×2E-127×(1+F)轉(zhuǎn)換。S位的值為(-1)0=1,E位的值為2137-127=1024。F位的值為1+2-3+2-4+2-6+2-9+2-11+2-14+2-18+2-20+2-21= 1.205632686614990234375。最終結(jié)果為1×1024×1.205632686614990234375=1234.56787109375。

?

其中F位比較長,使用二進(jìn)制方式轉(zhuǎn)換比較麻煩,也可以先轉(zhuǎn)換成十六進(jìn)制再計算,轉(zhuǎn)換為十六進(jìn)制如下:

0011?0100? 1010? 0100?0101? 1000

0x3??0x4?? 0xA?? 0x4??0x5?? 0x8

?

F位為23bits,需要在最后補(bǔ)一個0湊成24bits,共6個十六進(jìn)制數(shù)。F位的值為1+3×16-1+4×16-2+10×16-3+4×16-4+5×16-5+8×16-6=1.205632686614990234375,與上面使用二進(jìn)制方法得到的結(jié)果相同。

?

2、浮點(diǎn)數(shù)換算成二進(jìn)制數(shù):

?

下面我們將-987.654e30換算成二進(jìn)制數(shù)。我們先不看符號位,將987.654e30歸一化為整數(shù)部分為1的形式,也就是寫作987.654e30=2E-127×(1+F)的標(biāo)準(zhǔn)形式,其中E=log(987.654e30)/log2+127=109.6+127,取E位的整數(shù)值為109+127=236,再求F=987.654e30/2236-127-1=0.52172193,這個小數(shù)位數(shù)保留8位就夠了,已經(jīng)超出了7位的精度。然后我們求小數(shù)部分的二進(jìn)制數(shù),這個轉(zhuǎn)換就沒啥好說的了,依次減去2的冪,從2-1一直到2-23,夠減的位置1,不夠減的位置0,例如,2-1為0.5,0.52172193-0.5=0.02172193,F位的bit22置1,2-2為0.25,0.02172193不夠減,F位的bit21置0,2-3為0.125,0.02172193不夠減,F位的bit20置0,2-4為0.0625,0.02172193不夠減,F位的bit19置0……,一直算到F位的bit0,這樣就得到F位的數(shù)值。

?

如果覺得使用二進(jìn)制方式轉(zhuǎn)換太麻煩的話也可以使用十六進(jìn)制進(jìn)行轉(zhuǎn)換。16-1為0.0625,0.52172193/0.0625=8.3,說明夠減8個,記做0x8,0.52172193-0.0625×8=0.02172193,16-2為0.00390625,0.02172193/0.00390625=5.6,說明夠減5個,加上剛才的0x8記做0x85,以此類推:


?

16的-N次冪

被減數(shù)

十六進(jìn)制數(shù)

減后的數(shù)

1

0.0625

0.52172193

0x8

0.02172193

2

0.00390625

0.02172193

0x85

0.00219068

3

0.000244140625

0.00219068

0x858

0.000237555

4

0.0000152587890625

0.000237555

0x858F

0.0000086731640625

5

0.00000095367431640625

0.0000086731640625

0x858F9

0.00000009009521484375

6

0.000000059604644775390625

0.00000009009521484375

0x858F91

?

?

一直湊夠23bits,也就是6個十六進(jìn)制,得到0x858F91,換算成二進(jìn)制如下所示:

1000?0101? 1000? 1111?1001? 0001

?

由于只有23bits有效,因此需要去掉最后一個bit,二進(jìn)制本著0舍1入的原則,變成

1000?0101? 1000? 1111?1001? 001

?

最后需要再補(bǔ)上前面的S位和E位。由于是負(fù)數(shù),S位為1。E位為236,二進(jìn)制形式為1110 1100,將S、E、F位組合在一起就形成了:

1?1110 1100? 1000? 0101?1000? 1111? 1001?001

?

從左邊最高位開始,4個一組合并成十六進(jìn)制:

1111?0110? 0100? 0010?1100? 0111? 1100?1001

?

換算成十六進(jìn)制為:

0xF??0x6?? 0x4?? 0x2??0xC?? 0x7? 0xC??0x9

所以-987.654e30換算成二進(jìn)制數(shù)為0xF642C7C9。

???????

浮點(diǎn)數(shù)的精度

?

在前面的講解中可以看到1.xx這個數(shù)量級的最小數(shù)是2-23,對應(yīng)的十進(jìn)制數(shù)值為1.00000011920928955078125,可以精確表示到小數(shù)點(diǎn)后23位,但有些C語言書上卻說float型的有效位只有6~7位,這是為什么?

?

這是因?yàn)槎M(jìn)制小數(shù)與十進(jìn)制小數(shù)沒有完全一一對應(yīng)的關(guān)系,二進(jìn)制小數(shù)對于十進(jìn)制小數(shù)來說相當(dāng)于是離散的而不是連續(xù)的,我們來看看下面這些數(shù)字:

二進(jìn)制小數(shù)??????? 十進(jìn)制小數(shù)

2-23???????1.00000011920928955078125

2-22???????1.0000002384185791015625

2-21???????1.000000476837158203125

2-20???????1.00000095367431640625

2-19???????1.0000019073486328125

2-18???????1.000003814697265625

?

不看S位和E位,只看F位,上表列出了1.xx這個數(shù)量級的6個最小冪的二進(jìn)制小數(shù),對應(yīng)的十進(jìn)制在上表的右邊,可以看到使用二進(jìn)制所能表示的最小小數(shù)是1.00000011920928955078125,接下來是1.0000002384185791015625,這兩個數(shù)之間是有間隔的,如果想用二進(jìn)制小數(shù)來表示8位有效數(shù)(只算小數(shù)部分,小數(shù)點(diǎn)前面的1是隱藏的默認(rèn)值)1.00000002、1.00000003、1.00000004...這些數(shù)是無法辦到的,而7位有效數(shù)1.0000001可以用2-23來表示,1.0000002可以用2-22來表示,1.0000003可以用2-23+2-22來表示。從這個角度來看,float型所能精確表示的位數(shù)只有7位,7位之后的數(shù)雖然也是精確表示的,但卻無法表示任意一個想表示的數(shù)值。

?

但還是有一些例外的,比如說7位有效數(shù)1.0000006這個數(shù)就無法使用F位表示,二進(jìn)制小數(shù)對于十進(jìn)制小數(shù)來說相當(dāng)于是離散的,剛好湊不出1.0000006這個數(shù),從這點(diǎn)來看float型所能精確表示的位數(shù)只有6位。至于5位有效值的任何數(shù)都是可以使用F位相加組合出來的,即便是乘以E位的指數(shù)后也是可以準(zhǔn)確表示出來的。

?

因此float型的有效位數(shù)是6~7位,但這個說法應(yīng)該不是非常準(zhǔn)確,準(zhǔn)確來說應(yīng)該是6位,C語言的頭文件中規(guī)定也是6位。

?

對于一個很大的數(shù),比如說1234567890,它是F位乘上E位的系數(shù)被放大了的,但它的有效位仍然是F位所能表示的6位有效數(shù)字。1234567890對應(yīng)的二進(jìn)制數(shù)是0x4E932C06,其中F位的數(shù)值為1.1497809886932373046875,E位的數(shù)值為230=1073741824,1073741824×1.1497809886932373046875=1234567936,對比1234567890,也只有高7位是有效位,后3位是無效的。int型定點(diǎn)數(shù)可以準(zhǔn)確的表示1234567890,而float浮點(diǎn)數(shù)則只能近似的表示1234567890,精度問題決定了float型根本無法取代int型。

???????

浮點(diǎn)數(shù)的比較

?

float型的有效位數(shù)是6位,那么我們在用float型運(yùn)算時就要注意了,來看下面這段程序:

#include <stdio.h>int main(void) {float a = 9.87654321;float b = 9.87654322;if(a > b){printf("a > b\n");}else if(a == b){printf("a == b\n");}else{printf("a < b\n");}return 0; }

?

按照我們平時的經(jīng)驗(yàn)來說這段程序應(yīng)該走a < b的分支,但程序運(yùn)行的結(jié)果卻走了a == b的分支,原因就是float型的精度問題,float型無法區(qū)分出小數(shù)點(diǎn)后的第8位數(shù),在內(nèi)存中,a和b的二進(jìn)制數(shù)都是0x411E0652,因此就走了a == b的分支。

?

某些編譯器在編譯時會發(fā)現(xiàn)a和b的值超出了浮點(diǎn)數(shù)的精度,會產(chǎn)生一個告警,提示數(shù)據(jù)超過精度,但有些編譯器則不會做任何提示。最可怕的是有一類編譯器,調(diào)試窗口里顯示的長度超出float型的精度,比如說a的值顯示為9.87654321,b的值顯示為9.87654322,但在運(yùn)行時,硬件可不管這套,硬件認(rèn)為這2個數(shù)都是0x411E0652,因此實(shí)際運(yùn)行結(jié)果是a == b的分支。

?

由于精度這個問題的限制,我們在浮點(diǎn)數(shù)比較時就需要加一個可接受的精度條件來做判決,比如說上面的這個問題,如果我們認(rèn)為精度在0.00001就足夠了,那么a - b之差的絕對值只要小于0.00001,我們就認(rèn)為a和b的值是相等的,大于0.00001則認(rèn)為不等,還要考慮到a - b正負(fù)等情況,因此可以將上面的程序改寫為:

?

#include <stdio.h>int main(void) {float a = 9.87654321;float b = 9.87654322;if(a - b < -0.00001){printf("a < b\n");}else if(a - b > 0.00001){printf("a > b\n");}else{printf("a == b\n");}return 0; }

?

例子中a和b之差的絕對值小于0.00001,因此認(rèn)為a和b是相等的,運(yùn)行程序,也正確的打印了a == b。

??

為什么我們要做一個精度的限定?這是因?yàn)槲覀冊趹?yīng)用中的精度往往要低于硬件的6位精度。

?

浮點(diǎn)數(shù)的加減

?二進(jìn)制小數(shù)與十進(jìn)制小數(shù)之間不存在一一對應(yīng)的關(guān)系,因此某些十進(jìn)制很整的加減法小數(shù)運(yùn)算由二進(jìn)制小數(shù)來實(shí)現(xiàn)就表現(xiàn)出了不整的情況,來看下面的例子:

?

#include <stdio.h>int main(void) {float a = 10.2;float b = 9;float c;c= a - b;printf("%f\n", c);return 0; }

?

如果用十進(jìn)制計算的話變量c應(yīng)該為1.2,在Visual C++ 2010環(huán)境下實(shí)驗(yàn)輸出為1.200000,但實(shí)際上c變量的值是1.1999998,只不過是在輸出時被四舍五入為1.200000罷了。在內(nèi)存中c變量的二進(jìn)制數(shù)是0x3F999998,它對應(yīng)的浮點(diǎn)數(shù)是0.19999980926513671875。如果我們將printf函數(shù)%f的格式改為%.7f格式,就會看到c變量輸出的值是1.1999998。

?

兩個數(shù)量級相差很大的數(shù)做加減運(yùn)算時,數(shù)值小的浮點(diǎn)數(shù)會受精度限制而被忽略,看下面的例子:

#include <stdio.h>int main(void) {float a = 987654321;float b = 987.654322;float c;c= a + b;printf("%f\n", c);return 0; }
?

Visual C++ 2013上的計算結(jié)果為987655296.000000,而實(shí)際的真實(shí)值為987655308.654322,二進(jìn)制值為0x4E6B79B2對應(yīng)987655296,就是987655296.000000。可以看出有效值是6位,如果按四舍五入的話可以精確到8位,其中變量b貢獻(xiàn)的有效數(shù)值只有2位。

? ? ? 987654321

+????987.654322

--------------------------------

987655308.654322??? 真實(shí)值

987655296.000000??? 計算值

987655296?????????? 二進(jìn)制值,0x4E6B79B2

?

對于這種數(shù)量級相差很大的計算,計算結(jié)果會保證高位數(shù)有效,數(shù)量級小的數(shù)相對計算結(jié)果顯的太小了,不能按自身6位的精度保持,而是需要按照計算結(jié)果的6位精度保持。

?

C語言中有關(guān)浮點(diǎn)數(shù)的定義

C語言對浮點(diǎn)數(shù)做了一些規(guī)定,下面是摘自VisualC++ 2013頭文件float.h中有關(guān)float型的定義,如下:

#define FLT_DIG 6 /* # of decimal digitsof precision */ #define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON!= 1.0 */ #define FLT_GUARD 0 #define FLT_MANT_DIG 24 /* # of bits in mantissa*/ #define FLT_MAX 3.402823466e+38F /* max value */ #define FLT_MAX_10_EXP 38 /* max decimal exponent*/ #define FLT_MAX_EXP 128 /* max binary exponent */ #define FLT_MIN 1.175494351e-38F /* min positive value */ #define FLT_MIN_10_EXP (-37) /* min decimal exponent */ #define FLT_MIN_EXP (-125) /* min binary exponent */

其中FLT_DIG定義了float型的十進(jìn)制精度,是6位,與我們上面的討論是一致的。

FLT_EPSILON定義了float型在1.xx數(shù)量級下的最小精度,1.xx數(shù)量級下判斷浮點(diǎn)數(shù)是否為0可以使用這個精度。

FLT_MANT_DIG定義了float型F位的長度。

FLT_MAX定義了float型可表示的最大數(shù)值。

FLT_MAX_10_EXP定義了float型十進(jìn)制的最大冪。

FLT_MAX_EXP定義了float型二進(jìn)制的最大冪。

FLT_MIN定義了float型所能表示的最小正數(shù)。

FLT_MIN_10_EXP定義了float型十進(jìn)制的最小冪。

FLT_MIN_EXP定義了float型二進(jìn)制的最小冪。

?

float.h文件里對其它的浮點(diǎn)數(shù)也做了規(guī)定,有興趣的讀者可以打開float.h頭文件繼續(xù)研究。

轉(zhuǎn)載于:https://www.cnblogs.com/new0801/p/6177121.html

總結(jié)

以上是生活随笔為你收集整理的C语言的本质(4)——浮点数的本质与运算的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。