日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

c语言中的取模运算符_C语言除法算法和取模运算的实现(多种算法,多种思路)...

發(fā)布時(shí)間:2023/12/10 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言中的取模运算符_C语言除法算法和取模运算的实现(多种算法,多种思路)... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

對(duì)計(jì)算機(jī)來(lái)說(shuō),除法與求模是整數(shù)算術(shù)運(yùn)算中最復(fù)雜的運(yùn)算。相對(duì)其他運(yùn)算(如加法與減法)來(lái)說(shuō),這兩種算法的執(zhí)行速度非常慢。例如,ARM 硬件上不支持除法指令,編譯器調(diào)用 C 庫(kù)函數(shù)來(lái)實(shí)現(xiàn)除法運(yùn)算。直接利用 C 庫(kù)函數(shù)中的標(biāo)準(zhǔn)整數(shù)除法程序要花費(fèi) 20~100 個(gè)周期,消耗較多資源。

在非嵌入式領(lǐng)域,因?yàn)?CPU 運(yùn)算速度快、存儲(chǔ)器容量大,所以執(zhí)行除法運(yùn)算和求模運(yùn)算消耗的這些資源對(duì)計(jì)算機(jī)來(lái)說(shuō)不算什么。但是在嵌入式領(lǐng)域,消耗大量資源帶來(lái)的影響不言而喻。因此,從理論上講,我們應(yīng)該在程序表達(dá)式中盡量減少對(duì)除法運(yùn)算與求模運(yùn)算的使用,盡量使用其他方法來(lái)代替除法與求模運(yùn)算。例如,對(duì)于下面的示例代碼:

if (x/y>z)

{

// ...

}

我們可以將其修改成如下形式:

