青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

清風竹林

ぷ雪飄絳梅映殘紅
   ぷ花舞霜飛映蒼松
     ----- Do more,suffer less

一個Sqrt函數引發的血案(轉)


注:我(轉載本文的人)實測結果是sqrt()函數要比卡馬克的方法更快一些,測試環境xp sp2+ vc6 + stlport

-------------------------------------------------以下是轉載原文

好吧,我承認我標題黨了,不過既然你來了,就認真看下去吧,保證你有收獲。

我們平時經常會有一些數據運算的操作,需要調用sqrt,exp,abs等函數,那么時候你有沒有想過:這個些函數系統是如何實現的?就拿最常用的sqrt函數來說吧,系統怎么來實現這個經常調用的函數呢?

雖然有可能你平時沒有想過這個問題,不過正所謂是“臨陣磨槍,不快也光”,你“眉頭一皺,計上心來”,這個不是太簡單了嘛,用二分的方法,在一個區間中,每次拿中間數的平方來試驗,如果大了,就再試左區間的中間數;如果小了,就再拿右區間的中間數來試。比如求sqrt(16)的結果,你先試(0+16)/2=8,8*8=64,64比16大,然后就向左移,試(0+8)/2=4,4*4=16剛好,你得到了正確的結果sqrt(16)=4。然后你三下五除二就把程序寫出來了:

float SqrtByBisection(float n) //用二分法 
{ 
	if(n<0) //小于0的按照你需要的處理 
		return n; 
	float mid,last; 
	float low,up; 
	low=0,up=n; 
	mid=(low+up)/2; 
	do
	{
		if(mid*mid>n)
			up=mid; 
		else 
			low=mid;
		last=mid;
		mid=(up+low)/2; 
	}while(abs(mid-last) > eps);//精度控制
	return mid; 
} 

然后看看和系統函數性能和精度的差別(其中時間單位不是秒也不是毫秒,而是CPU Tick,不管單位是什么,統一了就有可比性) 
二分法性能對比

從圖中可以看出,二分法和系統的方法結果上完全相同,但是性能上整整差了幾百倍。為什么會有這么大的區別呢?難道系統有什么更好的辦法?難道。。。。哦,對了,回憶下我們曾經的高數課,曾經老師教過我們“牛頓迭代法快速尋找平方根”,或者這種方法可以幫助我們,具體步驟如下:

求出根號a的近似值:首先隨便猜一個近似值x,然后不斷令x等于x和a/x的平均數,迭代個六七次后x的值就已經相當精確了。 
例如,我想求根號2等于多少。假如我猜測的結果為4,雖然錯的離譜,但你可以看到使用牛頓迭代法后這個值很快就趨近于根號2了: 
(       4  + 2/4        ) / 2 = 2.25 
(     2.25 + 2/2.25     ) / 2 = 1.56944.. 
( 1.56944..+ 2/1.56944..) / 2 = 1.42189.. 
( 1.42189..+ 2/1.42189..) / 2 = 1.41423.. 
....sqrt
這種算法的原理很簡單,我們僅僅是不斷用(x,f(x))的切線來逼近方程x^2-a=0的根。根號a實際上就是x^2-a=0的一個正實根,這個函數的導數是2x。也就是說,函數上任一點(x,f(x))處的切線斜率是2x。那么,x-f(x)/(2x)就是一個比x更接近的近似值。代入 f(x)=x^2-a得到x-(x^2-a)/(2x),也就是(x+a/x)/2。

相關的代碼如下:

float SqrtByNewton(float x)
{
	float val = x;//最終
	float last;//保存上一個計算的值
	do
	{
		last = val;
		val =(val + x/val) / 2;
	}while(abs(val-last) > eps);
	return val;
}

然后我們再來看下性能測試:

image

哇塞,性能提高了很多,可是和系統函數相比,還是有這么大差距,這是為什么呀?想啊想啊,想了很久仍然百思不得其解。突然有一天,我在網上看到一個神奇的方法,于是就有了今天的這篇文章,廢話不多說,看代碼先:

float InvSqrt(float x)
{
	float xhalf = 0.5f*x;
	int i = *(int*)&x; // get bits for floating VALUE 
	i = 0x5f375a86- (i>>1); // gives initial guess y0
	x = *(float*)&i; // convert bits BACK to float
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy

	return 1/x;
}

然后我們最后一次來看下性能測試:

image這次真的是質變了,結果竟然比系統的還要好。。。哥真的是震驚了!!!哥吐血了!!!一個函數引發了血案!!!血案,血案。。。

到現在你是不是還不明白那個“鬼函數”,到底為什么速度那么快嗎?不急,先看看下面的故事吧:

