單片機C語言實現求平方根算法

2020-12-19 二進位君

C語言中要求平方根,可以在頭文件中加入#include <math.h>.然後調用sqrt(n);函數即可。但在單片機中調用此函數無疑會耗費大量資源和時間,是極不合適的。在此,總結下網上常見的四種單片機常用開方根算法:

對於擁有專門的乘除法指令的單片機,可採用以下兩種方法:

1、二分法

對於一個非負數n,它的平方根不會小於大於(n/2+1)(謝謝@linzhi-cs提醒)。在[0, n/2+1]這個範圍內可以進行二分搜索,求出n的平方根。

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

1 int sqrt(int x) { 2 long long i = 0; 3 long long j = x / 2 + 1; 4 while (i <= j) 5 { 6 long long mid = (i + j) / 2; 7 long long sq = mid * mid; 8 if (sq == x) return mid; 9 else if (sq < x) i = mid + 1;10 else j = mid - 1;11 }12 return j;13 }

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

2、更為常用的牛頓迭代法

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

1 int sqrt(int x) { 2 if (x == 0) return 0; 3 double last = 0; 4 double res = 1; 5 while (res != last) 6 { 7 last = res; 8 res = (res + x / res) / 2; 9 }10 return int(res);11 }

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

牛頓迭代法也可以求解多次方程。

對於不帶乘除法指令的單片機,可採取以下兩種算法:

算法3:

本算法只採用移位、加減法、判斷和循環實現,因為它不需要浮點運算,也不需要乘除運算,因此可以很方便地運用到各種晶片上去。

我們先來看看10進位下是如何手工計算開方的:

先看下面兩個算式:

x = 10*p + q (1)

公式(1)左右平方之後得:

x^2 = 100*p^2 + 20pq + q^2 (2)

現在假設我們知道x^2和p,希望求出q來,求出了q也就求出了x^2的開方x了。

我們把公式(2)改寫為如下格式:

q = (x^2 - 100*p^2)/(20*p+q) (3)

這個算式左右都有q,因此無法直接計算出q來,因此手工的開方算法和手工除法算法一樣有一步需要猜值。

我們來一個手工計算的例子:計算1234567890的開方

首先我們把這個數兩位兩位一組分開,計算出最高位為3。也就是(3)中的p,最下面一行的334為餘數,也就是公式(3)中的(x^2 - 100*p^2)近似值

3 --------------- | 12 34 56 78 90 9 --------------- | 3 34

下面我們要找到一個0-9的數q使它最接近滿足公式(3)。我們先把p乘以20寫在334左邊:

3 q --------------- | 12 34 56 78 90 9 --------------- 6q| 3 34

我們看到q為5時(60+q*q)的值最接近334,而且不超過334。於是我們得到:

3 5 --------------- | 12 34 56 78 90 9 --------------- 65| 3 34 | 3 25 --------------- 9 56

接下來就是重複上面的步驟了,這裡就不再囉嗦了。

這個手工算法其實和10進位關係不大,因此我們可以很容易的把它改為二進位,改為二進位之後,公式(3)就變成了:

q = (x^2 - 4*p^2)/(4*p+q) (4)

我們來看一個例子,計算100(二進位1100100)的開方:

1 0 1 0 --------------- | 1 10 01 00 1 --------------- 100| 0 10 | 0 00 --------------- | 10 011001| 10 01 --------------- 0 00

這裡每一步不再是把p乘以20了,而是把p乘以4,也就是把p右移兩位,而由於q的值只能為0或者1,所以我們只需要判斷餘數(x^2 - 4*p^2)和(4*p+1)的大小關係,如果餘數大於等於(4*p+q)那麼該上一個1,否則該上一個0。

下面給出完成的C語言程序,其中root表示p,rem表示每步計算之後的餘數,divisor表示(4*p+1),通過a>>30取a的最高 2位,通過a<<=2將計算後的最高2位剔除。其中root的兩次<<1相當於4*p。程序完全是按照手工計算改寫的,應該不難理解。

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

unsigned short sqrt(unsigned long a){ unsigned long rem = 0; unsigned long root = 0; unsigned long divisor = 0; for(int i=0; i<16; i++){ root <<= 1; rem = ((rem << 2) + (a >> 30)); a <<= 2; divisor = (root<<1) + 1; if(divisor <= rem){ rem -= divisor; root++; } } return (unsigned short)(root); }

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

算法4

這種方法比牛頓迭代法更加快速的方法。

1.原理

