你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言Q格式的表示方式以及相应的运算

[复制链接]
gaosmile 发布时间:2020-7-2 19:32
1 前言
Q格式是二进制的定点数格式,相对于浮点数,Q格式指定了相应的小数位数和整数位数,在没有浮点运算的平台上,可以更快地对浮点数据进行处理,以及应用在需要恒定分辨率的程序中(浮点数的精度是会变化的);需要注意的是Q格式是概念上小数定点,通过选择常规的二进制数整数位数和小数位数,从而达到所需要的数值范围和精度,这里可能有点抽象,下面继续看介绍。
2 Q数据的表示2.1 范围和精度
定点数通常表示为,其中m为整数个数,n为小数个数,其中最高位位符号位并且以二进制补码的形式存储;
  • 范围: 微信图片_20200702192546.jpg
  • 精度:
    7 }" V; C+ O0 r; r( L
无符号的用 微信图片_20200702192551.jpg 表示;
  • 范围: 微信图片_20200702192555.jpg
  • 精度:

    ) N* b8 G7 ?8 h5 u" X' ~
2.2 推导
无符号Q格式数据的推导
9 O% e3 Q/ d2 r, I3 F1 ]0 E' {这里以一个16位无符号整数为例, 微信图片_20200702192603.jpg 所能表示的最大数据的二进制形式如下图所示;
微信图片_20200702192612.jpg 6 F9 `; c' Z# d" M+ {: i7 O5 z6 Z6 r
所以不难看出, 微信图片_20200702192628.jpg 的范围大小和精度;
0 i% U, p1 _8 r1 u/ s根据等比数列求和公式得到,整数域最大值如下:

, X, h( t6 u' a$ @
微信图片_20200702192636.jpg

  o/ a# O6 ?  x3 V# y! S7 n小数域最大值如下:

6 [& X5 `6 k' n0 B- E
微信图片_20200702192644.png

