3

I want to convert a hex string such as "43a2be2a42380" to their decimal representation using a uint64 variable. I need that because i'm implementing a RFID reader acting as a keyboard and the keypresses need to be the decimal digit.

I have seen other answers (convert HEX string to Decimal in arduino) and implementing a solution using strtoul but it only works for 32 bit integers and strtoull is not available.

uint64_t res = 0;
String datatosend = String("43a2be2a42380");
char charBuf[datatosend.length() + 1];
datatosend.toCharArray(charBuf, datatosend.length() + 1) ;
res = strtoul(charBuf, NULL, 16);

What can i do to get the decimal number of a big hex string / byte array using an Arduino?

Community
  • 1
  • 1
h3ct0r
  • 705
  • 1
  • 11
  • 23
  • 1
    "decimal representation in a uint64 variable". Cognition circuit overload, contradiction in terms detected. It's either decimal representation, or a uint64 variable. – n. m. could be an AI Apr 18 '16 at 09:50
  • I said that because I need the real hexadecimal number not the decimal value of every digit! – h3ct0r Apr 18 '16 at 09:59

3 Answers3

6

... solution using strtoul but it only works for 32 bit integers and strtoull is not available.

Do it twice using strtoul(), once for the lower four bytes, once for the rest and add the two results, multiplying the latter by 0x100000000LLU beforehand.

alk
  • 69,737
  • 10
  • 105
  • 255
5

You can make your own implementation :

#include <stdio.h>
#include <stdint.h>
#include <ctype.h>

uint64_t getUInt64fromHex(char const *str)
{
    uint64_t accumulator = 0;
    for (size_t i = 0 ; isxdigit((unsigned char)str[i]) ; ++i)
    {
        char c = str[i];
        accumulator *= 16;
        if (isdigit(c)) /* '0' .. '9'*/
            accumulator += c - '0';
        else if (isupper(c)) /* 'A' .. 'F'*/
            accumulator += c - 'A' + 10;
        else /* 'a' .. 'f'*/
            accumulator += c - 'a' + 10;

    }

    return accumulator;
}

int main(void)
{
    printf("%llu\n", (long long unsigned)getUInt64fromHex("43a2be2a42380"));
    return 0;
}
Boiethios
  • 38,438
  • 19
  • 134
  • 183
  • Nitpicking: It should better be `getInt64fromHex(const char *str)`. – alk Apr 18 '16 at 10:05
  • Yes, that is better ;) – Boiethios Apr 18 '16 at 10:06
  • 1
    And perhaps a `switch` ... `case` may be faster, instead of `if`s – Boiethios Apr 18 '16 at 10:07
  • Although may work fine with arduino, code would raise at least 5 issues on a code review. – chux - Reinstate Monica Apr 18 '16 at 14:48
  • 1) `"%llu\n"` match `unsigned long long`, not necessarily `uint64_t`. 2) `const` in signature (I had old version) 3) `isxdigit(str[i])` is UB if `str[i]<0` (and not `EOF`), 4) strange to use `c >= 'A' && c <= 'F'` and not `isupper()`. (also `c >= 'a' && c <= 'f'`) 5) Does not report errors. (overflow, bad character)6) Minor: `c - 'A' + 10` relies on typically character encoding. OTOH) Good use of `size_t` although code could have simply used `str` and `str++`; – chux - Reinstate Monica Apr 18 '16 at 15:05
  • @chux This is an example, and I assumed the input was correct. OP will check the errors if he wants in his code. However : 1) On his 64 bits platform, `%llu` match `uint64_t`. 2) what do you mean ? the input is `const` in my function. 3) I will not implement another isxdigit since there is not the question here. I only provide an example. 4) I did not understand what you mean ? 5) Same answer as in 3. 6) I dare you to find a platform where ASCII code of A, B, C, D, E, F are not "contiguous". – Boiethios Apr 18 '16 at 15:16
  • @chux However, I recognize Alf's answer was better. – Boiethios Apr 18 '16 at 15:18
  • My initial comment began about working with arduino, the issues go beyond that platform. 1) %llu match `uint64_t` arduino today. Yet simple enough to use the portable C print specifier of `PRIu64` or cast operator to `unsigned long long`. 2) Your initial version lacked `const` and that what was on my screen. the `< strike >` was to show my canceling of that issue. 3) Simple enough to use `isxdigit((unsigned char) str[i])` to avoid UB. 4) Code code could use `(isupper((unsigned char)c)` instead of `(c >= 'A' && c <= 'F')` – chux - Reinstate Monica Apr 18 '16 at 15:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/109456/discussion-between-chux-and-morovaille). – chux - Reinstate Monica Apr 18 '16 at 15:31
0

And more shorter convert:

void str2hex(uint8_t * dst, uint8_t * str, uint16_t len)
{
    uint8_t byte;
    while (len) {
        byte = *str-(uint8_t)0x30;
        if (byte > (uint8_t)0x9) byte -= (uint8_t)0x7;
        *dst = byte << 4;
        str++;
        byte = *str-(uint8_t)0x30; 
        if (byte > (uint8_t)0x9) byte -= (uint8_t)0x7;
        *dst |= byte;
        str++; dst++;
        len -= (uint16_t)0x1;
    }
}

And than for get uint64_t from unsigned char buffer:

uint64_t hex2_64(uint8_t * ptr)
{
    uint64_t ret;
    ret = (uint64_t)((uint64_t)(*ptr) << (uint64_t)56); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)48); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)40); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)32); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)24); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)16); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)8); ptr++;
    ret |= (uint64_t)(*ptr);
    return ret;
}
imbearr
  • 999
  • 7
  • 22