下述用pow(X,Y)表示X的Y次冪,用B[0],B[1],...,B[m-1]表示一個序列,

其中[x]為下標。

假設:

B[x],b[x]都是二進位序列,取值0或1。

1、 M = B[m-1]*pow(2,m-1) + B[m-2]*pow(2,m-2) + ... + B[1]*pow(2,1) + B[0]*pow

(2,0)

2、 N = b[n-1]*pow(2,n-1) + b[n-2]*pow(2,n-2) + ... + b[1]*pow(2,1) + n[0]*pow

(2,0)

3、 pow(N,2) = M

(1) N的最高位b[n-1]可以根據M的最高位B[m-1]直接求得。

設 m 已知,因為 pow(2, m-1) <= M <= pow(2, m),所以 pow(2, (m-1)/2) <= N <=

pow(2, m/2)

如果 m 是奇數,設m=2*k+1,

那麼 pow(2,k) <= N < pow(2, 1/2+k) < pow(2, k+1),

n-1=k, n=k+1=(m+1)/2

如果 m 是偶數,設m=2k,

那麼 pow(2,k) > N >= pow(2, k-1/2) > pow(2, k-1),

n-1=k-1,n=k=m/2

所以b[n-1]完全由B[m-1]決定。

餘數 M[1] = M - b[n-1]*pow(2, 2*n-2)

(2) N的次高位b[n-2]可以採用試探法來確定。

因為b[n-1]=1,假設b[n-2]=1,則 pow(b[n-1]*pow(2,n-1) + b[n-1]*pow(2,n-2),

2) = b[n-1]*pow(2,2*n-2) + (b[n-1]*pow(2,2*n-2) + b[n-2]*pow(2,2*n-4)),

然後比較餘數M[1]是否大於等於 (pow(2,2)*b[n-1] + b[n-2]) * pow(2,2*n-4)。這種

比較只須根據B[m-1]、B[m-2]、...、B[2*n-4]便可做出判斷,其餘低位不做比較。

若 M[1] >= (pow(2,2)*b[n-1] + b[n-2]) * pow(2,2*n-4), 則假設有效,b[n-2] =

1;

餘數 M[2] = M[1] - pow(pow(2,n-1)*b[n-1] + pow(2,n-2)*b[n-2], 2) = M[1] -

(pow(2,2)+1)*pow(2,2*n-4);

若 M[1] < (pow(2,2)*b[n-1] + b[n-2]) * pow(2,2*n-4), 則假設無效,b[n-2] =

0;餘數 M[2] = M[1]。

(3) 同理,可以從高位到低位逐位求出M的平方根N的各位。

使用這種算法計算32位數的平方根時最多只須比較16次,而且每次比較時不必把M的各位逐

一比較,尤其是開始時比較的位數很少,所以消耗的時間遠低於牛頓迭代法。

2. 實現代碼

這裡給出實現32位無符號整數開方得到16位無符號整數的C語言代碼。

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