Quake-III Arena (雷神之錘3)是90年代的經典游戲之一。該系列的游戲不但畫面和內容不錯,而且即使計算機配置低,也能極其流暢地運行。這要歸功于它3D引擎的開發者約翰-卡馬克(John Carmack)。事實上早在90年代初DOS時代,只要能在PC上搞個小動畫都能讓人驚嘆一番的時候,John Carmack就推出了石破天驚的Castle Wolfstein, 然后再接再勵,doom, doomII, Quake...每次都把3-D技術推到極致。他的3D引擎代碼資極度高效,幾乎是在壓榨PC機的每條運算指令。當初MS的Direct3D也得聽取他的意見,修改了不少API。

最近,QUAKE的開發商ID SOFTWARE 遵守GPL協議,公開了QUAKE-III的原代碼,讓世人有幸目睹Carmack傳奇的3D引擎的原碼。這是QUAKE-III原代碼的下載地址: 
http://www.fileshack.com/file.x?fid=7547

(下面是官方的下載網址,搜索 “quake3-1.32b-source.zip” 可以找到一大堆中文網頁的。ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip)

我們知道,越底層的函數,調用越頻繁。3D引擎歸根到底還是數學運算。那么找到最底層的數學運算函數(在game/code/q_math.c), 必然是精心編寫的。里面有很多有趣的函數,很多都令人驚奇,估計我們幾年時間都學不完。在game/code/q_math.c里發現了這樣一段代碼。它的作用是將一個數開平方并取倒,經測試這段代碼比(float)(1.0/sqrt(x))快4倍:

float Q_rsqrt( float number )
{
	long i;
	float x2, y;
	const float threehalfs = 1.5F;

	x2 = number * 0.5F;
	y   = number;
	i   = * ( long * ) &y;   // evil floating point bit level hacking
	i   = 0x5f3759df - ( i >> 1 ); // what the fuck?
	y   = * ( float * ) &i;
	y   = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
	// y   = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed

	#ifndef Q3_VM
	#ifdef __linux__
		 assert( !isnan(y) ); // bk010122 - FPE?
	#endif
	#endif
	return y;
}  

函數返回1/sqrt(x),這個函數在圖像處理中比sqrt(x)更有用。 
注意到這個函數只用了一次疊代!(其實就是根本沒用疊代,直接運算)。編譯,實驗,這個函數不僅工作的很好,而且比標準的sqrt()函數快4倍!要知道,編譯器自帶的函數,可是經過嚴格仔細的匯編優化的啊! 
這個簡潔的函數,最核心,也是最讓人費解的,就是標注了“what the fuck?”的一句 
      i = 0x5f3759df - ( i >> 1 );

再加上y  = y * ( threehalfs - ( x2 * y * y ) ); 
兩句話就完成了開方運算!而且注意到,核心那句是定點移位運算,速度極快!特別在很多沒有乘法指令的RISC結構CPU上,這樣做是極其高效的。

算法的原理其實不復雜,就是牛頓迭代法,用x-f(x)/f'(x)來不斷的逼近f(x)=a的根。

沒錯,一般的求平方根都是這么循環迭代算的但是卡馬克(quake3作者)真正牛B的地方是他選擇了一個神秘的常數0x5f3759df 來計算那個猜測值,就是我們加注釋的那一行,那一行算出的值非常接近1/sqrt(n),這樣我們只需要2次牛頓迭代就可以達到我們所需要的精度。好吧如果這個還不算NB,接著看:

普渡大學的數學家Chris Lomont看了以后覺得有趣,決定要研究一下卡馬克弄出來的這個猜測值有什么奧秘。Lomont也是個牛人,在精心研究之后從理論上也推導出一個最佳猜測值,和卡馬克的數字非常接近, 0x5f37642f。卡馬克真牛,他是外星人嗎?

傳奇并沒有在這里結束。Lomont計算出結果以后非常滿意,于是拿自己計算出的起始值和卡馬克的神秘數字做比賽,看看誰的數字能夠更快更精確的求得平方根。結果是卡馬克贏了... 誰也不知道卡馬克是怎么找到這個數字的。

最后Lomont怒了,采用暴力方法一個數字一個數字試過來,終于找到一個比卡馬克數字要好上那么一丁點的數字,雖然實際上這兩個數字所產生的結果非常近似,這個暴力得出的數字是0x5f375a86。

Lomont為此寫下一篇論文,"Fast Inverse Square Root"。 論文下載地址: 
http://www.math.purdue.edu/~clomont/Math/Papers/2003/InvSqrt.pdf 
http://www.matrix67.com/data/InvSqrt.pdf

