How do I output a 64bit number on Arduino if its libraries have 32 bit restrictions?

Viewed 9140

The compiler provided for Arduino seems to generate code that can work with 64 bit numbers, but the I/O classes, for instance Serial.Print(), seem to not work beyond 32 bit numbers.

How can I overcome these limitations and print all possible 64 bit unsigned integers as decimal numbers?

A bit of background:

For an Arduino project I need to send a number on Serial that fits on a uint64_t variable type, but for some reason the Arduino Serial port can't handle a type bigger than 32 bit (but the device supports a 6-bit type).

Thus, I need to print that (64-bit) number as Decimal on Serial port only. To do this I was thinking of dividing the number in two blocks of 32 bit each with a bitwise operation. Then send each number in sequence on the Serial port, but obviously the resulting number is different from the original 64-bit number.

For example, I use an unsigned 8-bit number as follows:

11100010 <- 226 in Decimal

And I need to send it to my serial port, but it can only handle 4-bit numbers at max.

Thus I try to split the number in two parts with a bitwise mask:

1110 <- MSBs (14 in Decimal)
0010 <- LSBs (2 in Decimal)

So when I send it in sequence on the Serial I see a 142, but instead what I would is 226.

So, is there an algorithm that given a number as input, the returned result are two numbers that are the breakdown of the previous binary number in MSBs and LSBs?

Appendix:

I'm using streaming.h for serial communication.


As properly guessed by @Infixed I'm trying to:

How do I print a 64 bit number in decimal given 32 bit integer limitations on formatted decimal output?

And up to now I found:

uint64_t val;
uint32_t MSBval, LSBval;
MSBval = (uint32_t)(val / 10000000000LL);
LSBval = (uint32_t)(val % 10000000000LL);
Serial << F("[") << MSBval << LSBval << F("]") << endl;

As you can see I have not yet found the right solution. For higher numbers the formula should not work correctly, but for some reason (I'm still trying to figure out why) when I send the result on the Serial it returns the right number even with the highest number (255 255 255 255 255 255 255 255 [18446744073709551615]).


Check the accepted answer for more solutions that do not relies on Streaming.h library.


THE SOLUTION

This solution is not very elegant, but at least works well for my purpose. If desired, it can be generalized to be used with all types of integers (depends on your Arduino board microcontroller type).

For now is tested only with uint64_t on Arduino Uno R3 (ATmega328P).

Since it seems to be the only complete solution to solve this kind of problem that I was able to find on the web then, if anyone finds any bugs or like to improve it, feel free to reply to (or edit) this post.

Here's the code:

uint8_t MLSBval;
uint64_t val;
char buffer[11];

//val = 18440000009000100000LL; // Test 64bit split
val = 18440000009099999999LL;   // Test 64bit split

sprintf(buffer, "%010lu", MLSBval); // Formatting zeros
Serial << F("[") << (uint32_t)(val / 10000000000LL); // Output MSBval

val = val % 10000000000LL;                         // Calculate LSBval
if (val != 0){
    MLSBval = (uint8_t)(val / 100000000LL);
    val = (val % 100000000LL);
    sprintf(buffer, "%02u", MLSBval); // Formatting zeros
    Serial << buffer;
}
sprintf(buffer, "%08lu", (uint32_t)(val));    // Formatting zeros
Serial << (buffer) << F("]") << endl;         // Data to Serial Monitor;

Then a little explanation:

The biggest problem lies in the length of the decimal number. If the length of the number (e.g. 99 length is 2; 1294 length is 4; etc.) is odd we need to put the if statement code with the MSBval result and then use it in the serial.

Conversely (as showed in the code), if the number is even we need to put the if statement code with the LSBval and then use the result.

This applies to all types of integers.

If you use another int type (32 bit, 16 bit, etc.) as a val type you need to change (reduce) the divider and module number in relation to the above example.

In this case the numbers of zeros of the divider and module before the if statement corresponds to the half the length of the largest number that fit the uint64 type (in Decimal, then ten zeroes). Instead those contained inside the if statement have two zeros in less (eight zeroes).

Obviously I have not tested the code with all the numbers involved, but I hope this work with all!

1 Answers
Related