一.计算机中有符号整数的取值范围
计算机中基本数据类型共有4类8种,分别为整数、浮点数、字符型、布尔型。这次只谈论整型
有符号整数在计算机中分别占有一个字节(8位),2个字节(16位),四个字节(32位),8个字节(64位)
整数类型有:byte short int long
一个字节(byte)的有符号整数的取值范围
范围是 -128至127.
根据补码的几条规定即可推出上述结论:
1 若二进制每位全为0,则表示数0
2 若最高位(即符号位)为0,表示正数
3 若最高位为1, 表示是负数,而该负数的绝对值是多少呢?将每个二进制位(包括符号位)取反加1,得到一个二进制数,将该数看成无符号数,其值就是上述负数的绝对值。
例如,二进制的 10000000 的最高位为1, 所以它表示的是负数。是负的多少呢?我们将其八位全部取反,得到01111111, 然后加1,得到10000000. 将该数看作无符号数,值为128, 故计算机中的10000000表示的是-128
最高位(即符号位)为1的8位有符号数有128个,故可表示128个负数;最高位为0的8位有符号数有128个,但全0的那个表示数0,所以总共只能表示127个正整数。
两个字节(short)为16位
四个字节(int)32位
八个字节(long)64位
二.原码和补码之间的相互转化
补码是一种计算机中用于表示有符号整数的方式,它允许使用相同的二进制加法操作来处理有符号数的加法和减法,简化了计算机的设计。
补码转换为原码(从补码得到实际数值):
如果补码的最高位(符号位)是0,那么它表示的是一个正数,原码就是补码本身。
如果补码的最高位是1,那么它表示的是一个负数,原码可以通过以下步骤得到:
先对补码除符号位外的其他位进行按位取反(位反转)。
然后给反转后的数加1。
原码转换为补码(将实际数值转换为补码以便在计算机中表示):
如果原码表示的是一个正数,补码就是原码本身。
如果原码表示的是一个负数,补码可以通过以下步骤得到:
先对原码的绝对值部分进行按位取反。
然后给反转后的数加1。
举例
假设我们有一个8位的系统,我们要转换 -5 的原码为补码。
-5 的原码(考虑到符号位)是 1000 0101。
取绝对值 5 的二进制表示(不包括符号位):0000 0101。
按位取反(除符号位外):1111 1010。
加1得到补码:1111 1011。
因此,-5 在8位系统中的补码是 1111 1011。
反过来,如果我们要将补码 1111 1011 转换为原码:
补码 1111 1011 的最高位是1,因此它是负数的补码。
除去最高位,取反得到:0000 0100。
加1得到绝对值:0000 0101。
添加符号位得到原码:1000 0101。
这样我们就得到了 -5 的原码 1000 0101。
三.负数左移和右移的运算规则
负数左移和右移的运算规则
负数在计算机中的表示通常使用补码形式。补码是一位符号位跟随原码的位表示,对于负数而言,最高位(最左边的位)是1。在位运算中,无论是左移还是右移,都需要考虑到这一点。12
负数右移的规则:
当负数进行右移操作时,为了保持其负数的特性,需要在二进制表示的右侧补上1。这样,即使经过多次右移,仍然能够保持负数的状态。如果一直右移,最终会变成-1,即所有位都为1的情况。
负数左移的规则:
当负数进行左移操作时,与正整数左移相同,右侧补0。在左移过程中,负数的符号位(最左边的位)保持不变,即始终为1。如果一直左移,最终会变成0,因为所有位都移出并补0。
总结:
负数右移时,右侧补1,以保持其负数的状态。多次右移后,结果为-1。
负数左移时,右侧补0。多次左移后,结果为0。
四.STM32中DSP的负数移位运算
typedef signed char int8_t;
typedef int8_t q7_t;
q7_t pSrcA3 = 0x86;
q7_t pDst3;
arm_shift_q7(&pSrcA3, 3, &pDst3, 1);
printf("arm_shift_q7 = %2x\r\n", pDst3);
输出结果:arm_shift_q7 = ffffff80
输出结果:arm_shift_q7 = -128
为什么输出结果为:ffffff80
这是因为pSrcA3类型为有符号整型范围为-128-127,而pSrcA3 = 0x86=134,134已经超过了127,所以这个值为负数,由于在单片机中负数是以补码的形式存在的,所以0x86即为负数的补码,0x86=0b1000 0110将此补码转换为原码,去掉最高位取反即为0b0111 1001,再加上1为0111 1010,添加符号位即为0b1111 1010,即为负数-122。
arm_shift_q7(&pSrcA3, 3, &pDst3, 1);函数是将pSrcA3数据左移三位,由于0x86=0b1000 0110,左移三位后即为0b0011 0000,由于负数的最小值为-128,饱和运算后即为-128。
typedef signed char int8_t;
typedef int8_t q7_t;
q7_t pSrcA3 = 0xF6;
q7_t pDst3;
arm_shift_q7(&pSrcA3, 3, &pDst3, 1);
printf("arm_shift_q7 = %2x\r\n", pDst3);
输出结果:arm_shift_q7 = ffffffb0
输出结果:arm_shift_q7 = -80
为什么输出结果为:ffffffb0
这是因为pSrcA3类型为有符号整型范围为-128-127,而pSrcA3 =0xF6=0b1111 0110,由于最高位为1,所以这个数为负数,由于在单片机中负数是以补码的形式存在的,所以0xF6即为负数的补码,将补码转换为原码,去掉最高位取反即为0b0000 1001,再加上1为0b0000 1010,添加符号位0b1000 1010,即为负数-10。
arm_shift_q7(&pSrcA3, 3, &pDst3, 1);函数是将pSrcA3数据左移三位,由于0xF6=0b1111 0110,左移三位后即为0b1011 0000,将补码转换为原码,去掉最高位取反即为0b0100 1111,再加上1为0101 0000,添加符号位即为1101 0000,即为负数-80。
typedef signed short int int16_t;
typedef int16_t q15_t;
q15_t pSrcA2 = 0x8866;
q15_t pDst2;
arm_shift_q15(&pSrcA2, -3, &pDst2, 1);
printf("arm_shift_q15 = %4x\r\n", pDst2);
输出结果:arm_shift_q15 = fffff10c
为什么会输出fffff10c
这是因为pSrcA2类型为有符号整型范围为-32,768-32767,而pSrcA2=0x8866=34918,已经超过了32767,所以这个数为负数,0x8866即为这个数的补码,0x8866=0b1000 1000 0110 0110,最高位为1,所以这个数为负数,由于在单片机中负数是以补码的形式存在的,所以0x8866即为负数的补码,将补码转换为原码,去掉最高位取反即为0b0111 0111 1001 1001,再加上1为0b0111 0111 1001 1010,添加符号位,即为负数-30618。
arm_shift_q15(&pSrcA2, -3, &pDst2, 1);函数是将pSrcA2数据右移三位(因为为-3,有负号即代表右移),由于0x8866=0b1000 1000 0110 0110,右移三位,由于负数右移时,右侧补1,以保持其负数的状态,所以右移后0b111 1 0001 0000 1100=0xF10C。
五.STM32中DSP比例因子的计算规则
主要用于实现数据的比例放大和缩小,浮点数据公式描述如下:
pDst[n] = pSrc[n] * scale, 0 <= n < blockSize.
如果是 Q31, Q15, Q7 格式的数据,公式描述如下:
pDst[n] = (pSrc[n] * scaleFract) << shift, 0 <= n < blockSize.
typedef signed short int int16_t;
typedef int16_t q15_t;
q15_t pSrcA2[5] = {0x6fff,1,1,1,1};
q15_t scale2 = 0x6fff;
q15_t pDst2[5];
scale2 += 1;
arm_scale_q15(pSrcA2, scale2, 0, pDst2, 5);
printf("arm_scale_q15 = %x\r\n", pDst2[0]);
printf("arm_scale_q15 = %x\r\n", pDst2[1]);
输出结果:arm_scale_q15 = 61ff
输出结果:arm_scale_q15 = 0
为什么是这样的输出结果了?
由于定点数的比例因子计算公式为:pDst[n] = (pSrc[n] * scaleFract) << shift,所以在这里先计算scaleFract的值
scale2=scale2+1=0x6fff+1=0x7000
在第一组数据中pSrcA2[0]=0x6fff,(0x6fff * 0x7000)<<0.
转换为定点数的乘法运算,先转换为浮点数的计算,(0x6fff * 0x7000)/(0x8000 * 0x8000)=(2867128672)/(3276832768)=822054912/1073741824=0.765598297119140625。
再将计算之后的浮点数换算为定点数:0.765598297119140625 * 32768=25087.125=0x61FF。
在第二组数据中pSrcA2[1]=1,(1* 0x7000)<<0.
转换为定点数的乘法运算,先转换为浮点数的计算,(1 * 0x7000)/(0x8000 * 0x8000)=(128672)/(3276832768)=28672/1073741824=0.000026702880859375
由于q15数据类型最小的浮点数精度为:1/32768=0.000030517578125,而0.000026702880859375 < 0.000030517578125,即小于最小的精度,故近似于0。0*32768=0,所以结果为0。
typedef signed char int8_t;
typedef int8_t q7_t;
q7_t pSrcA3[5] = {0x70,1,1,1,1};
q7_t scale3 = 0x6f;
q7_t pDst3[5];
scale3 += 1;
arm_scale_q7(pSrcA3, scale3, 0, pDst3, 5);
printf("arm_scale_q7 = %x\r\n", pDst3[0]);
输出结果:arm_scale_q7 = 62
为什么会输出这样的结果?
由于定点数的比例因子计算公式为:pDst[n] = (pSrc[n] * scaleFract) << shift,所以在这里先计算scaleFract的值
scale3=scale3+1=0x6f+1=0x70
在第一组数据中:(0x70 * 0x70) << 0
转换为定点数的乘法运算,先转换为浮点数的计算,(0x70 * 0x70)/(0x80 * 0x80)=12544/16384=0.765625,再将计算之后的浮点数换算为定点数:0.765625 * 128=98=0x62。
六.STM32中DSP的平方根(Sqrt)计算规则
arm_sqrt_q31(1000, &pOut1);
printf("arm_sqrt_q31 = %d\r\n", pOut1);
输出结果:arm_sqrt_q31 = 1465429
为什么输出的结果为1465429。
这里in的输入范围是0x00000000 到 0x7FFFFFFF,转化成浮点数范围就是[0 +1),这是因为将0x7FFFFFFF转换为浮点数即为0x7FFFFFFF/0x8000 0000=2147483647/2147483648=0.9999999995343387126922607421875,四舍五入之后即为1。
arm_sqrt_q31(1000, &pOut1),定点数为1000,将此定点数转换为浮点数,1000/2147483648=0.0000004656612873077392578125。利用计算器对0.0000004656612873077392578125求平方根之后结果为6.82393791961605742312369894022e-4。之后再将6.82393791961605742312369894022e-4转换为定点数6.82393791961605742312369894022e-4 * 2147483648=1465429.5097342621754387160555397。所以输出的结果为1465429。
arm_sqrt_q15(1000, &pOut);
printf("arm_sqrt_q15 = %d\r\n", pOut);
输出结果:arm_sqrt_q15 = 5724
为什么输出的结果为5724
参数范围 0x0000 到 0x7FFF( 转化成浮点数范围就是[0 +1))。
arm_sqrt_q15(1000, &pOut);定点数为1000,将此定点数转换为浮点数,1000/32768=0.030517578125,利用计算器对0.030517578125求平方根之后结果为0.17469281074217107003196669286963,之后再将0.17469281074217107003196669286963转换为定点数0.17469281074217107003196669286963 * 32768=5724.334022399461622807484591952,所以输出结果为5724。
七.STM32中DSP的标准偏差计算规则
标准偏差计算公式
Result = sqrt((sumOfSquares – sum^2 / blockSize) / (blockSize - 1))
其中:
sumOfSquares = pSrc[0] * pSrc[0] + pSrc[1] * pSrc[1] + … + pSrc[blockSize-1] *
pSrc[blockSize-1]
sum = pSrc[0] + pSrc[1] + pSrc[2] + … + pSrc[blockSize-1]
typedef float float32_t;
float32_t pSrc3[2] = {0.6557f, 0.0357f};
float32_t pResult3;
arm_std_f32(pSrc3, 2, &pResult3);
printf("arm_std_f32 : pResult3 = %f\r\n", pResult3);
输出结果:arm_std_f32 : pResult3 = 0.438406
为什么结果为:0.438406
sumOfSquares=0.6557f * 0.6557f + 0.0357f * 0.0357f = 0.42994249 + 0.00127449 = 0.43121698
sum=0.6557f + 0.0357f = 0.6914
sum^2=0.6914 ^ 2 = 0.47803396
sum^2 / 2 = 0.47803396 / 2 = 0.23901698
sqrt((sumOfSquares – sum^2 / blockSize) / (blockSize - 1)) =sqrt ( (0.43121698 - 0.23901698) / (2-1) ) = sqrt (0.1922 / 1) = 0.43840620433565946512852350450501。
标准差的数据计算方式规则
例如上面的一组数据0.6557f, 0.0357f,一共是两个数据
(1).先求出这两个数据的平均值:(0.6557f + 0.0357f) / 2 = 0.3457
(2).根据计算出的平均值,求出这两个数据分别和平均值的差值的平方,将平方求和:
(0.6557 - 0.3457) * (0.6557 - 0.3457) + (0.0357 - 0.3457) * (0.0357 - 0.3457) = 0.0961 + 0.0961 = 0.1922
(3).由于一共是两个数据:0.1922 / (2 - 1) = 0.1922,这样计算出来的即为方差。
(4).由于标准差的平方即为方差,所以将方差开根号即为标准差:0.1922开根号即为0.4384。
八.STM32中DSP的均方根计算规则
Result = sqrt(((pSrc[0] * pSrc[0] + pSrc[1] * pSrc[1] + … + pSrc[blockSize-1] *
pSrc[blockSize-1]) / blockSize));
typedef float float32_t;
float32_t pSrc3[2] = {0.7060f, 0.0318f};
float32_t pResult3;
arm_rms_f32(pSrc3, 2, &pResult3);
printf("arm_rms_f32 : pResult3 = %f\r\n", pResult3);
输出结果:arm_rms_f32 : pResult3 = 0.499724
0.7060 * 0.7060 + 0.0318 * 0.0318 = 0.498436 + 0.00101124=0.49944724
0.49944724 / 2 = 0.24972362
sqrt(0.24972362) = 0.49972354357184333085643235839603,所以输出结果为:0.499724
九.STM32中DSP的方差计算规则
标准差和方差的关系:标准差的平方即为方差
float32_t pSrc3[2] = {0.4387f, 0.3816f};
float32_t pResult3;
arm_var_f32(pSrc3, 2, &pResult3);
printf("arm_var_f32 : pResult3 = %f\r\n", pResult3);
输出结果:arm_var_f32 : pResult3 = 0.001630
计算数组中元素的总和:0.4387 + 0.3816 = 0.8203
计算数组中总的元素的平均值:0.8203 / 2 = 0.41015
计算数组中每个元素和平均值的差值:0.4387 - 0.41015=0.02855
计算数组中每个元素和平均值的差值:0.3816 - 0.41015=-0.02855
计算差值的平方累加和:0.02855 * 0.02855 + (-0.02855 * -0.02855) = 0.0008151025+0.0008151025=0.001630205
计算平方累加和求平均:由于一共2个数据,所以:0.001630205 / (2 - 1) = 0.001630205
所以输出结果为:0.001630
十.STM32中浮点数转换为定点数的计算规则
浮点数转 Q31 公式描述:
pDst[n] = (q31_t)(pSrc[n] * 2147483648); 0 <= n < blockSize。
浮点数转 Q15 公式描述:
pDst[n] = (q15_t)(pSrc[n] * 32768); 0 <= n < blockSize
浮点数转 Q7 公式描述:
pDst[n] = (q7_t)(pSrc[n] * 128); 0 <= n < blockSize
float32_t pSrc[10] = {0.6557, 0.0357, 0.8491, 0.9340, 0.6787, 0.7577, 0.7431, 0.3922, 0.6555, 0.1712};
uint32_t pIndex;
q15_t pDst2[10];
arm_float_to_q15(pSrc, pDst2, 10);
for(pIndex = 0; pIndex < 10; pIndex++)
{
printf("arm_float_to_q15: pDst1[%d] = %d\r\n", pIndex, pDst2[pIndex]);
}
输出结果:
arm_float_to_q15: pDst1[0] = 21485
arm_float_to_q15: pDst1[1] = 1169
arm_float_to_q15: pDst1[2] = 27823
arm_float_to_q15: pDst1[3] = 30605
arm_float_to_q15: pDst1[4] = 22239
arm_float_to_q15: pDst1[5] = 24828
arm_float_to_q15: pDst1[6] = 24349
arm_float_to_q15: pDst1[7] = 12851
arm_float_to_q15: pDst1[8] = 21479
arm_float_to_q15: pDst1[9] = 5609
arm_float_to_q15(pSrc, pDst2, 10);由于q15的范围为:-32768-32767,0.6557 * 32768 = 21485.9776,所以输出结果为:21485
十一.STM32中DSP的复数共轭运算( ComplexConj)
计算规则
for(n=0; n<numSamples; n++)
{
pDst[(2n)+0)] = pSrc[(2n)+0]; // 实部
pDst[(2n)+1)] = -pSrc[(2n)+1]; // 虚部
}
用代数式来表示a+bi的共轭就是a-bi
uint8_t i;
float32_t pSrc[10] = {1.1f, 1.1f, 2.1f, 2.1f, 3.1f, 3.1f, 4.1f, 4.1f, 5.1f, 5.1f};
float32_t pDst[10];
arm_cmplx_conj_f32(pSrc, pDst, 5);
for(i = 0; i < 5; i++)
{
printf("pSrc[%d] = %f %fj pDst[%d] = %f %fj\r\n", i, pSrc[2*i], pSrc[2*i+1], i, pDst[2*i], pDst[2*i+1]);
}
输出结果:
pSrc[0] = 1.100000 1.100000j pDst[0] = 1.100000 -1.100000j
pSrc[1] = 2.100000 2.100000j pDst[1] = 2.100000 -2.100000j
pSrc[2] = 3.100000 3.100000j pDst[2] = 3.100000 -3.100000j
pSrc[3] = 4.100000 4.100000j pDst[3] = 4.100000 -4.100000j
pSrc[4] = 5.100000 5.100000j pDst[4] = 5.100000 -5.100000j