Author Topic: Printing uint64 with arm gcc?  (Read 21674 times)

0 Members and 1 Guest are viewing this topic.

Offline zaptaTopic starter

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Printing uint64 with arm gcc?
« on: March 31, 2015, 04:05:04 am »
I am having trouble printing a uint64_t either decimal (%lld) or hex (%llx). Instead I am getting 'lu' and 'lx' respectively.  Using a single long (%lu or %lx) prints the only the lower 32 bits.

My test code looks like this:

Code: [Select]
for(;;) {
    static char bfr[100];
    uint64_t value = -3;   // this should be a large unsigned value
    snprintf(bfr, sizeof(bfr), "%llu", value);
    debug.printf("[%s]\n", bfr);   // this prints '[lu]'
    wait_ms(1000);
}

I am using lpcxpresso for LPC11U35/501, project exported from mbed.

Compiler invocation line looks like this

arm-none-eabi-c++ -D__NEWLIB__ -D__CODE_RED -DCPP_USE_HEAP -DTARGET_LPC11U35_501 -DTARGET_M0 -DTARGET_CORTEX_M -DTARGET_NXP -DTARGET_LPC11UXX -DTARGET_MCU_LPC11U35_501 -DTOOLCHAIN_GCC_CR -DTOOLCHAIN_GCC -D__CORTEX_M0 -DARM_MATH_CM0 -DMBED_BUILD_TIMESTAMP=1424065707.97 -D__MBED__=1 -I"/my_project_root" -I"/my_project_root/USBDevice" -I"/my_project_root/USBDevice/USBDevice" -I"/my_project_root/USBDevice/USBMSD" -I"/my_project_root/USBDevice/USBSerial" -I"/my_project_root/USBDevice/USBMIDI" -I"/my_project_root/USBDevice/USBHID" -I"/my_project_root/USBDevice/USBAudio" -I"/my_project_root/mbed" -I"/my_project_root/mbed/TARGET_LPC11U35_501" -I"/my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR" -I"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP" -I"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP/TARGET_LPC11UXX" -I"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP/TARGET_LPC11UXX/TARGET_MCU_LPC11U35_501" -I"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP/TARGET_LPC11UXX/TARGET_MCU_LPC11U35_501/TARGET_LPC11U35_501" -Og -g3 -Wall -c -fmessage-length=0 -fno-builtin -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -mcpu=cortex-m0 -mthumb -D__NEWLIB__ -MMD -MP -MF"main.d" -MT"main.o" -MT"main.d" -o "main.o" "../main.cpp"

and linker looks like this

arm-none-eabi-c++ -nostdlib -L"/my_project_root" -L"/my_project_root/USBDevice" -L"/my_project_root/USBDevice/USBDevice" -L"/my_project_root/USBDevice/USBMSD" -L"/my_project_root/USBDevice/USBSerial" -L"/my_project_root/USBDevice/USBMIDI" -L"/my_project_root/USBDevice/USBHID" -L"/my_project_root/USBDevice/USBAudio" -L"/my_project_root/mbed" -L"/my_project_root/mbed/TARGET_LPC11U35_501" -L"/my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR" -L"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP" -L"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP/TARGET_LPC11UXX" -L"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP/TARGET_LPC11UXX/TARGET_MCU_LPC11U35_501" -L"/my_project_root/mbed/TARGET_LPC11U35_501/TARGET_NXP/TARGET_LPC11UXX/TARGET_MCU_LPC11U35_501/TARGET_LPC11U35_501" -Xlinker -Map="mbed_wifi.map" -Xlinker --gc-sections -mcpu=cortex-m0 -mthumb -T "/my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR/LPC11U35.ld" -o "mbed_wifi.axf"  ./USBDevice/USBSerial/USBCDC.o ./USBDevice/USBSerial/USBSerial.o  ./USBDevice/USBMSD/USBMSD.o  ./USBDevice/USBMIDI/USBMIDI.o  ./USBDevice/USBHID/USBHID.o ./USBDevice/USBHID/USBKeyboard.o ./USBDevice/USBHID/USBMouse.o ./USBDevice/USBHID/USBMouseKeyboard.o  ./USBDevice/USBDevice/USBDevice.o ./USBDevice/USBDevice/USBHAL_KL25Z.o ./USBDevice/USBDevice/USBHAL_LPC11U.o ./USBDevice/USBDevice/USBHAL_LPC17.o ./USBDevice/USBDevice/USBHAL_LPC40.o ./USBDevice/USBDevice/USBHAL_RZ_A1H.o ./USBDevice/USBDevice/USBHAL_STM32F4.o  ./USBDevice/USBAudio/USBAudio.o  ./debug.o ./esp8266.o ./main.o ./parser.o ./protobuf.o ./protocol.o  /my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR/startup_LPC11xx.o /my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR/retarget.o /my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR/board.o /my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR/system_LPC11Uxx.o /my_project_root/mbed/TARGET_LPC11U35_501/TOOLCHAIN_GCC_CR/cmsis_nvic.o -lmbed

