参考文章
① Introduction to Fixed Point Number Representation
② tas5751m
③ 原码
④ 补码
定点数存储
定点数是以补码方式存储。原码与补码的概念请移步参考文章。
+1的原码为00000001,-1的原码就是10000001。
-1的补码:
1 1111110 // 除符号位,全部取反
1 1111111 // 加1,进位不能进给符号位,例如1 1111111 + 1应为1 0000000,
// 得到的便是最终补码
注意,正数的补码等于原码。
实验
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
int main(int argc, char *argv[])
{
union {
unsigned char uc;
signed char sc;
} num;
num.sc = -128;
printf("%+4d:%02x\n", num.sc, num.uc);
num.sc = +127;
printf("%+4d:%02x\n", num.sc, num.uc);
num.sc = -1;
printf("%+4d:%02x\n", num.sc, num.uc);
num.sc = +1;
printf("%+4d:%02x\n", num.sc, num.uc);
return 0;
}
运行结果
-128:80
+127:7f
-1:ff
+1:01
浮点数存储
符号位 | 指数位 | 小数位 | 指数偏移量 | |
---|---|---|---|---|
单精度浮点数 | 1位[31] | 8位[30 ~ 23] | 23位[22 ~ 00] | 127 |
双精度浮点数 | 1位[63] | 11位[62 ~ 52] | 52位[51 ~ 00] | 1023 |
举例
浮点数式:+27.5
二进制为:11011.1
指数式为:1.10111*2^4
尾数(小数点后的数):10111,补够23位 1011 1000 0000 0000 0000 000
指数:4,加上指数偏移量 127,等于131,二进制1000 0011
组合: (符号数位1位)0 (指数位8位)1000 0011 (尾数位23位)1011 1000 0000 0000 0000 000
即:0100 0001 1101 1100 0000 0000 0000 0000
16进制:41 DC 00 00
实验
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
static bool ls_cpu_little_endian(void)
{
union __TENDIAN{
int a;
char b;
} tend;
tend.a = 1;
if(tend.b == 1) {
return true;
} else {
return false;
}
}
int main(int argc, char *argv[])
{
union {
uint8_t c[4];
float f;
} num = { .f = +27.5 };
printf("sizeof(float) = %d\n", sizeof(float));
if (ls_cpu_little_endian()) {
printf("%02x%02x%02x%02x:%+f\n",
num.c[3], num.c[2], num.c[1], num.c[0], num.f);
} else {
printf("%02x%02x%02x%02x:%+f\n",
num.c[0], num.c[1], num.c[2], num.c[3], num.f);
}
return 0;
}
运行结果
sizeof(float) = 4
41dc0000:+27.500000
其他例子
sizeof(double) = 8
403b800000000000:+27.500000
c03b800000000000:-27.500000
3.23
TAS5751m滤波器参数采用的是3.23格式存储方式。类似的格式还有1.31,9.23等等。
实验
导出滤波器参数
0x00,0x82,0x7b,0x2e
0x07,0x03,0xd7,0x2b
0x00,0x7b,0xda,0xad
0x00,0xfc,0x28,0xd5
0x07,0x81,0xaa,0x25
代码
static double fixed2double(uint32_t fixed, uint32_t format)
{
double d = 0.0;
uint32_t n = 0;
switch (format) {
case 323:
if (fixed & 0x02000000) {
fixed = ~fixed;
fixed = fixed & 0x01ffffff;
fixed = fixed + 1;
d += (double)(fixed) / ( 1 << 23);
d *= (-1);
} else {
d += (double)(fixed) / ( 1 << 23);
}
break;
default:
break;
}
return d;
}
转换成浮点数
> ./Math.exe fixed2float -f 323 -v 00827b2e
323(00827b2e) = +1.019384
> ./Math.exe fixed2float -f 323 -v 0703d72b
323(0703d72b) = -1.969996
> ./Math.exe fixed2float -f 323 -v 007bdaad
323(007bdaad) = +0.967611
> ./Math.exe fixed2float -f 323 -v 00fc28d5
323(00fc28d5) = +1.969996
> ./Math.exe fixed2float -f 323 -v 0781aa25
323(0781aa25) = -0.986995
注意DSP习惯采用的传递函数与Octave等数学工具的a1, a2的符号取反。
[a0, -a1, -a2];
[b0, +b1, +b2];
所以Octave参数应为:
BIQUAD_1_A = [+1.000000, -1.969996, +0.986995];
BIQUAD_1_B = [+1.019384, -1.969996, +0.967611];
代码
% Loader Octave packages
pkg load signal
% Sample Rate
Fs = 48000;
BIQUAD_1_A = [+1.000000, -1.969996, +0.986995];
BIQUAD_1_B = [+1.019384, -1.969996, +0.967611];
[H_BIQUAD_1, w_BIQUAD_1] = freqz(BIQUAD_1_B, BIQUAD_1_A, Fs);
% Transfer from Rad to Hz.
F_BIQUAD_1 = (w_BIQUAD_1 / (2 * pi)) * Fs;
% Transfer from Magnitude to DB
Hf_BIQUAD_1 = mag2db(abs(H_BIQUAD_1));
Hx_BIQUAD_1 = rad2deg(angle(H_BIQUAD_1));
clear figure
clf;
figure(1);
% Plot Magnitude VS Frequency
% Range: 1 ~ Fs / 2 = 1 ~ 24000
% Because Freqz only return half result (1, +pi) rather than (-pi, +pi),
% So divided by 2.
warning ("off", "Octave:negative-data-log-axis");
subplot(1, 2, 1);
semilogx(F_BIQUAD_1(1:Fs, 1), Hf_BIQUAD_1(1:Fs, 1), "linewidth", 3.0);
axis([10, 24000]);
xlabel('Frequency(Hz)');
ylabel('Amplitude(DB)');
subplot(1, 2, 2);
plot(F_BIQUAD_1(1:Fs, 1), Hx_BIQUAD_1(1:Fs, 1), "linewidth", 3.0);
xlabel('Frequency(Hz)');
ylabel('Phase(Degree)');
运行结果
可以看到频响是一致的。