if (((y>0)&&(x>y*z))||((y<0)&&(x

{

// ...

}

這樣就簡(jiǎn)單地避免了一些除法運(yùn)算。同時(shí),也可以在表達(dá)式中通過(guò)合并除法的方式來(lái)減少除法運(yùn)算,下面通過(guò)示例來(lái)講解。對(duì)于如下代碼:

double x=a/b/c;

double y=a/b+c/b;

根據(jù)數(shù)學(xué)結(jié)合原則,上面的代碼可以通過(guò)合并的方式減少代碼中的除法運(yùn)算,修改后的代碼如下:

double x=a/(b*c);

double y=(a+c)/b;

同樣,對(duì)于求模運(yùn)算,也可以采用相應(yīng)的方法來(lái)代替,如下面的示例代碼:

a=a%8;

可以修改為:

a=a&7;

對(duì)于下面的表達(dá)式:

x=(x+y)%z;

可以通過(guò)如下方式來(lái)避免使用模操作符:

x+=y;

while(x>=z)

{

x-=z;

}

通過(guò)上面的闡述,相信大家對(duì)如何減少使用除法與模運(yùn)算有了初步了解。下面將詳細(xì)討論如何優(yōu)化除法運(yùn)算與求模運(yùn)算。

用倒數(shù)相乘來(lái)實(shí)現(xiàn)除法運(yùn)算

何為倒數(shù)相乘?其實(shí)很簡(jiǎn)單,它的核心思想就是利用乘法來(lái)代替實(shí)現(xiàn)除法運(yùn)算。例如,在 IA-32 處理器中,乘法指令的運(yùn)算速度比除法指令要快 4~6 倍。因此,在某些情況下盡量使用乘法指令來(lái)代替除法指令。

那么,我們?cè)撊绾卫贸朔▉?lái)代替實(shí)現(xiàn)除法運(yùn)算呢?原理就是被除數(shù)乘以除數(shù)的倒數(shù),用公式表現(xiàn)為:

x/y=x*(1/y)

例如,計(jì)算 10/5,可以根據(jù)公式 x/y=x*(1/y) 這樣來(lái)計(jì)算:

10/5=10*(1/5)=10*0.2=2

在實(shí)際應(yīng)用中,一些編譯器也正是基于這個(gè)原理才得以將除法運(yùn)算轉(zhuǎn)換為乘法運(yùn)算的。現(xiàn)在我們來(lái)看一個(gè)除法運(yùn)算示例:

#include

int main(void)

{

int x = 3/2;

float y = 3.0/2.0;

printf("3/2 = %d\r\n3.0/2.0 = %1.1f\n",x,y);

return 0;

}

運(yùn)算結(jié)果為:

3/2 = 1

3.0/2.0 = 1.5

通過(guò)該除法運(yùn)算示例可以看出,很明顯沒(méi)能充分考慮到浮點(diǎn)類(lèi)型。另外,在 C 語(yǔ)言中,一般情況下 1 除以任何數(shù)其結(jié)果皆為 0。那么怎樣才能解決這個(gè)問(wèn)題呢?編譯器采用了一種稱(chēng)為“定點(diǎn)運(yùn)算”(fixed-point arithmetic)的方法。

那么何為定點(diǎn)運(yùn)算,定點(diǎn)運(yùn)算有什么特點(diǎn)呢?

前面已經(jīng)闡述過(guò),由于計(jì)算機(jī)表示實(shí)數(shù)時(shí)為了在固定位數(shù)內(nèi)能表示盡量精確的實(shí)數(shù)值,分配給表示小數(shù)部分的位數(shù)并不是固定的,也就是說(shuō)“小數(shù)點(diǎn)是浮動(dòng)的”,因此計(jì)算機(jī)表示的實(shí)數(shù)數(shù)據(jù)類(lèi)型也稱(chēng)為浮點(diǎn)數(shù)。

相對(duì)于“小數(shù)點(diǎn)是浮動(dòng)的”來(lái)講,定點(diǎn)運(yùn)算根據(jù)字面意思來(lái)理解就是“小數(shù)點(diǎn)是固定的”。有了定點(diǎn)運(yùn)算,表示小數(shù)時(shí)不再用階碼(exponent component,即小數(shù)點(diǎn)在浮點(diǎn)數(shù)據(jù)類(lèi)型中的位置),而是要保持小數(shù)點(diǎn)的位置固定不變。這和硬件浮點(diǎn)數(shù)機(jī)制截然不同,硬件浮點(diǎn)數(shù)機(jī)制是由硬件負(fù)責(zé)向整數(shù)部分和小數(shù)部分分配可用的位數(shù)。有了這種機(jī)制,浮點(diǎn)數(shù)就可以表示很大范圍的數(shù)——從極小的數(shù)(在 0~1 的實(shí)數(shù))到極大的數(shù)(在小數(shù)點(diǎn)前有數(shù)十個(gè) 0)。這種小數(shù)的定點(diǎn)表示法有很多優(yōu)點(diǎn),尤其能極大地提高效率。當(dāng)然,作為代價(jià),同樣也必須承受隨之而來(lái)的精度上的損失。

對(duì)于定點(diǎn)數(shù)表示法(fixed-point),相信大家并不陌生。所謂定點(diǎn)格式,即約定機(jī)器中所有數(shù)據(jù)的小數(shù)點(diǎn)位置是固定不變的。在計(jì)算機(jī)中通常采用兩種簡(jiǎn)單的約定:將小數(shù)點(diǎn)的位置固定在數(shù)據(jù)的最高位之前(即定點(diǎn)小數(shù)),或者固定在最低位之后(即定點(diǎn)整數(shù))。

其中,定點(diǎn)小數(shù)是純小數(shù),約定的小數(shù)點(diǎn)位置在符號(hào)位之后、有效數(shù)值部分的最高位之前。若數(shù)據(jù) x 的形式為 x=x0x1x2…xn(其中 x0 為符號(hào)位,x1,…,xn 是數(shù)值的有效部分,也稱(chēng)為尾數(shù),x1 為最高有效位),則在計(jì)算機(jī)中的表示形式為:

一般說(shuō)來(lái),如果最末位 xn=1,前面各位都為 0,則數(shù)的絕對(duì)值最小,即 |x|min=2-n;如果各位均為 1,則數(shù)的絕對(duì)值最大,即 |x|max=1-2-n。因此定點(diǎn)小數(shù)的表示范圍是:

定點(diǎn)整數(shù)是純整數(shù),約定的小數(shù)點(diǎn)位置在有效數(shù)值部分最低位之后。若數(shù)據(jù) x 的形式為 x=x0x1x2…xn(其中 x0 為符號(hào)位,x1,…,xn 是尾數(shù),xn 為最低有效位),則在計(jì)算機(jī)中的表示形式為:

由此可知,定點(diǎn)整數(shù)的表示范圍是:

當(dāng)數(shù)據(jù)小于定點(diǎn)數(shù)能表示的最小值時(shí),計(jì)算機(jī)將它作 0 處理,稱(chēng)為下溢;當(dāng)數(shù)據(jù)大于定點(diǎn)數(shù)能表示的最大值時(shí),計(jì)算機(jī)將無(wú)法表示,稱(chēng)為上溢,上溢和下溢統(tǒng)稱(chēng)為溢出。

當(dāng)計(jì)算機(jī)采用定點(diǎn)數(shù)表示時(shí),對(duì)于既有整數(shù)又有小數(shù)的原始數(shù)據(jù),需要設(shè)定一個(gè)比例因子,數(shù)據(jù)按該比例縮小成定點(diǎn)小數(shù)或擴(kuò)大成定點(diǎn)整數(shù)再參加運(yùn)算。在運(yùn)算結(jié)果中,根據(jù)比例因子,將數(shù)據(jù)還原成實(shí)際數(shù)值。若比例因子選擇不當(dāng),往往會(huì)使運(yùn)算結(jié)果產(chǎn)生溢出或降低數(shù)據(jù)的有效精度。

使用牛頓迭代法求除數(shù)的倒數(shù)

在上一小節(jié),我們闡述了如何使用倒數(shù)相乘(x/y=x*(1/y))的方法來(lái)實(shí)現(xiàn)除法運(yùn)算。然而,對(duì)于如何能夠快速有效地取倒數(shù),牛頓迭代法(Newton’s method)是最佳方案。

對(duì)于牛頓迭代法,相信學(xué)過(guò)高等數(shù)學(xué)的讀者并不陌生,它又稱(chēng)為牛頓-拉夫遜方法(Newton-Raphson method),是牛頓在 17 世紀(jì)提出的一種在實(shí)數(shù)域和復(fù)數(shù)域上近似求解方程的方法,它將非線(xiàn)性方程線(xiàn)性化,從而得到迭代序列的一種方法。

對(duì)于方程 f(x)=0,設(shè) x0 為它的一個(gè)近似根,則函數(shù) f(x) 在 x0 附近截?cái)喔叽雾?xiàng)可用一階泰勒多項(xiàng)式展開(kāi)為如下形式:

