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

清風(fēng)竹林

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

一個(gè)Sqrt函數(shù)引發(fā)的血案(轉(zhuǎn))


注:我(轉(zhuǎn)載本文的人)實(shí)測(cè)結(jié)果是sqrt()函數(shù)要比卡馬克的方法更快一些,測(cè)試環(huán)境xp sp2+ vc6 + stlport

-------------------------------------------------以下是轉(zhuǎn)載原文

好吧,我承認(rèn)我標(biāo)題黨了,不過既然你來了,就認(rèn)真看下去吧,保證你有收獲。

我們平時(shí)經(jīng)常會(huì)有一些數(shù)據(jù)運(yùn)算的操作,需要調(diào)用sqrt,exp,abs等函數(shù),那么時(shí)候你有沒有想過:這個(gè)些函數(shù)系統(tǒng)是如何實(shí)現(xiàn)的?就拿最常用的sqrt函數(shù)來說吧,系統(tǒng)怎么來實(shí)現(xiàn)這個(gè)經(jīng)常調(diào)用的函數(shù)呢?

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

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; 
} 

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

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

求出根號(hào)a的近似值:首先隨便猜一個(gè)近似值x,然后不斷令x等于x和a/x的平均數(shù),迭代個(gè)六七次后x的值就已經(jīng)相當(dāng)精確了。 
例如,我想求根號(hào)2等于多少。假如我猜測(cè)的結(jié)果為4,雖然錯(cuò)的離譜,但你可以看到使用牛頓迭代法后這個(gè)值很快就趨近于根號(hào)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
這種算法的原理很簡(jiǎn)單,我們僅僅是不斷用(x,f(x))的切線來逼近方程x^2-a=0的根。根號(hào)a實(shí)際上就是x^2-a=0的一個(gè)正實(shí)根,這個(gè)函數(shù)的導(dǎo)數(shù)是2x。也就是說,函數(shù)上任一點(diǎn)(x,f(x))處的切線斜率是2x。那么,x-f(x)/(2x)就是一個(gè)比x更接近的近似值。代入 f(x)=x^2-a得到x-(x^2-a)/(2x),也就是(x+a/x)/2。

相關(guān)的代碼如下:

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

然后我們?cè)賮砜聪滦阅軠y(cè)試:

image

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

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;
}

然后我們最后一次來看下性能測(cè)試:

image這次真的是質(zhì)變了,結(jié)果竟然比系統(tǒng)的還要好。。。哥真的是震驚了!!!哥吐血了!!!一個(gè)函數(shù)引發(fā)了血案!!!血案,血案。。。

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

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

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

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

我們知道,越底層的函數(shù),調(diào)用越頻繁。3D引擎歸根到底還是數(shù)學(xué)運(yùn)算。那么找到最底層的數(shù)學(xué)運(yùn)算函數(shù)(在game/code/q_math.c), 必然是精心編寫的。里面有很多有趣的函數(shù),很多都令人驚奇,估計(jì)我們幾年時(shí)間都學(xué)不完。在game/code/q_math.c里發(fā)現(xiàn)了這樣一段代碼。它的作用是將一個(gè)數(shù)開平方并取倒,經(jīng)測(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;
}  

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

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

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

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

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

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

最后Lomont怒了,采用暴力方法一個(gè)數(shù)字一個(gè)數(shù)字試過來,終于找到一個(gè)比卡馬克數(shù)字要好上那么一丁點(diǎn)的數(shù)字,雖然實(shí)際上這兩個(gè)數(shù)字所產(chǎn)生的結(jié)果非常近似,這個(gè)暴力得出的數(shù)字是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>

最后,給出最精簡(jiǎn)的1/sqrt()函數(shù):

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機(jī)、51、AVR、430、ARM、上面編譯并實(shí)驗(yàn),驚訝一下它的工作效率。