7 x" y8 |) |& T4 m% A
因此 微信图片_20200702192657.jpg 的范围满足 微信图片_20200702192700.jpg
有符号Q格式数据的推导
+ V% ?. _! t8 K这里以一个16位有符号整数为例, 微信图片_20200702192708.jpg 所能表示的最大数据的二进制形式如下图所示;
微信图片_20200702192712.png
2 g- g5 y% k' H4 J/ N* n; G3 C0 N+ g' P( L0 `! B( {/ `/ N  g! ]7 B
所以不难求出, 微信图片_20200702192723.jpg 的范围大小和精度;! ^6 p& q  u5 Z% N* [! s+ z
根据等比数列求和公式得到,整数域最大值如下:

% Z0 h$ M" i5 v/ X4 T4 w" d8 q
微信图片_20200702192731.png
: U8 ?. L# {. w, D* v
小数域最大值如下:

1 J7 {* l, S, a# C, `$ U* j, K
微信图片_20200702192734.png
. {! o4 D7 q0 A+ [% q$ _0 J
因此最大能表示的数为: 微信图片_20200702192738.png
所能表示的最小数据的二进制形式如下图所示;
微信图片_20200702192743.png
3 Z  q& p% v  |4 F, s1 ~
. i2 ^: t, A' O- A* F可以从图中看到,该数表示为
补充一下:负数在计算机中是补码的形式存在的,补码=反码+1,符号位为1则表示为负数;& E' j4 p( V5 v. _7 j5 n6 C6 f4 u0 \
那么-4该如何表示呢?; I7 s+ S, U" e& b" @7 G
以8 bit数据为例,如下所示;
) N9 D. Z  d5 @原码:0B 0000 100
: k. p# C/ S& J5 b' N/ J" P反码:0B 1111 011
; J$ E1 g, f2 k$ X# r) N4 F补码:0B 1111 100
综上,可以得到有符号的范围是: 微信图片_20200702192746.png
3 Q数据的运算3.1 0x7FFF
最大数的十六进制为0x7FFF,如下图所示;
微信图片_20200702192750.png , t) a  u3 ^" K4 v3 {  O
3.2 0x8000
最小数的十六进制为0X8000,如下图所示;
微信图片_20200702192754.png 9 V! I) M3 w. ?7 I: O/ P" T. ~

2 Y7 a& r( f1 v7 {0 z+ y$ Q! u上述这两种情况,下面都会用到。
1 j% C. Q- [. }- ]/ }; n
3.3 加法
加法和减法需要两个Q格式的数据定标相同,即 微信图片_20200702192757.png 微信图片_20200702192801.png 满足以下条件;5 K5 f4 ?- `( ?: w
微信图片_20200702192804.png
int16_t q_add(int16_t a, int16_t b)
" E% C& l9 T6 N6 W{
5 L2 M9 {1 e: h1 y7 L4 S    return a + b;( y9 T- z  @; ]4 }
}
: [* D8 ^5 X: W) i- l( L
上面的程序其实并不安全,在一般的DSP芯片具有防止溢出的指令,但是通常需要做一下溢出检测,具体如下所示;
//http://great.blog.csdn.net/1 y' s! D) b0 Z; i6 ?# b' I
int16_t q_add_sat(int16_t a, int16_t b)
7 J1 i/ {, a# L  j. Y{3 `: E3 X* O2 F
    int16_t result;
1 F; U* [4 s1 _% T    int32_t tmp;- s" R6 E- f% }- [0 ]6 p

% e. A4 q, X. V, [    tmp = (int32_t)a + (int32_t)b;
  {: w" [7 ^% r, n5 i, j* R) y  y  l    if (tmp > 0x7FFF)& V" Q' Z3 S1 R! S
        tmp = 0x7FFF;
8 {) {! M/ ^; _+ n# _4 M9 s' ~    if (tmp < -1 * 0x8000)
. S  w' n3 c6 ?& \0 U4 u/ D3 I        tmp = -1 * 0x8000;
4 T& ]  P8 l6 O+ s& E" f    result = (int16_t)tmp;
' T5 A4 `: n( T+ g* B8 z
7 z% N% L* c5 i+ C' P+ d$ S    return result;# f4 N1 ~# \/ J: |0 q4 A7 P
}
9 c0 ~; }; z3 W9 P2 k
3.4 减法
类似于加法的操作,需要相同定标的两个Q格式数进行相减,但是不会存在溢出的情况;
//http://great.blog.csdn.net/
9 M( O% A+ q5 `7 O0 u4 ]int16_t q_sub(int16_t a, int16_t b)
( |8 F/ r* E" U& c! ^- p" o& [  Q{
5 ]0 N! M1 _4 d) X9 S    return a - b;6 C% y1 p7 D5 l9 D3 K( M* F
}
- ^0 c0 V2 m% e, a) I' D- L
3.5 乘法
乘法同样需要考虑溢出的问题,这里通过sat16函数,对溢出做了处理;
//http://great.blog.csdn.net/, b5 ]4 j: o% R  s
// precomputed value:, s5 E9 X6 o6 @7 q5 s* L% n# `
#define K   (1 << (Q - 1))
/ a) ?' Z/ H4 D. b9 U) O
2 T0 l: G- |! q& J. t9 N// saturate to range of int16_t& @. _' N2 s: O4 p6 _6 J
int16_t sat16(int32_t x)
$ @# y1 \( I+ U/ m- H6 A{
- w$ Q4 h+ R! b    if (x > 0x7FFF) return 0x7FFF;
. c4 R. N  {9 p    else if (x < -0x8000) return -0x8000;0 X2 \# b6 p! P. ]# k; r5 b! H; e  v
    else return (int16_t)x;
: y. N, q, S3 v  j/ U7 ^* y}7 |9 R, ~& a4 z5 U) H- d( D

; v# q3 C1 g4 |( B# ]' Y$ ^int16_t q_mul(int16_t a, int16_t b)+ F: K4 J& G( w/ U' o; j
{" N+ D5 I$ `9 t2 }7 s
    int16_t result;& |' g+ `: A1 g. J4 ^1 w
    int32_t temp;+ U0 j5 b. C# b1 p2 P

) z; J1 g, @; `8 |  I    temp = (int32_t)a * (int32_t)b; // result type is operand's type
+ m- r% {% {4 w0 W" |* V) o    // Rounding; mid values are rounded up
4 B' P2 W' z& ^0 Q% K    temp += K;
& v$ W# D5 ~) J- @& t: ?8 _    // Correct by dividing by base and saturate result2 h- h+ F7 C1 d9 Y) [
    result = sat16(temp >> Q);
/ I! M8 t! A4 W. F" Q" N8 k1 V
3 x% F; c. X6 ~! I& D* J: ]    return result;5 Z" V. r! c4 l- h% V: a
}

! h  q  u5 p5 S0 j6 a/ ?5 ?# p3.6 除法//http://great.blog.csdn.net/2 k6 j5 k4 j0 V% S+ H
int16_t q_div(int16_t a, int16_t b)3 s9 V% R( ?: e0 Y% ^/ J/ R& _
{  R- A, J5 s$ M/ r( U
    /* pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format) */2 r/ T( _. J. l9 C
    int32_t temp = (int32_t)a << Q;: [, f1 H3 _  o+ T0 d9 h
    /* Rounding: mid values are rounded up (down for negative values). */8 p7 T6 Q1 C' |) b; D
    /* OR compare most significant bits i.e. if (((temp >> 31) & 1) == ((b >> 15) & 1)) */5 }. C9 t+ h5 _. ~* h) Z
    if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0)) {   
* |* T% o) X# R1 z; y8 y' {        temp += b / 2;    /* OR shift 1 bit i.e. temp += (b >> 1); */
% G9 J& G- ?  K; W    } else {, j" Q% |/ x" m* k+ k$ w) ?
        temp -= b / 2;    /* OR shift 1 bit i.e. temp -= (b >> 1); */# ?% x- D5 f7 G8 v: g& E
    }9 \; r# f* \, [; g& s) d
    return (int16_t)(temp / b);
" Z* r7 ?2 ]' J8 y$ Q' }# m# G8 i}
' H& V5 A3 X% i/ e/ T1 C+ m& M
4 常见Q格式的数据范围
定点数和浮点数转换的关系满足以下公式:
微信图片_20200702192807.png
其中为,m表示整数位数,n表示小数位数;
#include <stdio.h>( ^: ?1 T# }; b+ Z5 Q/ M7 W5 ]
#include <stdint.h>
9 d8 ]5 m. i* E4 o6 I. n#include <math.h>
  [* Z2 q: y; l- Z$ G, P
- H; G* j. H$ v7 _* `: r! e* k8 _2 h. U- ^
int main()6 G* }1 y/ }" U" U/ ^
{9 W6 c: H4 k6 P" {$ t6 Y! z/ \6 S# ?
    // 0111 1111 1111 1111
9 @0 h) {8 J, ^4 I! }    int16_t q_max = 32767; // 0x7FFF  x" S. D& \- b  d
    // 1000 0000 0000 0000