這樣,由式(1)我們可以將 f(x)=0 轉(zhuǎn)化為如下形式:

在這里,我們?cè)O(shè) f′(x)≠0,則有:

取 x 作為原方程新的近似根 x1,再代入方程,如此反復(fù),于是就產(chǎn)生了迭代公式:

有了迭代公式(4)之后,現(xiàn)在我們繼續(xù)來(lái)看如何用牛頓迭代公式來(lái)求倒數(shù),即求除數(shù) a 的倒數(shù) 1/a。

這里我們?cè)O(shè):

式中 x 為 a 的倒數(shù),方程 f(x)=0 為一非線(xiàn)性方程。現(xiàn)在把 f(x)=0 代入牛頓迭代序列式(4)中,就可以得出求倒數(shù)的公式,如下所示:

在式(5)中,xn 為第 n 次迭代的近似根。

如式(5)所示,用牛頓迭代法求倒數(shù),每次迭代需要一次減法與兩次乘法,所用的迭代次數(shù)決定最終的計(jì)算速度和精度。迭代次數(shù)越多,則精度越高。但迭代次數(shù)越多,速度也越慢,因此實(shí)際運(yùn)用時(shí)應(yīng)綜合考慮速度和精度兩方面的因素,選擇合適的迭代次數(shù)。

其實(shí),牛頓迭代法在程序中應(yīng)用得非常廣泛,如最常用的開(kāi)方、開(kāi)方求倒數(shù)等。在 QuakeⅢ 源碼中,在 game/code/q_math.c 文件中就有一個(gè)函數(shù) Q_rsqrt,它的作用是將一個(gè)數(shù)開(kāi)平方后取倒,其運(yùn)行效率也非常高。其函數(shù)實(shí)現(xiàn)為:

float Q_rsqrt(float number)

{

long i;

float x2, y;

const float threehalfs = 1.5F;

x2 = number * 0.5F;

y = number;

i = * (long * ) &y;

i = 0x5f3759df - (i >> 1);

y = * ( float * ) &i;

// 第一次迭代

y = y * ( threehalfs - ( x2 * y * y ));

// 第二迭代

// y = y * ( threehalfs - ( x2 * y * y ) );

return y;

}