參考:<IEEE Standard 754 for Binary Floating-Point Arithmetic><FAST INVERSE SQUARE ROOT>

最后,給出最精簡的1/sqrt()函數:

float InvSqrt(float x)
{
	float xhalf = 0.5f*x;
	int i = *(int*)&x; // get bits for floating VALUE 
	i = 0x5f375a86- (i>>1); // gives initial guess y0
	x = *(float*)&i; // convert bits BACK to float
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
	return x;
}  

大家可以嘗試在PC機、51、AVR、430、ARM、上面編譯并實驗,驚訝一下它的工作效率。

前兩天有一則新聞,大意是說 Ryszard Sommefeldt 很久以前看到這么樣的一段 code (可能出自 Quake III 的 source code):

float InvSqrt (float x) 
{
	float xhalf = 0.5f*x;
	int i = *(int*)&x;
	i = 0x5f3759df - (i>>1);
	x = *(float*)&i;
	x = x*(1.5f - xhalf*x*x);
	return x;
}
他一看之下驚為天人,想要拜見這位前輩高人,但是一路追尋下去卻一直找不到人;同時間也有其他人在找,雖然也沒找到出處,但是 Chris Lomont 寫了一篇論文 (in PDF) 解析這段 code 的算法 (用的是 Newton’s Method,牛頓法;比較重要的是后半段講到怎么找出神奇的 0x5f3759df 的)。 
PS. 這個 function 之所以重要,是因為求 開根號倒數 這個動作在 3D 運算 (向量運算的部份) 里面常常會用到,如果你用最原始的 sqrt() 然后再倒數的話,速度比上面的這個版本大概慢了四倍吧… XD 
PS2. 在他們追尋的過程中,有人提到一份叫做 MIT HACKMEM 的文件,這是 1970 年代的 MIT 強者們做的一些筆記 (hack memo),大部份是 algorithm,有些 code 是 PDP-10 asm 寫的,另外有少數是 C code (有人整理了一份列表)

好了,故事就到這里結束了,希望大家能有有收獲:)

下載代碼

TestSqrt.cpp (2.58 kb)

posted on 2010-10-20 16:18 李現民 閱讀(2015) 評論(2)  編輯 收藏 引用 所屬分類: 絕對盜版

評論

# re: 一個Sqrt函數引發的血案(轉) 2010-10-21 10:04 kongque

好文。  回復  更多評論   

# re: 一個Sqrt函數引發的血案(轉)[未登錄] 2010-11-15 17:22 megax