Do I need to somehow select a different printf library that supports long long? The compiler seems to be happy about the %llu and doesn't complain about type mismatch.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4196
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #1 on: March 31, 2015, 04:30:11 am »
When all else fails, read the documentation:  http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html

Quote
the ll length modifier will to abort the output, as this realization does not operate long long arguments.
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #2 on: March 31, 2015, 04:55:39 am »
Didn't try it, but would this work?

of course you can use a define for the 2^32 constant as 4294967296UL or (1UL<<32)

Code: [Select]
for(;;) {
    static char bfr[100];
    uint64_t value = -3;   // this should be a large unsigned value
    if (value/4294967296UL > 0)
    {
        snprintf(bfr, sizeof(bfr), "%lu%lu", value/4294967296UL,value%4294967296UL);
    }
    else
    {
       snprintf(bfr, sizeof(bfr), "%lu", value%4294967296UL);
    }
    debug.printf("[%s]\n", bfr);   // this prints '[lu]'
    wait_ms(1000);
}

btw on the else you only need
snprintf(bfr, sizeof(bfr), "%lu", value);

since it's already under 2^32
« Last Edit: March 31, 2015, 07:07:18 am by miguelvp »
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Printing uint64 with arm gcc?
« Reply #3 on: March 31, 2015, 06:48:13 am »
When all else fails, read the documentation
While good advice, I don't think the avr-libc documentation is all that relevant for an ARM compiler. Nevertheless, assuming the OP is using the GNU Tools for ARM Embedded Processors toolchain, the cause may be the same.

Offline zaptaTopic starter

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #4 on: March 31, 2015, 08:04:11 am »
When all else fails, read the documentation:  http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html

Quote
the ll length modifier will to abort the output, as this realization does not operate long long arguments.

Couldn't be clearer. Thanks. I was looking here http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Ciabcebf.html
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 26752
  • Country: nl
    • NCT Developments
Re: Printing uint64 with arm gcc?
« Reply #5 on: March 31, 2015, 12:03:48 pm »
In my experience printing 64 bit integers doesn't really work with GCC on 32 bit (or less) platforms. When I face this situation I add an extra function to format the 64 bit values.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #6 on: March 31, 2015, 01:33:48 pm »
Must have been too sleepy when I wrote my code, that wont work at all since what is needed is base 10.  :-DD
 

Offline zaptaTopic starter

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #7 on: March 31, 2015, 02:40:29 pm »
Must have been too sleepy when I wrote my code, that wont work at all since what is needed is base 10.  :-DD

I think it requires divs by a power of 10. For example 1, 2 or 3 printf with 10^9 and 10^18 limits so you don't split digits. I ended writing a quick and dirty function that encodes in reversed order

Code: [Select]
static char bfr[20+1];

static char* uint64ToDecimal(uint64_t v) {
  char* p = bfr + sizeof(bfr);
  *(--p) = '\0';
  for (bool first = true; v || first; first = false) {
    const uint32_t digit = v % 10;
    const char c = '0' + digit;
    *(--p) = c;
    v = v / 10;
  }
  return p;
}

 

Offline Lukas

  • Frequent Contributor
  • **
  • Posts: 412
  • Country: de
    • carrotIndustries.net
Re: Printing uint64 with arm gcc?
« Reply #8 on: March 31, 2015, 04:52:53 pm »
When passing 64bit data around you should make sure that the stack is aligned to an 8 byte boundary. Check your linker script for that. See http://www.mikrocontroller.net/topic/359573#new (german)
 