前兩天有一則新聞,大意是說 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;
}
他一看之下驚為天人,想要拜見這位前輩高人,但是一路追尋下去卻一直找不到人;同時(shí)間也有其他人在找,雖然也沒找到出處,但是 Chris Lomont 寫了一篇論文 (in PDF) 解析這段 code 的算法 (用的是 Newton’s Method,牛頓法;比較重要的是后半段講到怎么找出神奇的 0x5f3759df 的)。 
PS. 這個(gè) function 之所以重要,是因?yàn)榍?開根號(hào)倒數(shù) 這個(gè)動(dòng)作在 3D 運(yùn)算 (向量運(yùn)算的部份) 里面常常會(huì)用到,如果你用最原始的 sqrt() 然后再倒數(shù)的話,速度比上面的這個(gè)版本大概慢了四倍吧… XD 
PS2. 在他們追尋的過程中,有人提到一份叫做 MIT HACKMEM 的文件,這是 1970 年代的 MIT 強(qiáng)者們做的一些筆記 (hack memo),大部份是 algorithm,有些 code 是 PDP-10 asm 寫的,另外有少數(shù)是 C code (有人整理了一份列表)

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

下載代碼

TestSqrt.cpp (2.58 kb)

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

評(píng)論

# re: 一個(gè)Sqrt函數(shù)引發(fā)的血案(轉(zhuǎn)) 2010-10-21 10:04 kongque

好文。  回復(fù)  更多評(píng)論   

# re: 一個(gè)Sqrt函數(shù)引發(fā)的血案(轉(zhuǎn))[未登錄] 2010-11-15 17:22 megax

