国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 操作系統(tǒng) > 正文

你應(yīng)該知道的浮點(diǎn)數(shù)基礎(chǔ)知識(shí)

2024-06-28 13:21:13
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
你應(yīng)該知道的浮點(diǎn)數(shù)基礎(chǔ)知識(shí)

本文從一個(gè)有趣而又令人意外的實(shí)驗(yàn)展開(kāi),介紹一些關(guān)于浮點(diǎn)數(shù)你應(yīng)該知道的基礎(chǔ)知識(shí)

本博客已經(jīng)遷移至:

http://cenalulu.github.io/

為了更好的體驗(yàn),請(qǐng)通過(guò)此鏈接閱讀:

http://cenalulu.github.io/linux/about-denormalized-float-number/

文章歡迎轉(zhuǎn)載,但轉(zhuǎn)載時(shí)請(qǐng)保留本段文字,并置于文章的頂部作者:盧鈞軼(cenalulu)本文原文地址:

一個(gè)有趣的實(shí)驗(yàn)

本文從一個(gè)有趣而詭異的實(shí)驗(yàn)開(kāi)始。最早這個(gè)例子博主是從 Stackoverflow上的一個(gè)問(wèn)題中看到的。為了提高可讀性,博主這里做了改寫(xiě),簡(jiǎn)化成了以下兩段代碼:

#include <iostream>#include <string>using namespace std;int main() {    const float x=1.1;    const float z=1.123;    float y=x;    for(int j=0;j<90000000;j++)    {        y*=x;        y/=z;        y+=0.1f;        y-=0.1f;    }    return 0;}
#include <iostream>#include <string>using namespace std;int main() {    const float x=1.1;    const float z=1.123;    float y=x;    for(int j=0;j<90000000;j++)    {        y*=x;        y/=z;        y+=0;        y-=0;    }    return 0;}

上面兩段代碼的唯一差別就是第一段代碼中y+=0.1f,而第二段代碼中是y+=0。由于y會(huì)先加后減同樣一個(gè)數(shù)值,照理說(shuō)這兩段代碼的作用和效率應(yīng)該是完全一樣的,當(dāng)然也是沒(méi)有任何邏輯意義的。假設(shè)現(xiàn)在我告訴你:其中一段代碼的效率要比另一段慢7倍。想必讀者會(huì)認(rèn)為一定是y+=0.1f的那段慢,畢竟它和y+=0相比看上去要多一些運(yùn)算。但是,實(shí)驗(yàn)結(jié)果,卻出乎意料, y+=0的那段代碼比y+=0.1f足足慢了7倍。{: style="color: red" } 。世界觀被顛覆了有木有?博主是在自己的Macbook PRo上進(jìn)行的測(cè)試,有興趣的讀者也可以在自己的筆記本上試試。(只要是支持SSE2指令集的CPU都會(huì)有相似的結(jié)果)。

shell> g++ code1.c -o test1shell> g++ code2.c -o test2shell> time ./test1real    0m1.490suser    0m1.483ssys     0m0.003sshell>?time ./test2real    0m9.895suser    0m9.871ssys     0m0.009s

當(dāng)然 原文中的投票最高的回答解釋的非常好,但博主第一次看的時(shí)候是一頭霧水,因?yàn)榇蟛糠只A(chǔ)知識(shí)已經(jīng)還給大學(xué)老師了。所以,本著知其然還要知其所以然的態(tài)度,博主做了一個(gè)詳盡的分析和思路整理過(guò)程。也希望讀者能夠從0開(kāi)始解釋這個(gè)詭異現(xiàn)象的原因。

復(fù)習(xí)浮點(diǎn)數(shù)的二進(jìn)制轉(zhuǎn)換

現(xiàn)在讓我們復(fù)習(xí)大學(xué)計(jì)算機(jī)基礎(chǔ)課程。如果你熟練掌握了浮點(diǎn)數(shù)向二進(jìn)制表達(dá)式轉(zhuǎn)換的方法,那么你可以跳過(guò)這節(jié)。我們先來(lái)看下浮點(diǎn)數(shù)二進(jìn)制表達(dá)的三個(gè)組成部分。

float_exponent

