一文教你搞懂C語言的Q格式

2021-01-18 裸機思維

用過DSP的應該都知道Q格式吧;


1 前言

2 Q數據的表示

3 Q數據的運算

3.1 0x7FFF

3.2 0x8000

3.3 加法

3.4 減法

3.5 乘法

3.6 除法

4 常見Q格式的數據範圍

5 0x5f3759df

6 總結


1 前言

Q格式是二進位的定點數格式,相對於浮點數,Q格式指定了相應的小數位數和整數位數,在沒有浮點運算的平臺上,可以更快地對浮點數據進行處理,以及應用在需要恆定解析度的程序中(浮點數的精度是會變化的);
需要注意的是Q格式是概念上小數定點,通過選擇常規的二進位數整數位數和小數位數,從而達到所需要的數值範圍和精度,這裡可能有點抽象,下面繼續看介紹。

2 Q數據的表示2.1 範圍和精度

定點數通常表示為

無符號的用

2.2 推導

無符號Q格式數據的推導這裡以一個16位無符號整數為例,

所以不難看出,

小數域最大值如下:

因此

有符號Q格式數據的推導這裡以一個16位有符號整數為例,

所以不難求出,

小數域最大值如下:

因此

可以從圖中看到,該數表示為

補充一下:負數在計算機中是補碼的形式存在的,補碼=反碼+1,符號位為1則表示為負數;
那麼-4該如何表示呢?
以8 bit數據為例,如下所示;
原碼:0B 0000 100
反碼:0B 1111 011
補碼:0B 1111 100

綜上,可以得到有符號

3 Q數據的運算3.1 0x7FFF

最大數的十六進位為0x7FFF,如下圖所示;

3.2 0x8000

最小數的十六進位為0X8000,如下圖所示;

上述這兩種情況,下面都會用到。

3.3 加法

加法和減法需要兩個Q格式的數據定標相同,即

int16_t q_add(int16_t a, int16_t b)
{
    return a + b;
}

上面的程序其實並不安全,在一般的DSP晶片具有防止溢出的指令,但是通常需要做一下溢出檢測,具體如下所示;

//https://great.blog.csdn.net/
int16_t q_add_sat(int16_t a, int16_t b)
{
    int16_t result;
    int32_t tmp;

    tmp = (int32_t)a + (int32_t)b;
    if (tmp > 0x7FFF)
        tmp = 0x7FFF;
    if (tmp < -1 * 0x8000)
        tmp = -1 * 0x8000;
    result = (int16_t)tmp;

    return result;
}

3.4 減法

類似於加法的操作,需要相同定標的兩個Q格式數進行相減,但是不會存在溢出的情況;

//https://great.blog.csdn.net/
int16_t q_sub(int16_t a, int16_t b)
{
    return a - b;
}

3.5 乘法

乘法同樣需要考慮溢出的問題,這裡通過sat16函數,對溢出做了處理;

//https://great.blog.csdn.net/
// precomputed value:
#define K   (1 << (Q - 1))
 
// saturate to range of int16_t
int16_t sat16(int32_t x)
{
    if (x > 0x7FFF) return 0x7FFF;
    else if (x < -0x8000) return -0x8000;
    else return (int16_t)x;
}

int16_t q_mul(int16_t a, int16_t b)
{
    int16_t result;
    int32_t temp;

    temp = (int32_t)a * (int32_t)b; // result type is operand's type
    // Rounding; mid values are rounded up
    temp += K;
    // Correct by dividing by base and saturate result
    result = sat16(temp >> Q);

    return result;
}

3.6 除法
//https://great.blog.csdn.net/
int16_t q_div(int16_t a, int16_t b)
{
    /* pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format) */
    int32_t temp = (int32_t)a << Q;
    /* Rounding: mid values are rounded up (down for negative values). */
    /* OR compare most significant bits i.e. if (((temp >> 31) & 1) == ((b >> 15) & 1)) */
    if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0)) {   
        temp += b / 2;    /* OR shift 1 bit i.e. temp += (b >> 1); */
    } else {
        temp -= b / 2;    /* OR shift 1 bit i.e. temp -= (b >> 1); */
    }
    return (int16_t)(temp / b);
}

4 常見Q格式的數據範圍

定點數

其中

#include <stdio.h>
#include <stdint.h>
#include <math.h>


int main()
{
    // 0111 1111 1111 1111
    int16_t q_max = 32767; // 0x7FFF
    // 1000 0000 0000 0000
    int16_t q_min = -32768; // 0x8000
    float f_max = 0;
    float f_min = 0;
    printf("\r\n");
    for (int8_t i = 15; i>=0; i--) {
        f_max = (float)q_max / pow(2,i);
        f_min = (float)q_min / pow(2,i);

        printf("\t| Q %d | Q %d.%d| %f | %f |\r\n",
               i,(15-i),i,f_max,f_min);
    }

    return 0;
}

運行得到結果如下所示;

