如何正确打印 2.30 定点变量
How to correctly print a 2.30 fixed point variable
我正在使用 cortex-M3 获取 DFT。我正在使用 CMSIS DSP 函数 arm_cmplx_mag_q31 计算幅度。文档说它 returns 格式为 2.30 的结果(我假设整数是 2 位,小数部分是 30 位;没有符号位,因为幅度不能为负)。
我正在尝试将结果输出给用户,但我发现很难打印出正确的值。
我试过使用 typedef 定义一个新的联合,我可以像这样存储整数和小数部分
/* 2.30 union*/
typedef union {
uint32_t full;
struct {
uint32_t fpart:30;
uint8_t ipart:2;
} parts;
} fixed2_30_t;
然后我将 2.30 震级结果存储到一个 fixed2_30_t 变量中并尝试打印它的部分
fixed2_30_t result;
result = magnitude;
sprintf(msg, "final results %0d.%010u",
result.parts.ipart, result.parts.fpart / 1073741824);
我将小数部分除以 2^30 以将其缩小为小数,但我没有获得合理的结果,而且我不完全确定我的零填充是否正确。
正确的打印方式是什么?以及如何确定要使用的零填充?
谢谢!
假设您的 Cortex M3 对 64 位整数运算有足够好的支持(支持 C99 或更高版本——这需要 64 位运算),那么这段代码展示了它是如何完成的。
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
static const uint64_t TWO_THIRTY = 1UL << 30;
static const uint64_t ONE_BILLION = 1000000000;
static uint32_t fraction(uint32_t fpart)
{
assert(fpart < TWO_THIRTY);
uint64_t result = (fpart * ONE_BILLION) / TWO_THIRTY;
assert(result < ONE_BILLION);
return result;
}
int main(void)
{
for (uint32_t i = 0; i < 32; i++)
printf("%10" PRIu32 " = 0.%.9" PRIu32 "\n", i, fraction(i));
for (uint32_t i = 64; i < TWO_THIRTY; i += (20 * i) / 19 + 1)
printf("%10" PRIu32 " = 0.%.9" PRIu32 "\n", i, fraction(i));
for (uint32_t i = TWO_THIRTY - 32; i < TWO_THIRTY; i++)
printf("%10" PRIu32 " = 0.%.9" PRIu32 "\n", i, fraction(i));
return 0;
}
我得到的输出是:
0 = 0.000000000
1 = 0.000000000
2 = 0.000000001
3 = 0.000000002
4 = 0.000000003
5 = 0.000000004
6 = 0.000000005
7 = 0.000000006
8 = 0.000000007
9 = 0.000000008
10 = 0.000000009
11 = 0.000000010
12 = 0.000000011
13 = 0.000000012
14 = 0.000000013
15 = 0.000000013
16 = 0.000000014
17 = 0.000000015
18 = 0.000000016
19 = 0.000000017
20 = 0.000000018
21 = 0.000000019
22 = 0.000000020
23 = 0.000000021
24 = 0.000000022
25 = 0.000000023
26 = 0.000000024
27 = 0.000000025
28 = 0.000000026
29 = 0.000000027
30 = 0.000000027
31 = 0.000000028
64 = 0.000000059
132 = 0.000000122
271 = 0.000000252
557 = 0.000000518
1144 = 0.000001065
2349 = 0.000002187
4822 = 0.000004490
9898 = 0.000009218
20317 = 0.000018921
41704 = 0.000038839
85603 = 0.000079724
175712 = 0.000163644
360673 = 0.000335902
740329 = 0.000689485
1519623 = 0.001415259
3119227 = 0.002905006
6402624 = 0.005962908
13142229 = 0.012239654
26976155 = 0.025123502
55372108 = 0.051569294
113658538 = 0.105852762
233299105 = 0.217276723
252826200 = 0.235462747
292908132 = 0.272791955
375181572 = 0.349415067
544058633 = 0.506694086
664650111 = 0.619003652
686129076 = 0.639007497
730217478 = 0.680068021
820714724 = 0.764350149
1006472229 = 0.937350307
1073741792 = 0.999999970
1073741793 = 0.999999971
1073741794 = 0.999999972
1073741795 = 0.999999972
1073741796 = 0.999999973
1073741797 = 0.999999974
1073741798 = 0.999999975
1073741799 = 0.999999976
1073741800 = 0.999999977
1073741801 = 0.999999978
1073741802 = 0.999999979
1073741803 = 0.999999980
1073741804 = 0.999999981
1073741805 = 0.999999982
1073741806 = 0.999999983
1073741807 = 0.999999984
1073741808 = 0.999999985
1073741809 = 0.999999986
1073741810 = 0.999999986
1073741811 = 0.999999987
1073741812 = 0.999999988
1073741813 = 0.999999989
1073741814 = 0.999999990
1073741815 = 0.999999991
1073741816 = 0.999999992
1073741817 = 0.999999993
1073741818 = 0.999999994
1073741819 = 0.999999995
1073741820 = 0.999999996
1073741821 = 0.999999997
1073741822 = 0.999999998
1073741823 = 0.999999999
例如,您可以使用 bc
来验证计算。我调用程序 fp71
.
$ fp71 | awk '{print }' |
> { echo 'scale=9'; echo 'd=2^30'; while read value; do echo "$value / d"; done; } |
> bc
0
0
.000000001
.000000002
.000000003
.000000004
.000000005
.000000006
.000000007
.000000008
.000000009
.000000010
.000000011
.000000012
.000000013
.000000013
.000000014
.000000015
.000000016
.000000017
.000000018
.000000019
.000000020
.000000021
.000000022
.000000023
.000000024
.000000025
.000000026
.000000027
.000000027
.000000028
.000000059
.000000122
.000000252
.000000518
.000001065
.000002187
.000004490
.000009218
.000018921
.000038839
.000079724
.000163644
.000335902
.000689485
.001415259
.002905006
.005962908
.012239654
.025123502
.051569294
.105852762
.217276723
.235462747
.272791955
.349415067
.506694086
.619003652
.639007497
.680068021
.764350149
.937350307
.999999970
.999999971
.999999972
.999999972
.999999973
.999999974
.999999975
.999999976
.999999977
.999999978
.999999979
.999999980
.999999981
.999999982
.999999983
.999999984
.999999985
.999999986
.999999986
.999999987
.999999988
.999999989
.999999990
.999999991
.999999992
.999999993
.999999994
.999999995
.999999996
.999999997
.999999998
.999999999
$
这些结果是一致的——除了我更喜欢输出中小数点前的前导零。
How to correctly print a 2.30 fixed point variable
要在 .
后打印到小数点后 10 位的四舍五入值,请将分数乘以 1010,然后除以 232 .
使用unsigned long long
数学来确保概率。请注意,下面的乘积最大 .fpart
是一个 64 位正值。
sprintf(msg, "final results %0d.%010llu",
result.parts.ipart,
// add half the divisor
(result.parts.fpart * 10000000000LLu + 0x40000000u/2) / 0x40000000u);
注意:小数位数少于10位,四舍五入可能会改变整数部分。
我正在使用 cortex-M3 获取 DFT。我正在使用 CMSIS DSP 函数 arm_cmplx_mag_q31 计算幅度。文档说它 returns 格式为 2.30 的结果(我假设整数是 2 位,小数部分是 30 位;没有符号位,因为幅度不能为负)。
我正在尝试将结果输出给用户,但我发现很难打印出正确的值。
我试过使用 typedef 定义一个新的联合,我可以像这样存储整数和小数部分
/* 2.30 union*/
typedef union {
uint32_t full;
struct {
uint32_t fpart:30;
uint8_t ipart:2;
} parts;
} fixed2_30_t;
然后我将 2.30 震级结果存储到一个 fixed2_30_t 变量中并尝试打印它的部分
fixed2_30_t result;
result = magnitude;
sprintf(msg, "final results %0d.%010u",
result.parts.ipart, result.parts.fpart / 1073741824);
我将小数部分除以 2^30 以将其缩小为小数,但我没有获得合理的结果,而且我不完全确定我的零填充是否正确。
正确的打印方式是什么?以及如何确定要使用的零填充?
谢谢!
假设您的 Cortex M3 对 64 位整数运算有足够好的支持(支持 C99 或更高版本——这需要 64 位运算),那么这段代码展示了它是如何完成的。
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
static const uint64_t TWO_THIRTY = 1UL << 30;
static const uint64_t ONE_BILLION = 1000000000;
static uint32_t fraction(uint32_t fpart)
{
assert(fpart < TWO_THIRTY);
uint64_t result = (fpart * ONE_BILLION) / TWO_THIRTY;
assert(result < ONE_BILLION);
return result;
}
int main(void)
{
for (uint32_t i = 0; i < 32; i++)
printf("%10" PRIu32 " = 0.%.9" PRIu32 "\n", i, fraction(i));
for (uint32_t i = 64; i < TWO_THIRTY; i += (20 * i) / 19 + 1)
printf("%10" PRIu32 " = 0.%.9" PRIu32 "\n", i, fraction(i));
for (uint32_t i = TWO_THIRTY - 32; i < TWO_THIRTY; i++)
printf("%10" PRIu32 " = 0.%.9" PRIu32 "\n", i, fraction(i));
return 0;
}
我得到的输出是:
0 = 0.000000000
1 = 0.000000000
2 = 0.000000001
3 = 0.000000002
4 = 0.000000003
5 = 0.000000004
6 = 0.000000005
7 = 0.000000006
8 = 0.000000007
9 = 0.000000008
10 = 0.000000009
11 = 0.000000010
12 = 0.000000011
13 = 0.000000012
14 = 0.000000013
15 = 0.000000013
16 = 0.000000014
17 = 0.000000015
18 = 0.000000016
19 = 0.000000017
20 = 0.000000018
21 = 0.000000019
22 = 0.000000020
23 = 0.000000021
24 = 0.000000022
25 = 0.000000023
26 = 0.000000024
27 = 0.000000025
28 = 0.000000026
29 = 0.000000027
30 = 0.000000027
31 = 0.000000028
64 = 0.000000059
132 = 0.000000122
271 = 0.000000252
557 = 0.000000518
1144 = 0.000001065
2349 = 0.000002187
4822 = 0.000004490
9898 = 0.000009218
20317 = 0.000018921
41704 = 0.000038839
85603 = 0.000079724
175712 = 0.000163644
360673 = 0.000335902
740329 = 0.000689485
1519623 = 0.001415259
3119227 = 0.002905006
6402624 = 0.005962908
13142229 = 0.012239654
26976155 = 0.025123502
55372108 = 0.051569294
113658538 = 0.105852762
233299105 = 0.217276723
252826200 = 0.235462747
292908132 = 0.272791955
375181572 = 0.349415067
544058633 = 0.506694086
664650111 = 0.619003652
686129076 = 0.639007497
730217478 = 0.680068021
820714724 = 0.764350149
1006472229 = 0.937350307
1073741792 = 0.999999970
1073741793 = 0.999999971
1073741794 = 0.999999972
1073741795 = 0.999999972
1073741796 = 0.999999973
1073741797 = 0.999999974
1073741798 = 0.999999975
1073741799 = 0.999999976
1073741800 = 0.999999977
1073741801 = 0.999999978
1073741802 = 0.999999979
1073741803 = 0.999999980
1073741804 = 0.999999981
1073741805 = 0.999999982
1073741806 = 0.999999983
1073741807 = 0.999999984
1073741808 = 0.999999985
1073741809 = 0.999999986
1073741810 = 0.999999986
1073741811 = 0.999999987
1073741812 = 0.999999988
1073741813 = 0.999999989
1073741814 = 0.999999990
1073741815 = 0.999999991
1073741816 = 0.999999992
1073741817 = 0.999999993
1073741818 = 0.999999994
1073741819 = 0.999999995
1073741820 = 0.999999996
1073741821 = 0.999999997
1073741822 = 0.999999998
1073741823 = 0.999999999
例如,您可以使用 bc
来验证计算。我调用程序 fp71
.
$ fp71 | awk '{print }' |
> { echo 'scale=9'; echo 'd=2^30'; while read value; do echo "$value / d"; done; } |
> bc
0
0
.000000001
.000000002
.000000003
.000000004
.000000005
.000000006
.000000007
.000000008
.000000009
.000000010
.000000011
.000000012
.000000013
.000000013
.000000014
.000000015
.000000016
.000000017
.000000018
.000000019
.000000020
.000000021
.000000022
.000000023
.000000024
.000000025
.000000026
.000000027
.000000027
.000000028
.000000059
.000000122
.000000252
.000000518
.000001065
.000002187
.000004490
.000009218
.000018921
.000038839
.000079724
.000163644
.000335902
.000689485
.001415259
.002905006
.005962908
.012239654
.025123502
.051569294
.105852762
.217276723
.235462747
.272791955
.349415067
.506694086
.619003652
.639007497
.680068021
.764350149
.937350307
.999999970
.999999971
.999999972
.999999972
.999999973
.999999974
.999999975
.999999976
.999999977
.999999978
.999999979
.999999980
.999999981
.999999982
.999999983
.999999984
.999999985
.999999986
.999999986
.999999987
.999999988
.999999989
.999999990
.999999991
.999999992
.999999993
.999999994
.999999995
.999999996
.999999997
.999999998
.999999999
$
这些结果是一致的——除了我更喜欢输出中小数点前的前导零。
How to correctly print a 2.30 fixed point variable
要在 .
后打印到小数点后 10 位的四舍五入值,请将分数乘以 1010,然后除以 232 .
使用unsigned long long
数学来确保概率。请注意,下面的乘积最大 .fpart
是一个 64 位正值。
sprintf(msg, "final results %0d.%010llu",
result.parts.ipart,
// add half the divisor
(result.parts.fpart * 10000000000LLu + 0x40000000u/2) / 0x40000000u);
注意:小数位数少于10位,四舍五入可能会改变整数部分。