三個(gè)主要成分是:

  • Sign(1bit):表示浮點(diǎn)數(shù)是正數(shù)還是負(fù)數(shù)。0表示正數(shù),1表示負(fù)數(shù)
  • Exponent(8bits):指數(shù)部分。類(lèi)似于科學(xué)技術(shù)法中的M*10^N中的N,只不過(guò)這里是以2為底數(shù)而不是10。需要注意的是,這部分中是以2^7-1127,也即01111111代表2^0,轉(zhuǎn)換時(shí)需要根據(jù)127作偏移調(diào)整。
  • Mantissa(23bits):基數(shù)部分。浮點(diǎn)數(shù)具體數(shù)值的實(shí)際表示。

下面我們來(lái)看個(gè)實(shí)際例子來(lái)解釋下轉(zhuǎn)換過(guò)程。Step 1 改寫(xiě)整數(shù)部分以數(shù)值5.2為例。先不考慮指數(shù)部分,我們先單純的將十進(jìn)制數(shù)改寫(xiě)成二進(jìn)制。整數(shù)部分很簡(jiǎn)單,5.101.

Step 2 改寫(xiě)小數(shù)部分小數(shù)部分我們相當(dāng)于拆成是2^-1一直到2^-N的和。例如:0.2 = 0.125+0.0625+0.007825+0.003906252^-3+2^-4+2^-7+2^-8....,也即.00110011001100110011

Step 3 規(guī)格化現(xiàn)在我們已經(jīng)有了這么一串二進(jìn)制101.00110011001100110011。然后我們要將它規(guī)格化,也叫Normalize。其實(shí)原理很簡(jiǎn)單就是保證小數(shù)點(diǎn)前只有一個(gè)bit。于是我們就得到了以下表示:1.0100110011001100110011 * 2^2。到此為止我們已經(jīng)把改寫(xiě)工作完成,接下來(lái)就是要把bit填充到三個(gè)組成部分中去了。

Step 4 填充指數(shù)部分(Exponent):之前說(shuō)過(guò)需要以127作為偏移量調(diào)整。因此2的2次方,指數(shù)部分偏移成2+127即129,表示成10000001填入。整數(shù)部分(Mantissa):除了簡(jiǎn)單的填入外,需要特別解釋的地方是1.010011中的整數(shù)部分1在填充時(shí)被舍去了。因?yàn)橐?guī)格化后的數(shù)值整部部分總是為1。那大家可能有疑問(wèn)了,省略整數(shù)部分后豈不是1.0100110.010011就混淆了么?其實(shí)并不會(huì),如果你仔細(xì)看下后者:會(huì)發(fā)現(xiàn)他并不是一個(gè)規(guī)格化的二進(jìn)制,可以改寫(xiě)成1.0011 * 2^-2。所以省略小數(shù)點(diǎn)前的一個(gè)bit不會(huì)造成任何兩個(gè)浮點(diǎn)數(shù)的混淆。具體填充后的結(jié)果見(jiàn)下圖float_exponent

練習(xí):如果想考驗(yàn)自己是否充分理解這節(jié)內(nèi)容的話,可以隨便寫(xiě)一個(gè)浮點(diǎn)數(shù)嘗試轉(zhuǎn)換。通過(guò) 浮點(diǎn)二進(jìn)制轉(zhuǎn)換工具可以驗(yàn)證答案。

什么是Denormalized Number

了解完浮點(diǎn)數(shù)的表達(dá)以后,不難看出浮點(diǎn)數(shù)的精度和指數(shù)范圍有很大關(guān)系。最低不能低過(guò)2^-7-1最高不能高過(guò)2^8-1(其中剔除了指數(shù)部分全0喝全1的特殊情況)。那么當(dāng)我們要表示一個(gè)例如:1.00001111*2^-7這樣的超小數(shù)值的時(shí)候就無(wú)法用規(guī)格化數(shù)值表示,只能用0來(lái)代替。那么,這樣做有什么問(wèn)題呢?最容易理解的一種副作用就是:當(dāng)多次做低精度浮點(diǎn)數(shù)舍棄的時(shí)候,就會(huì)出現(xiàn)除數(shù)為0的exception,導(dǎo)致異常。

于是乎就出現(xiàn)了Denormalized Number(后稱(chēng)非規(guī)格化浮點(diǎn))。他和規(guī)格浮點(diǎn)的區(qū)別在于,規(guī)格浮點(diǎn)約定小數(shù)點(diǎn)前一位默認(rèn)是1。而非規(guī)格浮點(diǎn)約定小數(shù)點(diǎn)前一位可以為0,這樣小數(shù)精度就相當(dāng)于多了最多2^22范圍。