Offline zaptaTopic starter

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #9 on: March 31, 2015, 11:53:17 pm »
When passing 64bit data around you should make sure that the stack is aligned to an 8 byte boundary. Check your linker script for that. See http://www.mikrocontroller.net/topic/359573#new (german)

Thanks, good to know. I don't have problem passing int64, just that printf doesn't recognize %llu.
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #10 on: April 01, 2015, 12:44:17 am »
Would this work?
Code: [Select]
#define TEN_TO_THE9 1000000000UL
#define TEN_TO_THE18 (TEN_TO_THE9*TEN_TO_THE9)

for(;;) {
    static char bfr[100];
    uint64_t value = -3;   // this should be a large unsigned value
    uint64_t temp;
    if (value/4294967296UL > 0)
    {
        if(value >= TEN_TO_THE18)
        {
            temp = (value/TEN_TO_THE18)*TEN_TO_THE18;
            snprintf(bfr, sizeof(bfr), "%lu%09lu%09lu",
                         value/TEN_TO_THE18,
                         (value-temp)/TEN_TO_THE9,
                         (value-temp)%TEN_TO_THE9);
        }
        else
        {
            snprintf(bfr, sizeof(bfr), "%lu%09lu", value/TEN_TO_THE9,value%TEN_TO_THE9);
        }
    }
    else
    {
       snprintf(bfr, sizeof(bfr), "%lu", value);
    }
    debug.printf("[%s]\n", bfr);   // this prints '[lu]'
    wait_ms(1000);
}
[code]
« Last Edit: April 01, 2015, 12:50:18 am by miguelvp »
 

Offline zaptaTopic starter

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #11 on: April 01, 2015, 07:56:34 am »
Tapatalk distorts the code (it found three smilys) but it looks right.  And you have full efficiency for the case of u32 range instead of using the to the 9th limit.  Nice.

BTW how fast are 32 and 64 bits mul and div on M0?
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #12 on: April 01, 2015, 09:11:17 am »
Probably not that great since I don't think the M0 has a divide to begin with, and the multiply can be as fast as one cycle or as much as 32 cycles depending on the implementation and that's for 32 bits since it doesn't have 64bit native support.

So most likely they will be function calls implemented by your tool chain. I think you'll need an M3 for 64 bit support and for division support on hardware.
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 26752
  • Country: nl
    • NCT Developments