如果我沒記錯(cuò)的話,那個(gè)數(shù)字不是卡馬克發(fā)現(xiàn)的,只是被卡馬克用,而出名的。
在很久以前讀quake代碼的時(shí)候,what the fuck讓我忍俊不禁。這個(gè)函數(shù)只是近似,有一些誤差,但是因?yàn)槭怯螒蛏嫌玫模?jì)算兩點(diǎn)間的距離的時(shí)候,那點(diǎn)誤差可以忽略,屏幕本來就是像素的嘛。sqrt的精度要比那個(gè)高,你可以測(cè)測(cè)。
  回復(fù)  更多評(píng)論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美激情乱人伦| 亚洲欧美激情一区二区| 午夜欧美大尺度福利影院在线看 | 另类激情亚洲| 久久精品一区二区三区四区| 久久久久久久久久久久久久一区 | 久久久久久久久一区二区| 久久精品国产2020观看福利| 久久激五月天综合精品| 久久久夜夜夜| 亚洲第一页自拍| 亚洲精品一区二区三区av| 亚洲福利视频网| 亚洲狠狠丁香婷婷综合久久久| 亚洲九九九在线观看| 亚洲欧美国产视频| 老鸭窝91久久精品色噜噜导演| 欧美国产综合视频| 亚洲一区二区影院| 你懂的视频欧美| 国产毛片一区二区| 亚洲精华国产欧美| 欧美一二三视频| 欧美福利在线| 欧美一区二区精品| 欧美丝袜一区二区三区| 在线日韩成人| 欧美自拍偷拍| 一本大道久久精品懂色aⅴ| 久久se精品一区精品二区| 欧美日韩在线精品一区二区三区| 国产亚洲精品久久久| 一区二区三区免费网站| 免费成人高清视频| 亚洲欧美国产不卡| 欧美三级午夜理伦三级中视频| 影音先锋亚洲精品| 欧美中在线观看| 一区二区欧美在线| 欧美激情一区在线| 亚洲高清一区二区三区| 久久精品九九| 亚洲一区二区在线免费观看| 欧美激情第8页| 1024成人网色www| 久久久久久伊人| 午夜精品www| 国产精品一区在线观看你懂的| 99riav久久精品riav| 嫩草伊人久久精品少妇av杨幂| 亚洲少妇一区| 国产精品国产三级国产普通话99| 日韩一级裸体免费视频| 亚洲第一页中文字幕| 美女啪啪无遮挡免费久久网站| 国内揄拍国内精品久久| 久久精品导航| 欧美在线亚洲一区| 国产性天天综合网| 久久久精品一区二区三区| 午夜在线播放视频欧美| 国产乱肥老妇国产一区二| 亚洲免费网站| 亚洲一区精品电影| 国产欧美一区二区精品忘忧草| 亚洲欧洲99久久| 亚洲欧美春色| 国产真实乱偷精品视频免| 久久久精品一品道一区| 老牛嫩草一区二区三区日本| 亚洲精品视频在线播放| 一本色道久久综合亚洲精品婷婷 | 亚洲国产天堂久久综合| 欧美a一区二区| 99精品久久久| 亚洲视频一区二区| 国产一区二区三区四区在线观看| 久久免费午夜影院| 免费国产自线拍一欧美视频| 99精品久久| 午夜精品久久久| 永久免费毛片在线播放不卡| 亚洲成在人线av| 欧美日韩综合不卡| 欧美与欧洲交xxxx免费观看| 久久久综合香蕉尹人综合网| 亚洲精选一区| 午夜精品久久久久久久久| 亚洲电影天堂av| 日韩一区二区精品| 国产一区在线播放| 亚洲国产日韩美| 国产精品综合| 亚洲国产日韩一级| 国产日韩欧美二区| 欧美激情亚洲另类| 国产欧美精品一区二区色综合 | 亚洲三级电影全部在线观看高清| 欧美日韩在线播放一区| 久久人人97超碰国产公开结果 | 国产日产欧美一区| 欧美成人资源| 国产麻豆91精品| 91久久精品国产91久久性色| 国产伦精品一区| 亚洲国产精品第一区二区三区| 国产精品女主播| 亚洲二区视频| 国产一区在线免费观看| 一二三四社区欧美黄| 亚洲国产成人一区| 欧美在线视频二区| 午夜久久久久| 欧美日韩在线播放一区| 亚洲电影天堂av| 在线不卡中文字幕| 香蕉尹人综合在线观看| 亚洲已满18点击进入久久| 快射av在线播放一区| 久久久久天天天天| 国产欧美日韩综合| 亚洲小说欧美另类婷婷| 一区二区三区鲁丝不卡| 欧美老女人xx| 免费日韩av| 一区二区视频在线观看| 欧美日韩在线视频一区二区| 中文国产成人精品久久一| 久久久亚洲国产美女国产盗摄| 午夜久久影院| 国产精品视频区| 亚洲影院免费观看| 亚洲性线免费观看视频成熟| 欧美日韩成人综合在线一区二区| 欧美激情视频一区二区三区免费| 狠狠色丁香婷婷综合| 欧美一区二区三区在线视频| 欧美在线播放一区二区| 国产精品视频99| 亚洲欧美bt| 久久久久**毛片大全| 精品粉嫩aⅴ一区二区三区四区| 欧美一区二区免费| 久久精品系列| 国内成人精品视频| 久久九九全国免费精品观看| 欧美成人激情视频| 91久久黄色| 欧美日本视频在线| 亚洲小视频在线| 老牛影视一区二区三区| 亚洲人成网站999久久久综合| 欧美激情四色| 亚洲一区二区成人| 美女视频网站黄色亚洲| 亚洲免费观看| 国产农村妇女毛片精品久久莱园子| 亚洲欧美日韩精品久久奇米色影视 | 欧美日韩在线三区| 亚洲视频一区在线观看| 久久国产精品久久w女人spa| 一区二区三区我不卡| 欧美精品18videos性欧美| 亚洲午夜视频在线观看| 免费国产一区二区| 亚洲一区二区在线看| 黑人极品videos精品欧美裸| 欧美成年人在线观看| 亚洲视频网站在线观看| 久久亚洲春色中文字幕久久久| 亚洲娇小video精品| 国产精品欧美日韩一区二区| 久久婷婷一区| 亚洲一二三区精品| 欧美激情中文字幕一区二区| 香蕉亚洲视频| 亚洲精品国产精品久久清纯直播 | 欧美精品大片| 欧美亚洲日本一区| 亚洲精品欧美一区二区三区| 欧美中文在线免费| 中文av一区二区| 亚洲高清免费视频| 国产精品一区二区在线观看| 欧美成人69av| 欧美一区日韩一区| 中日韩美女免费视频网站在线观看| 亚洲精品在线三区| av成人免费| 韩国一区二区在线观看| 欧美日韩在线播放| 久久综合国产精品台湾中文娱乐网| 亚洲美女性视频| 久久亚裔精品欧美| 香蕉国产精品偷在线观看不卡| 亚洲精品在线观| 在线观看中文字幕不卡| 国产欧美日韩精品专区| 欧美日韩国产精品专区 | 欧美另类一区|