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);
}
the ll length modifier will to abort the output, as this realization does not operate long long arguments.
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);
}
When all else fails, read the documentation
When all else fails, read the documentation: http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.htmlQuotethe ll length modifier will to abort the output, as this realization does not operate long long arguments.
Must have been too sleepy when I wrote my code, that wont work at all since what is needed is base 10.
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;
}
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)
#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]
printf("64 bit value=%s\n, uint64ToDecimal(value));
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;
}
}
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
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.
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);
}
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.
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?
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 */
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 */
I will try to understand it and see how it works.
In my experience printing 64 bit integers doesn't really work with GCC on 32 bit ...
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