! o* ?0 ^1 J' r) K: \+ E    int16_t q_min = -32768; // 0x8000
6 X+ ^$ B& D& k6 t- M    float f_max = 0;
* V9 {  o8 K0 P! }4 P    float f_min = 0;/ O: c3 \2 F8 K* q: a8 r
    printf("\r\n");
4 U1 x# y% |/ P3 |" X; N    for (int8_t i = 15; i>=0; i--) {
- t" W  U- d# O. ~5 C2 J: R9 S( L        f_max = (float)q_max / pow(2,i);
7 R" `* ^, g2 V: \# R. v, e        f_min = (float)q_min / pow(2,i);  _+ S1 E2 @' j" R" s( s- @

0 \0 p& }9 F/ E" ]5 Z( _8 n# Q        printf("\t| Q %d | Q %d.%d| %f | %f |\r\n",
" v/ ]7 [( C# }1 B. U1 B# a' T               i,(15-i),i,f_max,f_min);
2 c9 }# i* e: T    }+ l7 e% }& {5 W2 f  V

; R8 w8 M* `" G1 g; D* e    return 0;
/ k, u- a- n% [. F: x/ r}

# W  d6 c! }# z9 ~$ }9 i
运行得到结果如下所示;
1 h% ]9 [0 N7 `. K2 V; G
微信图片_20200702192811.png
# J5 c! |/ S# k) H4 y' [: v- K0 ?) o& S7 F+ J* h
Q 格式
Qmn
Max
Min