從代碼中可以看出,程序首先猜測(cè)出一個(gè)接近 1.0/sqrt(number) 的近似值,然后兩次使用牛頓迭代法進(jìn)行迭代(實(shí)際只需要使用一次)。這里需要特別注意的是 0x5f3759df 這個(gè)值,因?yàn)橥ㄟ^(guò)執(zhí)行語(yǔ)句“0x5f3759df-(i>>1)”,得出的值出人意料地接近 1/sqrt(number) 的值,因此,我們只需要一次迭代就可以求得近似解,或許這就是數(shù)學(xué)的神奇。

用減法運(yùn)算來(lái)實(shí)現(xiàn)整數(shù)除法運(yùn)算

我們知道,減法運(yùn)算比除法運(yùn)算要快得多。因此,對(duì)整數(shù)除法運(yùn)算來(lái)說(shuō),如果知道被除數(shù)是除數(shù)很小的倍數(shù),那么可以使用減法運(yùn)算來(lái)代替除法運(yùn)算。例如,對(duì)于下面的示例代碼:

unsigned int x=300;

unsigned int y=100;

unsigned int z=x/y;

我們可以將“z=x/y”表達(dá)式修改成如下形式:

unsigned int x=300;

unsigned int y=100;

unsigned int z=0;

while (x>=y)

{

x-=y;

++z;

}

這里使用減法來(lái)代替除法運(yùn)算,雖然代碼看起來(lái)不是很直觀,但是在運(yùn)行效率上確實(shí)要快許多。當(dāng)然,具體效率也要取決于被除數(shù)與除數(shù)的倍數(shù)。如果倍數(shù)比較大,那么相應(yīng)的循環(huán)次數(shù)就會(huì)增多,采取這種方法就得不償失了。

用移位運(yùn)算實(shí)現(xiàn)乘除法運(yùn)算

用移位運(yùn)算來(lái)實(shí)現(xiàn)乘除法運(yùn)算的方法,相信大家并不陌生,實(shí)際上有很多 C 編譯器都能夠自動(dòng)地做好這個(gè)優(yōu)化。通常,如果需要乘以或除以 2n,都可以用移位的方法代替。例如:

a=a*2;

b=b/2;

可以修改為如下形式:

a=a<<1;

b=b>>1;

其中,除以 2 等價(jià)于右移 1 位,乘以 2 等價(jià)于左移 1 位。同理,除以 4 等價(jià)于右移 2 位,乘以 4 等價(jià)于左移 2 位;除以 8 等價(jià)于右移 3 位,乘以 8 等價(jià)于左移 3 位,以此類(lèi)推。

其實(shí),利用上面的原理,只要是乘以或除以一個(gè)整數(shù),均可以用移位運(yùn)算的方法來(lái)得到結(jié)果,例如:

a=a*5;

可以將其分解為 a*(4+1),即 a*4+a*1。由此,我們就可以很簡(jiǎn)單地得到下面的程序表達(dá)式:

a=(a<<2)+a

盡量將浮點(diǎn)除法轉(zhuǎn)化為相應(yīng)的整數(shù)除法運(yùn)算

有時(shí)候,如果不能夠在代碼中避免除法運(yùn)算,那么盡量使除數(shù)和被除數(shù)是無(wú)符號(hào)類(lèi)型的整數(shù)。實(shí)際上,有符號(hào)的除法運(yùn)算執(zhí)行起來(lái)比無(wú)符號(hào)的除法運(yùn)算更加慢,因?yàn)橛蟹?hào)的除法運(yùn)算要先取得除數(shù)和被除數(shù)的絕對(duì)值,再調(diào)用無(wú)符號(hào)除法運(yùn)算,最后再確定結(jié)果的符號(hào)。

同時(shí),對(duì)于浮點(diǎn)除法運(yùn)算,可以先將浮點(diǎn)除法運(yùn)算轉(zhuǎn)化為相應(yīng)的整數(shù)除法運(yùn)算,最后對(duì)結(jié)果進(jìn)行相應(yīng)處理。例如,可以將浮點(diǎn)除法運(yùn)算的分子和分母同時(shí)放大相同的倍數(shù),就可以將浮點(diǎn)除法運(yùn)算轉(zhuǎn)換成相同功能的整數(shù)除法運(yùn)算。

總結(jié)

以上是生活随笔為你收集整理的c语言中的取模运算符_C语言除法算法和取模运算的实现(多种算法,多种思路)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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