QQmnMaxMinQ 15Q 0.150.999969-1.000000Q 14Q 1.141.999939-2.000000Q 13Q 2.133.999878-4.000000Q 12Q 3.127.999756-8.000000Q 11Q 4.1115.999512-16.000000Q 10Q 5.1031.999023-32.000000Q 9Q 6.963.998047-64.000000Q 8Q 7.8127.996094-128.000000Q 7Q 8.7255.992188-256.000000Q 6Q 9.6511.984375-512.000000Q 5Q 10.51023.968750-1024.000000Q 4Q 11.42047.937500-2048.000000Q 3Q 12.34095.875000-4096.000000Q 2Q 13.28191.750000-8192.000000Q 1Q 14.116383.500000-16384.000000Q 0Q 15.032767.000000-32768.0000005 0x5f3759df

Q格式雖然十分抽象,但是且看看這個數字0x5f3759df,感覺和Q格式有某種聯繫,它是雷神之錘3中的一個算法的魔數,畢竟遊戲引擎需要充分考慮到效率,具體的由來可以看一下論文《Fast Inverse Square Root》,下面是源碼中剝出來的快速平方根算法;

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

6 總結

本文介紹了Q格式的表示方式以及相應的運算,另外需要注意在Q格式運算的時候,兩者定標必須相同,對於數據的溢出檢測也要做相應的處理。

作者能力有限,文中難免有錯誤和紕漏之處,請大佬們不吝賜教創作不易,如果本文幫到了您;請幫忙點個讚 👍👍👍;


長按下圖二維碼關注,獨自前進,走得快;結伴而行,走得遠;在這裡除了肝出來的文章,還有一步一個腳印學習的點點滴滴;