/****************************************/ /*Function: 開根號處理 */ /*入口參數:被開方數,長整型 */ /*出口參數:開方結果,整型 */ /****************************************/ unsigned int sqrt_16(unsigned long M) { unsigned int N, i; unsigned long tmp, ttp; // 結果、循環計數 if (M == 0) // 被開方數,開方結果也為0 return 0; N = 0; tmp = (M >> 30); // 獲取最高位:B[m-1] M <<= 2; if (tmp > 1) // 最高位為1 { N ++; // 結果當前位為1,否則為默認的0 tmp -= N; } for (i=15; i>0; i--) // 求剩餘的15位 { N <<= 1; // 左移一位 tmp <<= 2; tmp += (M >> 30); // 假設 ttp = N; ttp = (ttp<<1)+1; M <<= 2; if (tmp >= ttp) // 假設成立 { tmp -= ttp; N ++; } } return N; }

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

以上算法結尾網上收集所得,雖然原理可能比較難懂,但都可在單片機中實際運用。

相關焦點

  • Cortex―M0單片機二-十進位整數轉換的快速算法
    該快速算法的核心內容是通過高效的彙編語言來實現常數除法,無論在程序代碼的運行時間和存儲空間上,都遠勝於sprintf函數。許多單片機應用系統中都需要進行二進位整數轉換為十進位BCD碼的操作,以便實現系統信息的顯示。對於Cortex—M0系列單片機,由於其指令系統中沒有十進位調整指令和除法指令,使得一些文獻中提供的高效算法和技巧不再適用於這類單片機,從而造成上述轉換操作成為影響系統性能的重要因素,因此提高上述數制轉換速度對於提高系統運行效率有極大的促進作用。
  • 【愛找茬】都是C語言,單片機C語言和普通的C語言究竟有什麼差異呢?
    許多小夥伴在學完C語言後想入門單片機,但學著學著發現明明都是C語言,為什麼單片機C語言和我當初學的C語言有差異呢?今天小編就來梳理我們平時所學的C語言與單片機C語言的有什麼樣的不同。
  • 單片機C語言入門教程
    學習一種程式語言,最重要的是建立一個練習環境,邊學邊練才能學好。學習之前請先安裝KEILC51軟體,在學會使用彙編語言後,學習C語言編程是一件比較容易的事,我們將通過一系列的實例介紹C語言編程的方法。圖1-1所示電路圖使用89c51單片機作為主晶片,這種單片機性屬於80C51系列,其內部有8K的FLASH ROM,可以反覆擦寫,非常適於做實驗。
  • 一種實用的單片機多字節除法程序
    在單片機的實際應用中,除法運算是以比較常見的運算。 以MCS-51單片機為例,雖然提供了除法指令,但只能進行單字節的運算。如果要進行多字節的除法運算,就得自己設計算法。目前,許多資料上都介紹了四字節除以二字節的算法,但它們主要有以下幾點不足:1. 只能求出商,不能求出餘數;2. 在被除數高二字節大於除數時,不能進行運算;3. 商只有兩個字節。 例如,被除數是0FFFFFFFFH,除數是0004H時,商數應該是3FFFFFFFH,餘數是0003H。但是,用以前的算法是無法進行運算的。
  • 結合單片機學習板學習c語言之流水燈製作--intrins.h頭文件
    目的:實現流水燈。本文引用地址:http://www.eepw.com.cn/article/201608/294948.htm  源程序:  /*本程序結合STC89C51使用,晶振12M,中間用到串口中斷子程序是利用STC單片機的自定義ISP下載功能,自定義下載命令是FEH,關於自定義下載請參考《用51單片機就用STC51,手把手教你STC51的ISP
  • 單片機C語言程序設計:ADC0809數模轉換與顯示
    打開APP 單片機C語言程序設計:ADC0809數模轉換與顯示 發表於 2018-01-05 15:36:36 本文分享ADC0809數模轉換與顯示的單片機C語言程序設計與電路圖。
  • C語言入門級教程:基礎數據類型與基本算法,學編程從此刻開始!
    今天帶大家了解一下學C語言必備的基本數據類型和基本算法,適合剛學C以及零基礎的小夥伴! 算法的基本特性 算法包含兩方面的內容:算法設計和算法分析 算法設計其實就是針對某一特定類型的問題而設計的一個實現過程。
  • c語言入門教程
    它的應用範圍廣泛,具備很強的數據處理能力,不僅僅是在軟體開發上,而且各類科研都需要用到C語言,適於編寫系統軟體,三維,二維圖形和動畫,具體應用比如單片機以及嵌入式系統開發。這本書被 C語言開發者們稱為"K&R",很多年來被當作 C語言的非正式的標準說明。人們稱這個版本的 C語言為"K&R C"。  c語言宣傳圖1970到80年代,C語言被廣泛應用,從大型主機到小型微機,也衍生了C語言的很多不同版本。
  • JavaScript用Math.sqrt()求平方根
    基本概念Math.sqrt()方法的名字sqrt是"square root"的縮寫,它代表的正是平方根。因此,Math.sqrt()方法的作用就是用來求一個數的平方根。它的語法結構如下所示:Math.sqrt(x);參數x應該是一個數字,即它的類型應該是Number。
  • 學習單片機不可缺少的八大步驟
    在C語言中(極少量的彙編)掌握各種功能的初始化,啟動與停止,實現各種功能函數的編寫與調試。本文引用地址:http://www.eepw.com.cn/article/221348.htm  第一步:數字I/O的應用  在大多數的單片機實驗中,跑馬燈實驗正是數字I/O的典型應用,也是跑馬燈的實驗被安排第一個的原因。
  • 初中數學,求平方根問題不會做,總出錯?一節課幫你搞定
    上節課咱們討論了求算術平方根問題,這節課咱們把求平方根問題搞清楚,這兩類問題是實數部分比較難以理解的知識點,掌握了這兩節課的內容,解決實數部分的其它問題就會簡單多了。先複習一下平方根的定義:若a的平方等於b,則a是b的平方根,即a=±根號b;從定義可以看出,一個正數有兩個平方根,它倆互為相反數。從符號上來說,根號b意思就是b 的算術平方根,±根號b意思就是b的平方根,也可以說,b的算術平方根是b的兩個平方根中的正數的那一個。
  • 單片機C語言開發離不開它——秒懂二進位和十六進位
    單片機C語言開發離不開它--秒懂二進位和十六進位作為單片機愛好者,入門學習單片機編程一定要學會進位的基本概念,常見的有(二進位、八進位、十進位、十六進位),今天於曉超帶大家入門一下單片機C語言編程的二進位和十六進位(技術文章閱讀量慘澹,希望大家能夠點讚收藏加轉發
  • 基於單片機的可測溫式電子萬年曆
    萬年曆主要使用溫度傳感器DS18B20採集溫度信息,與單片機實現雙向通信;時鐘晶片DS1302實現時鐘,準確計時;並通過語音晶片完成整點報時和溫度報警功能。該萬年曆電路結構簡單,具有時間精確、抗幹擾能力強、功耗低、可靠性高等優點。  該可測溫式電子萬年曆主要由STC89C52、時鐘晶片、溫度採集、顯示電路、語音報警等組成。
  • 5分鐘弄懂的五大常用算法之「分治法」,以C語言歸併排序算法為例
    歸併排序法從前面這個簡單的實例可以看出,分治法有時的確能夠極大的提升解決問題的效率,事實上,在C語言程序開發中,許多優秀的算法本質上都是基於分治思想的,一個非常典型的例子就是歸併排序法,它在處理數組排序時有著不錯的效率。
  • 初中數學,理解了平方根和算術平方根,這些提高題做起來很簡單
    一個正數有兩個平方根,並且這兩個平方根互為相反數,也就是說,如果兩個數是一個正數的兩個平方根,則它倆的和等於0;一個正數只有一個算術平方根,並且這個算術平方根是一個正數;這是這節課要講的第一個內容。根據平方根的定義,如果一個數a是正數b的平方根,則可以得到一個等式a=b,其中a有兩個解,並且互為相反數,也可以寫成a=±根號b;根據算術平方根的定義,如果一個數a是正數b的算術平方根,則可以得到一個等式a=b,其中a只有1個解,並且是一個正數,也可以寫成a=根號b;這是這節課要講的第二個內容。下面咱們結合幾道例題來了解這兩點在實際解題中的應用。
  • verilog語言與c語言的區別
    儘管C語言提供了許多低級處理的功能,但仍然保持著良好跨平臺的特性,以一個標準規格寫出的C語言程序可在許多電腦平臺上進行編譯,甚至包含一些嵌入式處理器(單片機或稱MCU)以及超級電腦等作業平臺。二十世紀八十年代,為了避免各開發廠商用的C語言語法產生差異,由美國國家標準局為C語言制定了一套完整的國際標準語法,稱為ANSI C,作為C語言最初的標準。
  • 51單片機C語言編程中對單片機絕對地址訪問的兩種方法
    在進行8051單片機應用系統程序設計時,編程都往往少不了要直接作業系統的各個存儲器地址空間。/*向外部存儲器(XDATA)地址0x8300寫0本人強烈建議用at,這樣可能會更好些,還有一點就是不能亂用,因為有些存儲器空間不能隨便佔用,C51編譯器已經做其它的用了,而且有些空間單片機
  • 基於深聯華單片機的無線智能插座
    2013年前三季度,我國網民數量達6.08億,網際網路普及率45.4%,基於以上分析,以及通用性方面的考慮設計了基於深聯華單片機的無線智能插座。安裝了ADSL寬帶的用戶簡單設置路由器以後就可以將插座接入網際網路,通過Android客戶端就可以實現遠程控制。該插座有四個單獨插座,用戶可以根據需求將需要控制的電器插到插座上。
  • 關於單片機上for循環中運用ACC的隱蔽錯誤
    實現把8位的數據dat一位一位地寫入ds1302的io口。其中ACC0為ACC的第0位。本文引用地址:http://www.eepw.com.cn/article/201611/319881.htm學過c語言的人都知道,這兩個句子都是實現一個8次的循環,功能一模一樣。怎麼會因為這個句子的區別就導致單片機控制的錯誤呢?神奇!
  • FPGA和單片機的串行通信接口設計
    摘要:本文針對由FPGA構成的高速數據採集系統數據處理能力弱的問題,提出FPGA與單片機實現數據串行通信的解決方案。在通信過程中完全遵守RS232協議,具有較強的通用性和推廣價值。