6 H! u7 a! W" R, p! c6 K, }Q 15Q 0.150.999969-1.0000008 P$ @  O* e; b/ k8 g
Q 14Q 1.141.999939-2.000000
- M2 J5 j6 L; x- I) m' Y, |( QQ 13Q 2.133.999878-4.000000! V; w/ I1 I& T3 s
Q 12Q 3.127.999756-8.000000
9 [+ p% ^  u5 K4 f6 CQ 11Q 4.1115.999512-16.000000  F2 D" o. W1 a9 w$ W/ z# Z; l
Q 10Q 5.1031.999023-32.000000
5 ]5 T/ j* \& q2 yQ 9Q 6.963.998047-64.000000* L! Q4 R5 f3 Y/ A8 }( v; Z
Q 8Q 7.8127.996094-128.0000005 G: H* M6 E9 u6 }: A. C
Q 7Q 8.7255.992188-256.0000007 h7 S; P& c6 [6 t; h# o# I: K
Q 6Q 9.6511.984375-512.000000
* i2 U0 B; i, GQ 5Q 10.51023.968750-1024.000000' j$ |9 o6 l7 }5 ^9 G) w2 I
Q 4Q 11.42047.937500-2048.0000000 |( H6 g3 a3 m, i* X' ^$ V
Q 3Q 12.34095.875000-4096.000000
4 |/ q2 H1 m* ?$ SQ 2Q 13.28191.750000-8192.0000005 H- z: [6 O5 c6 d- e
Q 1Q 14.116383.500000-16384.000000
, A7 r1 J; v- A- E, kQ 0Q 15.032767.000000-32768.0000005 0x5f3759df
Q格式虽然十分抽象,但是且看看这个数字0x5f3759df,感觉和Q格式有某种联系,它是雷神之锤3中的一个算法的魔数,毕竟游戏引擎需要充分考虑到效率,具体的由来可以看一下论文《Fast Inverse Square Root》,下面是源码中剥出来的快速平方根算法;
float Q_rsqrt( float number )
8 g, F+ K  G- r8 b7 p{( e/ U" D( C, M
    long i;* T, G' e$ T7 _9 F- z
    float x2, y;
. [3 l* N/ h% I" z3 |5 Z2 B    const float threehalfs = 1.5F;
0 r  X3 D+ U6 C/ z: m! {4 f* g! e; h4 p' u9 n1 n8 T. r
    x2 = number * 0.5F;
9 f; ^! a* j* p8 Q    y   = number;4 K; M& C9 l: j) {' F9 O, I
    i   = * ( long * ) &y;   // evil floating point bit level hacking
! q' F7 M9 Q1 F1 t* p% e    i   = 0x5f3759df - ( i >> 1 ); // what the fuck?
1 `/ ]- ^$ K5 K, Q& e    y   = * ( float * ) &i;7 Q; `" M6 k' f2 Y
    y   = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
) a+ Y! f: g0 c/ ~2 H    // y   = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
* Q4 H, O  O4 V* n/ D
; Z& X& w0 i. i( V0 X    #ifndef Q3_VM4 I) Q: x* N; W7 U3 d! K0 \4 k
    #ifdef __linux__
% ^" W2 _  ^6 r  Y/ c         assert( !isnan(y) ); // bk010122 - FPE?
: e) ^0 O, e7 f5 P    #endif
7 L* R; ~/ a; T  D    #endif
9 \" U% C0 Y, S, N0 N- C+ j    return y;
# n8 C/ b" g" V}  

; X2 Q. `. _1 E+ X6 总结
本文介绍了Q格式的表示方式以及相应的运算,另外需要注意在Q格式运算的时候,两者定标必须相同,对于数据的溢出检测也要做相应的处理。

: z! c% q9 ~  p: Z& u; ^, n
收藏 评论0 发布时间:2020-7-2 19:32

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版