Re: Printing uint64 with arm gcc?
« Reply #13 on: April 01, 2015, 09:32:13 am »
IMHO the code Zapta posted is the best way to go. Printf will do the same conversion under the hood. You can use Zapta's function easely in a printf:
Code: [Select]
printf("64 bit value=%s\n, uint64ToDecimal(value));
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline Laura

  • Contributor
  • Posts: 18
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #14 on: April 01, 2015, 11:47:00 am »
Here's the guts of a different algorithm for binary to decimal conversion. This method needs no multiplies or divides.

Code: [Select]
char bcd[20]; /* bcd result in little endian order */
char carry;
uintt64_t mask;
int i;

for (i=0; i++; i<20)
 bcd[i] = 0;

for (mask = (1UL << 63); mask >>= 1; mask)
 {
 carry = (convertthis & mask) ? 1 : 0;
 for (i=0; i++; i<20)
  if (bcd[i] < 5)
   {
   bcd[i] += bcd[i] + carry;
   carry = 0;
   }
  else
   {
   bcd[i] += bcd[i] + carry - 10;
   carry = 1;
   }
 }

The above code can be optimized a lot. In particular, the bcd values can be packed together into whatever length integer your processor can easily process. You don't need (access to) bcd instructions - the bcd "adjust" needs just some shifts and bitwise operations. Here's the main loop (for 32 bit integers) in 68000 assembler:

Code: [Select]
MOVEQ #32-1,D6
nextbit ADD.L D0,D0 ; Next bit to X flag
 ABCD.B D5,D5
 ABCD.B D4,D4
 ABCD.B D3,D3
 ABCD.B D2,D2
 ABCD.B D1,D1
 DBRA D6,nextbit
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 26752
  • Country: nl
    • NCT Developments
Re: Printing uint64 with arm gcc?
« Reply #15 on: April 01, 2015, 12:57:23 pm »
That code hardly isn't any more efficient than the other methods using a division by 10. I see a loop with 64x20=1280 iterations which basically is a division algorithm. There just isn't an easy solution for converting hex to decimal.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline Laura

  • Contributor
  • Posts: 18
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #16 on: April 01, 2015, 01:41:10 pm »
That code hardly isn't any more efficient than the other methods using a division by 10. I see a loop with 64x20=1280 iterations which basically is a division algorithm. There just isn't an easy solution for converting hex to decimal.

Yes your right that the inner loop does a lot of iterations. But as I said, the implementation can be optimized quite a bit. Here's a version that only loops 64 times (it is up the reader to extend this to 20 digits and unpack the result):

Code: [Select]
uintt64_t sixteendigits = 0;
uintt64_t bcdadjust;
uintt64_t mask;

for (mask = 1UL<<63; mask >>= 1; mask)
 {
 bcdadjust = ((sixteendigits + 0x3333333333333333UL) & 0x8888888888888888UL) >> 2;
 bcdadjust |= bcdadjust << 1;
 sixteendigits += sixteendigits + bcdadjust + ((convertthis & mask) ? 1 : 0);
 }

On processors with fast multiply and/or divide, it could be a wash between this method and the "conventional" divide by ten method - that method has to iterate only about one third as much.
 

Offline zaptaTopic starter

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #17 on: April 01, 2015, 02:18:09 pm »
Yes your right that the inner loop does a lot of iterations. But as I said, the implementation can be optimized quite a bit. Here's a version that only loops 64 times (it is up the reader to extend this to 20 digits and unpack the result):

Code: [Select]
uintt64_t sixteendigits = 0;
uintt64_t bcdadjust;
uintt64_t mask;

for (mask = 1UL<<63; mask >>= 1; mask)
 {
 bcdadjust = ((sixteendigits + 0x3333333333333333UL) & 0x8888888888888888UL) >> 2;
 bcdadjust |= bcdadjust << 1;
 sixteendigits += sixteendigits + bcdadjust + ((convertthis & mask) ? 1 : 0);
 }

On processors with fast multiply and/or divide, it could be a wash between this method and the "conventional" divide by ten method - that method has to iterate only about one third as much.

I didn't look into the details how it works but if it does it's ver neat. The conversion to a string is then similar to hex conversion.  How would you do that, leaving the result starting at a given address?

 

Offline Laura

  • Contributor
  • Posts: 18
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #18 on: April 02, 2015, 08:17:51 am »
Here's a version that handles all 20 digits. Beware, I'm 15 years rusty and not up to date on all the latest 64 bit gotchas! Hence some details of this code might not be portable.

Also note, this is slightly different from the version about because the bcd adjustment is done on the pre-doubled nybbles.

This code splits the digits into 16 and 4. Another split could have been 10 and 10. That would make both halves the same length. If the target processor only has 32 bit native types, then perhaps a split of 8, 8, and 4 might be better.

Code: [Select]
uintt64_t sixteendigits = 0;
uintt64_t topfourdigits = 0;
uintt64_t bcdadjust; /* Turns digits of 5, 6, 7, 8, 9 into 0x10, 0x12, 0x14, 0x16, 0x18 after doubling */
uintt64_t mask;

for (mask = 1UL<<63; mask >>= 1; mask)
 {
 bcdadjust = ((sixteendigits + 0x3333333333333333UL) & 0x8888888888888888UL) >> 3;
 bcdadjust |= bcdadjust << 1;
 sixteendigits += bcdadjust;
 bcdadjust = ((topfourdigits + 0x3333UL) & 0x8888UL) >> 3;
 bcdadjust |= bcdadjust << 1;
 topfourdigits += bcdadjust;
 topfourdigits += topfourdigits + ((sixteendigits >> 63) & 0x1);
 sixteendigits += sixteendigits + ((convertthis & mask) ? 1 : 0);
 }

I didn't look into the details how it works but if it does it's ver neat. The conversion to a string is then similar to hex conversion.  How would you do that, leaving the result starting at a given address?

How's this:

Code: [Select]
shift = 80;
/* Trim leading zeroes except for the last digit */
do
 {
 if ((((shift > 64) ? (topfourdigits >> (shift - 64 - 4)) : (sixteendigits >> (shift - 4))) & 0xf) != 0)
  break;
 shift -= 4;
 }
while (shift > 4);
/* Output at least one digit */
do
 {
 *string++ = '0' + (((shift > 64) ? (topfourdigits >> (shift - 64 - 4)) : (sixteendigits >> (shift - 4))) & 0xf);
 shift -= 4;
 }
while (shift > 0);
/* write the string terminator or length here */
 

Offline zaptaTopic starter

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #19 on: April 03, 2015, 03:54:33 pm »

How's this:

Code: [Select]
shift = 80;
/* Trim leading zeroes except for the last digit */
do
 {
 if ((((shift > 64) ? (topfourdigits >> (shift - 64 - 4)) : (sixteendigits >> (shift - 4))) & 0xf) != 0)
  break;
 shift -= 4;
 }
while (shift > 4);
/* Output at least one digit */
do
 {
 *string++ = '0' + (((shift > 64) ? (topfourdigits >> (shift - 64 - 4)) : (sixteendigits >> (shift - 4))) & 0xf);
 shift -= 4;
 }
while (shift > 0);
/* write the string terminator or length here */

Mind tricks of the bit jedi! 

Thanks Laura.  I will try to understand it and see how it works.

I often opt for simpler and sub optimal code just because it's easier for me and future reader to understand and verify it.
 

Offline Laura

  • Contributor
  • Posts: 18
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #20 on: April 04, 2015, 11:27:27 pm »
I will try to understand it and see how it works.

How the conversion algorithm works:

Lets convert 0x89. This is 2^7 + 2^3 + 2^0, i.e. 1*2*2*2*2*2*2*2 + 1*2*2*2 + 1. Lets split the multiplies into multiple steps, and tack on the following terms at the appropriate stage:

1 = 1
1*2 = 2
1*2*2 = 4
1*2*2*2 = 8
1*2*2*2*2 + 1 = 16 + 1 = 17
1*2*2*2*2*2 + 1*2 = 32 + 2 = 34
1*2*2*2*2*2*2 + 1*2*2 = 64 + 4 = 68
1*2*2*2*2*2*2*2 + 1*2*2*2 + 1 = 128 + 8 + 1 = 137

Combining the multiplies:

(1*2*2*2*2 + 1)*2*2*2 + 1 = 137

The algorithm works to convert between any two bases. The multiplies and adds are done in the target base (base 10 here, hence bcd), and the multiply value is the source base (2 in this instance.)

Here's the same algorithm converting from base 10 to base 2:

binary = 0;
while ((digit = *string++) != 0)
 binary = binary * 10 + (digit - '0');

This is quite simple and familiar looking, right?

I assume everyone else has long since gotten bored...
 

Offline Karel

  • Super Contributor
  • ***
  • Posts: 2214
  • Country: 00
Re: Printing uint64 with arm gcc?
« Reply #21 on: April 05, 2015, 06:37:49 am »
In my experience printing 64 bit integers doesn't really work with GCC on 32 bit ...

It works perfectly. The net is full of code using the option %llu or %lli on 32-bit platforms, whether it is Linux on x86, ARM or windows 32-bit.
I'm using it myself all the time. Something else must be going on.

 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4196
  • Country: us
Re: Printing uint64 with arm gcc?
« Reply #22 on: April 05, 2015, 07:17:03 am »
It's a libc thing, not a compiler thing.  It looks like newlib supports long long in printf/etc, but newlib-nano doesn't.  (there is perhaps a build option for newlib as well; if you have some Nth-party build environment, who knows how things were compiled.)
https://answers.launchpad.net/gcc-arm-embedded/+question/257014

 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Printing uint64 with arm gcc?
« Reply #23 on: April 05, 2015, 08:49:20 am »
It's a libc thing, not a compiler thing.  It looks like newlib supports long long in printf/etc, but newlib-nano doesn't.  (there is perhaps a build option for newlib as well; if you have some Nth-party build environment, who knows how things were compiled.)
https://answers.launchpad.net/gcc-arm-embedded/+question/257014
Not to be grumpy or anything, but I gave that same link in the fourth post in this thread. And yes, it's a configurable option (--enable-newlib-io-long-long).
« Last Edit: April 05, 2015, 09:07:48 am by andersm »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf