Printing uint64 with arm gcc? - Page 1
Forum section: electronics / general

author: zapta
author_rank: Super Contributor
timestamp: March 31, 2015, 04:05:04 am
content: 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:for(;;) {static char bfr;uint64_t value = -3; // this should be a large unsigned valuesnprintf(bfr, sizeof(bfr), "%llu", value);debug.printf("[%s]\n", bfr); // this prints ''wait_ms(1000);}I am using lpcxpresso for LPC11U35/501, project exported from mbed.Compiler invocation line looks like thisarm-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 thisarm-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 -lmbedDo 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.

author: westfw
author_rank: Super Contributor
timestamp: March 31, 2015, 04:30:11 am
content: When all else fails, read the documentation:http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.htmlQuote

author: miguelvp
author_rank: Super Contributor
timestamp: March 31, 2015, 04:55:39 am
content: 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:for(;;) {static char bfr;uint64_t value = -3; // this should be a large unsigned valueif (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 ''wait_ms(1000);}btw on the else you only needsnprintf(bfr, sizeof(bfr), "%lu", value);since it's already under 2^32

author: andersm
author_rank: Super Contributor
timestamp: March 31, 2015, 06:48:13 am
content: Quote from: westfw on March 31, 2015, 04:30:11 amWhile good advice, I don't think the avr-libc documentation is all that relevant for an ARM compiler. Nevertheless, assuming the OP is using theGNU Tools for ARM Embedded Processorstoolchain, thecause may be the same.

author: zapta
author_rank: Super Contributor
timestamp: March 31, 2015, 08:04:11 am
content: Quote from: westfw on March 31, 2015, 04:30:11 amCouldn't be clearer. Thanks. I was looking herehttp://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Ciabcebf.html

author: nctnico
author_rank: Super Contributor
timestamp: March 31, 2015, 12:03:48 pm
content: 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.

author: miguelvp
author_rank: Super Contributor
timestamp: March 31, 2015, 01:33:48 pm
content: Must have been too sleepy when I wrote my code, that wont work at all since what is needed is base 10.

author: zapta
author_rank: Super Contributor
timestamp: March 31, 2015, 02:40:29 pm
content: Quote from: miguelvp on March 31, 2015, 01:33:48 pmI 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 orderCode: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;}

author: Lukas
author_rank: Frequent Contributor
timestamp: March 31, 2015, 04:52:53 pm
content: 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. Seehttp://www.mikrocontroller.net/topic/359573#new(german)

author: zapta
author_rank: Super Contributor
timestamp: March 31, 2015, 11:53:17 pm
content: Quote from: Lukas on March 31, 2015, 04:52:53 pmThanks, good to know. I don't have problem passing int64, just that printf doesn't recognize %llu.

author: miguelvp
author_rank: Super Contributor
timestamp: April 01, 2015, 12:44:17 am
content: Would this work?Code:#define TEN_TO_THE9 1000000000UL#define TEN_TO_THE18 (TEN_TO_THE9*TEN_TO_THE9)for(;;) {static char bfr;uint64_t value = -3; // this should be a large unsigned valueuint64_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 ''wait_ms(1000);}

author: zapta
author_rank: Super Contributor
timestamp: April 01, 2015, 07:56:34 am
content: 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?

author: miguelvp
author_rank: Super Contributor
timestamp: April 01, 2015, 09:11:17 am
content: 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.

author: nctnico
author_rank: Super Contributor
timestamp: April 01, 2015, 09:32:13 am
content: 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:printf("64 bit value=%s\n, uint64ToDecimal(value));

author: Laura
author_rank: Contributor
timestamp: April 01, 2015, 11:47:00 am
content: Here's the guts of a different algorithm for binary to decimal conversion. This method needs no multiplies or divides.Code:char bcd; /* bcd result in little endian order */char carry;uintt64_t mask;int i;for (i=0; i++; i<20)bcd = 0;for (mask = (1UL << 63); mask >>= 1; mask){carry = (convertthis & mask) ? 1 : 0;for (i=0; i++; i<20)if (bcd < 5){bcd += bcd + carry;carry = 0;}else{bcd += bcd + 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:MOVEQ #32-1,D6nextbit ADD.L D0,D0 ; Next bit to X flagABCD.B D5,D5ABCD.B D4,D4ABCD.B D3,D3ABCD.B D2,D2ABCD.B D1,D1DBRA D6,nextbit

author: nctnico
author_rank: Super Contributor
timestamp: April 01, 2015, 12:57:23 pm
content: 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.

author: Laura
author_rank: Contributor
timestamp: April 01, 2015, 01:41:10 pm
content: Quote from: nctnico on April 01, 2015, 12:57:23 pmYes 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: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.

author: zapta
author_rank: Super Contributor
timestamp: April 01, 2015, 02:18:09 pm
content: Quote from: Laura on April 01, 2015, 01:41:10 pmI 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?

author: Laura
author_rank: Contributor
timestamp: April 02, 2015, 08:17:51 am
content: 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: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);}Quote from: zapta on April 01, 2015, 02:18:09 pmHow's this:Code: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 */

author: zapta
author_rank: Super Contributor
timestamp: April 03, 2015, 03:54:33 pm
content: Quote from: Laura on April 02, 2015, 08:17:51 amMind 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.

author: Laura
author_rank: Contributor
timestamp: April 04, 2015, 11:27:27 pm
content: Quote from: zapta on April 03, 2015, 03:54:33 pmHow 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 = 11*2 = 21*2*2 = 41*2*2*2 = 81*2*2*2*2 + 1 = 16 + 1 = 171*2*2*2*2*2 + 1*2 = 32 + 2 = 341*2*2*2*2*2*2 + 1*2*2 = 64 + 4 = 681*2*2*2*2*2*2*2 + 1*2*2*2 + 1 = 128 + 8 + 1 = 137Combining the multiplies:(1*2*2*2*2 + 1)*2*2*2 + 1 = 137The 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...

author: Karel
author_rank: Super Contributor
timestamp: April 05, 2015, 06:37:49 am
content: Quote from: nctnico on March 31, 2015, 12:03:48 pmIt 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.

author: westfw
author_rank: Super Contributor
timestamp: April 05, 2015, 07:17:03 am
content: 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

author: andersm
author_rank: Super Contributor
timestamp: April 05, 2015, 08:49:20 am
content: Quote from: westfw on April 05, 2015, 07:17:03 amNot 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).