3

I'm obtaining a DFT using a cortex-M3. I'm calculating the magnitude using the CMSIS DSP function arm_cmplx_mag_q31. The documentation says that it returns the result in the format 2.30 (which I assume is 2 bits for the integer and 30 for the fractional part; without sign bit since the magnitude can't be negative).

I'm trying to output the result to the user, but I'm finding hard to print out the correct value.

I've tried using typedef to define a new union where I can store the integer and fractional part like this

/* 2.30 union*/
typedef union {
  uint32_t full;
  struct {
    uint32_t fpart:30;
    uint8_t  ipart:2;
  } parts;
} fixed2_30_t;

then I store the 2.30 magnitude result into a fixed2_30_t variable and try to print its parts

fixed2_30_t   result;
result = magnitude;

sprintf(msg, "final results %0d.%010u", 
        result.parts.ipart, result.parts.fpart / 1073741824);

I'm dividing the fractional part by 2^30 to scale it back to a decimal, but I'm not obtaining sensible results, and I'm not entirely sure my zero padding is correct.

What would be the correct way to print it? And how do you determine the zero-padding to use?

Thanks!

artless noise
  • 21,212
  • 6
  • 68
  • 105
DCrown
  • 57
  • 7
  • In theory, you'd be best off using `uint32_t ipart:2;` with the same type as the `fpart`. A compiler could, though most won't, allocate the two bit-fields in separate storage units, in which case you don't achieve the result you require. – Jonathan Leffler Aug 11 '19 at 18:24
  • 1
    You're doing an integer division of a value less than 2^30 by 2^30, which will always be zero. Presumably floating point arithmetic is out of the question; it would be by far the easiest way to deal with the problem. Does the Cortex M3 have 64-bit arithmetic that you're willing to use? The `%010u` format should print 10 decimal digits, right justified and padded with zeros on the left. However, I think what you want is 9 digits, not 10 (because 2^30 is a little over 1 billion), and so you probably need `%09u` instead of `%10u`. Using `%.9u` would work too. – Jonathan Leffler Aug 11 '19 at 18:54
  • Is `sizeof(fixed2_30_t) == 4`? I think that because `ipart` has different type then `fpart` they are splitted. What compiler are you using? Better just use bit masks. – KamilCuk Aug 11 '19 at 19:31
  • @KamilCuk,@JonathanLeffler, I'm using IAR embedded workbench as my compiler. I've tried the suggestion of changing the integer part to uint32_t and the masking `result = magnitude & 0xFFFFFFFF ;`. It doesn't seem to make much difference, e.g the result = 0x154A419E prints as 0.000000002. The Cortex M3 that I'm using does support 64-bit arithmetic – DCrown Aug 12 '19 at 17:21
  • So how much is `sizeof(fixed2_30_t)`? what does `printf("%d", (int)sizeof(fixes2_30_t))` output? – KamilCuk Aug 12 '19 at 21:03
  • @KamilCuk I tested it using the `ipart` as `uint32_t` and as `uint8_t' and with both I get the output 4 – DCrown Aug 13 '19 at 09:32
  • You may be interested in [CMSIS DSP library](https://github.com/ARM-software/CMSIS/tree/master/CMSIS/DSP_Lib) – KamilCuk Aug 13 '19 at 09:37

2 Answers2

2

Assuming your Cortex M3 has sufficiently good support for 64-bit integer arithmetic (supports C99 or later — that requires 64-bit arithmetic), then this code shows how it could be done.

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

The output I get is:

         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

You can verify the using bc to verify the calculation, for example. I called the program fp71.

$ fp71 | awk '{print $1}' |
> { 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
$

These results agree — except I prefer the leading zero before the decimal point in the output.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2

How to correctly print a 2.30 fixed point variable

To print to 10 decimal places after the . with a rounded value, scale the fraction by 1010 and then divide by 232.

Use unsigned long long math to insure probability. Note that the product below, with a maximal .fpart is a 64-bit positive value.

sprintf(msg, "final results %0d.%010llu", 
    result.parts.ipart, 
    //                                    add half the divisor
    (result.parts.fpart * 10000000000LLu + 0x40000000u/2) / 0x40000000u);

Note: with less than 10 fractional digits, rounding may change the integer portion.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Hi @chux I tried implementing your solution, unfortunately, it does print correctly (I'm using IAR embedded workbench compiler). I tried saving the conversion into a dummy variable so, e.g. with `result = 0x0B0C67E9` I get `dummy = 0x0000000066E59528` which is correct, but the printing is the issue I get 0.%lu, I thought it might be an issue with the compiler, so I tried printing the dummy variable alone and found out that it only prints using `%ull` but even changing to %ull in your solution I still get the weird result from above. – DCrown Aug 12 '19 at 17:53
  • @DiegoCorona Try using `"final results %0d.%010llu"` as in the answer. Notice the final `"llu"`. – chux - Reinstate Monica Aug 12 '19 at 20:27
  • @DiegoCorona Most cortex M3 compilers use newlib-nano for standard C implementation by default. The nano is a smaller ("nonconforming") version and most probably it doesn't support printfing `long long` values (and %zu and %hhu and floats are sometimes missing). I usually just `"%08lx%08lx", (unsigned long)(val >> 32), (unsigned long)(val));` and then convert hex to dec in some editor. – KamilCuk Aug 12 '19 at 21:05
  • @KamilCuk Condifnet OP's compiler supports 64-bit integers, but not printing them? If so, I can suggest code to handle that. – chux - Reinstate Monica Aug 12 '19 at 21:34
  • @chux Not 100% confident ;) , but it usually acts that way. – KamilCuk Aug 13 '19 at 05:50
  • 1
    @KamilCuk you were right about the compiler using newlib-nano, it does not support printfing long long values. Fortunately, I was able to find a way to enable printf full lib inside the project options in IAR. Now using chux answer it outputs the right number, thanks to both! – DCrown Aug 13 '19 at 09:52