但是,精度的提升是有代價(jià)的。由于CPU硬件只支持,或者默認(rèn)對(duì)一個(gè)32bit的二進(jìn)制使用規(guī)格化解碼。因此需要支持32bit非規(guī)格數(shù)值的轉(zhuǎn)碼和計(jì)算的話,需要額外的編碼標(biāo)識(shí),也就是需要額外的硬件或者軟件層面的支持。以下是wiki上的兩端摘抄,說(shuō)明了非規(guī)格化計(jì)算的效率非常低。> 一般來(lái)說(shuō),由軟件對(duì)非規(guī)格化浮點(diǎn)數(shù)進(jìn)行處理將帶來(lái)極大的性能損失,而由硬件處理的情況會(huì)稍好一些,但在多數(shù)現(xiàn)代處理器上這樣的操作仍是緩慢的。極端情況下,規(guī)格化浮點(diǎn)數(shù)操作可能比硬件支持的非規(guī)格化浮點(diǎn)數(shù)操作快100倍。

For example when using NVIDIA's CUDA platform, on gaming cards, calculations with double precision take 3 to 24 times longer to complete than calculations using single precision.

如果要解釋為什么有如此大的性能損耗,那就要需要涉及電路設(shè)計(jì)了,超出了博主的知識(shí)范圍。當(dāng)然萬(wàn)能的wiki也是有答案的,有興趣的讀者可以自行查閱。

回到實(shí)驗(yàn)

總上面的分析中我們得出了以下結(jié)論:

  • 浮點(diǎn)數(shù)表示范圍有限,精度受限于指數(shù)和底數(shù)部分的長(zhǎng)度,超過(guò)精度的小數(shù)部分將會(huì)被舍棄(underflow)
  • 為了表示更高精度的浮點(diǎn)數(shù),出現(xiàn)了非規(guī)格化浮點(diǎn)數(shù),但是他的計(jì)算成本非常高。

于是我們就可以發(fā)現(xiàn)通過(guò)幾十上百次的循環(huán)后,y中存放的數(shù)值無(wú)限接近于零。CPU將他表示為精度更高的非規(guī)格化浮點(diǎn)。而當(dāng)y+0.1f時(shí)為了保留跟重要的底數(shù)部分,之后無(wú)限接近0(也即y之前存的數(shù)值)被舍棄,當(dāng)y-0.1f后,y又退化為了規(guī)格化浮點(diǎn)數(shù)。并且之后的每次y*xy/z時(shí),CPU都執(zhí)行的是規(guī)劃化浮點(diǎn)運(yùn)算。而當(dāng)y+0,由于加上0值后的y仍然可以被表示為非規(guī)格化浮點(diǎn),因此整個(gè)循環(huán)的四次運(yùn)算中CPU都會(huì)使用非規(guī)格浮點(diǎn)計(jì)算,效率就大大降低了。

其他

當(dāng)然,也有在程序內(nèi)部也是有辦法控制非規(guī)范化浮點(diǎn)的使用的。在相關(guān)程序的上下文中加上fesetenv(FE_DFL_DISABLE_SSE_DENORMS_ENV);就可以迫使CPU放棄使用非規(guī)范化浮點(diǎn)計(jì)算,提高性能。我們用這種辦法修改上面實(shí)驗(yàn)中的代碼后,y+=0的效率就和y+=0.1f就一樣了。甚至還比y+=0.1f更快了些,世界觀又端正了不是么:) 修改后的代碼如下

#include <iostream>#include <string>#include <fenv.h>using namespace std;int main() {    fesetenv(FE_DFL_DISABLE_SSE_DENORMS_ENV);    const float x=1.1;    const float z=1.123;    float y=x;    for(int j=0;j<90000000;j++)    {        y*=x;        y/=z;        y+=0;        y-=0;    }    return 0;}
Reference

什么是非規(guī)格化浮點(diǎn)數(shù)Why does changing 0.1f to 0 slow down performance by 10x?IEEE floating pointFloating pointDenormal number


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 丰县| 襄垣县| 枣庄市| 石台县| 德保县| 金寨县| 曲阜市| 贵港市| 永清县| 淮安市| 阜南县| 武汉市| 泰来县| 宁安市| 北辰区| 富蕴县| 饶阳县| 崇明县| 黄浦区| 开化县| 大名县| 德保县| 尼木县| 唐河县| 潼南县| 临汾市| 湖南省| 云龙县| 无锡市| 泊头市| 徐水县| 张家川| 昭苏县| 京山县| 台南县| 越西县| 丰原市| 昌江| 阳曲县| 通榆县| 蒙城县|