相關焦點

  • 一文教你搞懂C語言的Q格式使用
    用過DSP的應該都知道Q格式吧;1 前言Q格式是二進位的定點數格式,相對於浮點數,Q格式指定了相應的小數位數和整數位數,在沒有浮點運算的平臺上,可以更快地對浮點數據進行處理,以及應用在需要恆定解析度的程序中(浮點數的精度是會變化的);需要注意的是Q格式是概念上小數定點,通過選擇常規的二進位數整數位數和小數位數
  • 如何利用C語言求二元一次方程的解
    今天,小編我來詳細的講解一節C語言的具體運用。我們今天先拿二元一次方程來開刀。大家都知道二元一次方程的解,有三種情況,即無解, 有兩個相同的解,有兩個不同的解。disc,x1,x2,p,q;printf("請輸入變量前的係數\n");scanf("%lf%lf%lf",&a,&b,&c);disc=b*b-4*a*c;
  • 「記」詳解C語言之格式
    ,在它的主體設計完成後,Thompson和Ritchie用它完全重寫了UNIX,且隨著UNIX的發展,c語言也得到了不斷的完善。為了利於C語言的全面推廣,許多專家學者和硬體廠商聯合組成了C語言標準委員會,並在之後的1989年,誕生了第一個完備的C標準,簡稱「C89」,也就是「ANSI c」,截至2020年,最新的C語言標準為2017年發布的 「C17」。
  • 「C語言從入門到入土」必備C語言基礎筆記整理
    一、C語言1、什麼是C語言?C語言是人寫機器看的一種語言。C語言是高級語言中的低級語言。C語言貼近硬體。C語言的入門學習比較簡單。彙編語言——>B語言——>C語言2、C語言的特性首先C語言就是你的女朋友。無論你讓它幹什麼,它絕對不會自己找到方法。
  • 自考「C語言程序設計」模擬試題十四
    一、選擇題(1—14每題1分,15-18每題1.5分,共20分)  1.可選作用戶標識符的一組標識符是(     )  A void    B c5_b8     C For     D 3a  Define
  • 自考「C語言程序設計」模擬試題十一
    一、單項選擇題(共30分,每題1分)  1.在PC機中,『\n』在內存佔用的字節數是(  )  A.1   B.2   C. 3   D.4  2.字符串「ABC」在內存佔用的字節數是(   )  A.3   B.  4
  • 華氏溫度換算公式及C語言轉換程序代碼
    攝氏溫度是這樣規定的:把冰水混合物的溫度定為零度,把沸水的溫度定為一百度,它們之間分成100等份,每一等份是攝氏度的一個單位,叫做1攝氏度。  「攝氏溫標」是經驗溫標之一,亦稱「百分溫標」。溫度符號為t,單位是攝氏度,國際代號是「℃」。攝氏溫標是以在一大氣壓下,純水的冰點定為0℃。在一大氣壓下,汽點作為100℃,兩個標準點之間分為100等分,每等分代表1℃。
  • 單片機C語言實現求平方根算法
    C語言中要求平方根,可以在頭文件中加入#include <math.h>.然後調用sqrt(n);函數即可。但在單片機中調用此函數無疑會耗費大量資源和時間,是極不合適的。我們把公式(2)改寫為如下格式:q = (x^2 - 100*p^2)/(20*p+q) (3)這個算式左右都有q,因此無法直接計算出q來,因此手工的開方算法和手工除法算法一樣有一步需要猜值。我們來一個手工計算的例子:計算1234567890的開方首先我們把這個數兩位兩位一組分開,計算出最高位為3。
  • C語言+EasyX庫:漢諾塔移動動畫
    漢諾塔(又稱河內塔)是一款源於印度一個古老傳說的益智類遊戲。傳說上帝創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按大小順序摞著7片黃金圓盤。上帝命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。遊戲裡有三根金剛石柱子,在一根柱子上從下往上按大小順序摞著7片黃金圓盤。
  • 自考「C語言程序設計」模擬試題九
    一、選擇題  1.一個C語言程序是由(      )構成。  A.語句          B.行號            C.數據           D.函數  2.下面標識符中正確的是(       )。
  • 二級c語言考試改革_計算機二級考試c語言考試題型 - CSDN
    傳送門如下:http://www.sdzs.gov.cn/zsks/2018/1211/26ef3e3ac20a491696d12537c624d573.shtm(山東省的,其他省的可以百度**省教育招生考試院)一、公告查詢操作流程:1、進入官網首頁
  • 最全的C語言基礎知識都在這了
    C語言是一門面向過程、抽象化的通用程序設計語言,廣泛應用於底層開發。我們用一個簡單的c程序例子,介紹c語言的基本構成、格式、以及良好的書寫風格,加深小夥伴們對C語言的認識。/ sum=a+b; /*把兩個數之和賦值給整型變量sum*/printf(「a=%d,b=%d,sum=%d\n」,a,b,sum); /*把計算結果輸出到顯示屏上*/ }重點說明:1、任何一個c語言程序都必須包括以下格式
  • 計算機二級考試C語言高頻考點
    一,C語言概述C語言基礎知識1.C語言的構成(1)源程序由函數構成,每個函數完成相對獨立的功能(2)每個源程序中必須有且只能有一個主函數可以放在任何位置(2)源程序的擴展名為.c,目標程序的擴展名為.obi,可執行程序的擴展名為.exe。
  • 快速上手系列-C語言之數組
    我們需要表示某個班級學生的年齡,比如,張三今年3歲,李四今年80歲,很顯然,我們可以用C語言中的某個數據類型來表示,比如int型:int age_zhangsan = 3; int age_lisi = 80; 那如果這個班級有50個同學,我們是不是需要定義50個變量來存放每個同學的年齡呢,當然可以,但是這麼做真的很不合理。
  • C Primer Plus怎樣高效學?C語言大神案例值得借鑑!
    C語言是面向過程的語言C語言原則上函數和模塊之間可以胡亂調用,如果框架思想不強,寫出來的代碼就是一鍋粥,非常難維護。很多初學者用C語言實現一個功能,基本上都放在一個文件或者函數裡面,亂鬨鬨的代碼搞在一起,如果再用指針實現功能,代碼簡直沒法看,當然這是初學者的通病。C語言要怎麼學?
  • 懂專業|APA論文格式解析
    APA格式APA格式是一個為廣泛接受的研究論文撰寫格式,特別針對社會科學領域的研究,規範學術文獻的引用和參考文獻的撰寫方法,以及表格、圖表、註腳和附錄的編排方式。其規範格式主要包括文內文獻引用和文後參考文獻列舉兩大部分。
  • 一文教你 「量子編程」入門式
    本書為普通非專業讀者而寫,庫爾圖瓦用人人能懂的語言帶領我們重溫繪製宇宙地圖的過程。本書糅雜了宇宙科學知識與許多生動有趣的故事,可謂一部極簡宇宙史。讀者不僅踏上了一場奇妙的星際旅行,還得以窺見庫爾圖瓦作為天文學家的學術人生。
  • 深入理解C語言
    導讀:Dennis Ritchie過世了,他發明了C語言,一個影響深遠並徹底改變世界的計算機語言。一門經歷40多年的到今天還長盛不訓的語言,今天很多語言都受到C的影響,C++,Java,C#,Perl,PHP,Javascript等等。但是,你對C了解嗎?相信你看過本站的《C語言的謎題》還有《誰說C語言很簡單?》。
  • r語言卡方檢驗算法_r語言符號檢驗算法 - CSDN
    在平爐上進行的一項試驗以確定改變操作方法的建議是否會增加剛的得率,試驗時在同一個平爐上進行的,每煉一爐剛時除操作方法外,其它條件都儘可能做到相同,先用標準方法煉一爐,然後用新方法煉一爐,以後交替進行,各煉了10爐,其得率分別為                           標準方法    78.1 72.4 76.2 74.3 77.4 78.4 76.0 75.5
  • 一文教你看懂植物人和腦死亡的區別
    原標題:一文教你看懂植物人和腦死亡的區別   在日常生活中,我們都聽說過「昏迷」、「植物人」這樣的說法,那麼昏迷的人就是植物人嗎?植物人和腦死亡是一回事嗎?   其實,老百姓口中的「植物人」只是昏迷狀態的一種,與腦死亡是兩個不同的概念,前者意味著「生」,後者意味著「死」。