如果我沒記錯的話,那個數字不是卡馬克發現的,只是被卡馬克用,而出名的。
在很久以前讀quake代碼的時候,what the fuck讓我忍俊不禁。這個函數只是近似,有一些誤差,但是因為是游戲上用的,計算兩點間的距離的時候,那點誤差可以忽略,屏幕本來就是像素的嘛。sqrt的精度要比那個高,你可以測測。
  回復  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品视频一区二区三区| 99riav久久精品riav| 国产婷婷97碰碰久久人人蜜臀| 欧美日韩中文| 国产精品高精视频免费| 国产精品视频免费一区| 国产真实久久| 亚洲经典自拍| 亚洲五月婷婷| 久久久久国产精品www| 蜜桃久久av一区| 日韩视频在线一区二区| 亚洲男人第一av网站| 久久久精品国产一区二区三区| 你懂的国产精品永久在线| 欧美日韩成人一区二区| 国产视频精品网| 亚洲精品社区| 欧美在线观看网址综合| 亚洲成色777777女色窝| 99热在这里有精品免费| 久久久久国内| 欧美日韩中文字幕在线视频| 国产一区二区观看| 在线亚洲+欧美+日本专区| 久久久久国色av免费看影院| 亚洲高清激情| 欧美在线日韩| 国产精品伦理| 亚洲美女精品久久| 久久久亚洲国产天美传媒修理工 | 亚洲一区二区三区四区视频| 久久精品一区二区三区不卡牛牛| 欧美日本高清| 欧美激情一区二区三区在线视频| 久久资源在线| 亚洲欧洲精品一区二区三区波多野1战4 | 欧美一区深夜视频| 欧美黑人国产人伦爽爽爽| 亚洲网站在线| 欧美另类亚洲| 亚洲国产成人久久综合一区| 欧美亚洲一区二区三区| 99香蕉国产精品偷在线观看| 欧美成人综合一区| 在线观看欧美| 免费亚洲网站| 久久国产欧美日韩精品| 国产伦精品一区二区三区免费 | 国产精品美女xx| 中文在线不卡| 99pao成人国产永久免费视频| 另类av导航| 亚洲国产成人91精品| 久久精品亚洲精品国产欧美kt∨| 亚洲午夜免费视频| 欧美天天在线| 亚洲一级免费视频| 在线午夜精品| 国产精品免费看片| 午夜精品久久久久久久99热浪潮 | 麻豆国产精品777777在线| 国产亚洲精品资源在线26u| 欧美一区二区三区久久精品茉莉花| 一区二区三区久久网| 欧美午夜国产| 欧美一区日韩一区| 午夜精品一区二区在线观看 | 亚洲综合国产| 亚洲无线一线二线三线区别av| 国产精品av久久久久久麻豆网| 亚洲男人av电影| 亚洲欧美日韩精品久久久久| 国产一区二区久久精品| 久久影视精品| 噜噜噜91成人网| 亚洲最快最全在线视频| 国产精品99久久久久久www| 国产精品视频导航| 老牛影视一区二区三区| 欧美福利在线| 香蕉成人伊视频在线观看 | 欧美日本韩国一区二区三区| 欧美二区不卡| 欧美精品1区2区| 日韩网站在线| 亚洲一区二区不卡免费| 国产一区二区三区视频在线观看| 久久综合精品一区| 美国成人毛片| 亚洲午夜激情| 久久精品一区中文字幕| 一级成人国产| 久久精品在线| 亚洲自拍都市欧美小说| 久久亚洲私人国产精品va媚药| 亚洲小说欧美另类婷婷| 欧美亚洲一区三区| 日韩视频―中文字幕| 亚洲欧美国产精品桃花| 在线日韩欧美视频| 一区二区激情视频| 精品av久久707| 亚洲视频在线观看三级| 亚洲国产第一页| 亚洲一区www| 91久久极品少妇xxxxⅹ软件| 亚洲一线二线三线久久久| 亚洲高清资源综合久久精品| 亚洲午夜精品一区二区| 亚洲精品中文字幕有码专区| 亚洲欧美在线磁力| 一区二区三区久久精品| 久久久久久自在自线| 午夜精品www| 欧美久久久久久久久| 久久久久久一区| 国产精品人人爽人人做我的可爱 | 久久精品一本久久99精品| 亚洲一区二区三区视频播放| 美女国产一区| 久久嫩草精品久久久精品| 欧美视频日韩视频| 亚洲国产精品va在线看黑人| 国产一区二区三区免费观看| 一区二区欧美日韩| 一片黄亚洲嫩模| 欧美国产日韩a欧美在线观看| 欧美77777| 亚洲第一精品夜夜躁人人爽| 久久久久久久久蜜桃| 久久精品国产2020观看福利| 国产精品国产三级欧美二区| 亚洲免费观看高清在线观看 | 久久久噜噜噜久久| 久久精品国产999大香线蕉| 国产精品国产a级| 一区二区国产日产| 亚洲天堂黄色| 国产精品v欧美精品∨日韩| 一区二区高清在线| 午夜精品视频一区| 国产精品久久久久毛片软件 | 亚洲韩日在线| 亚洲国产精品999| 久久人体大胆视频| 美女国产精品| 亚洲高清自拍| 欧美精品一区二区三区在线看午夜| 免费亚洲一区二区| 亚洲美女网站| 欧美日韩小视频| 亚洲影视在线| 久久综合伊人77777| 伊人一区二区三区久久精品| 久久亚洲免费| 99re6这里只有精品| 亚洲在线中文字幕| 国产亚洲欧美一区二区三区| 久久精品免视看| 亚洲第一黄色| 亚洲视频网在线直播| 国产欧美日韩一区| 久久精品99无色码中文字幕| 欧美成人情趣视频| 国产精品99久久不卡二区| 国产欧美日韩精品一区 | 欧美天天综合网| 欧美一区激情| 亚洲国产成人在线播放| 亚洲一区欧美激情| 精品999网站| 国产精品初高中精品久久| 欧美一区二区在线免费播放| 亚洲国产二区| 久久久久欧美精品| 一区二区不卡在线视频 午夜欧美不卡'| 国产精品乱码一区二三区小蝌蚪| 久久九九免费视频| 一区二区三区精品| 欧美不卡在线视频| 亚洲欧美国产高清va在线播| 亚洲人成网在线播放| 国产欧美日韩综合| 欧美日韩18| 久久久.com| 一区二区三区欧美成人| 久久综合精品国产一区二区三区| 亚洲午夜激情免费视频| 国产一区三区三区| 欧美午夜电影网| 欧美精品免费播放| 欧美在线首页| 亚洲一区精品在线| 亚洲狼人精品一区二区三区| 久久精品免视看| 午夜精品久久久久久久99樱桃 | 亚洲综合激情| 亚洲精品亚洲人成人网| 久久一日本道色综合久久|