Operations on hex strings in the context of integers uint128_t

The upper limit for a standard Linux library function strtoull

is 64 bits.

Is there an strtoX

x86_64 Linux implementation of the -family function that works with integers uint128_t

?

Possible prototype:

uint128_t strtoulll(const char *nptr, char **endptr, int base);

      

Also, how can you print integers uint128_t

using printf

/ sprintf

?

+1


source to share


3 answers


There is no type in the standard uint128_t

. The type __int128

is referred to as a "generic extension" only in J.5.6 , but is still not part of the official standard (and the specified type is obviously signed, not unsigned).

Note that strtoull

both the family are part of the standard, not Linux (which means "Linux" here).



So: there is no standard way to convert, neither in printf.

+2


source


As far as I know, there is no type in C11 stadium uint128_t

.
GCC has a built-in type __uint128_t

, however printf

, and strtoull

(a standard feature) offer support for this type.

You can find some bigint library for C.

I wrote a function strtoulll

that should behave like a standard function , strtoull

except for the extended return type. I also wrote a very very simple function sprint_uint128t

that prints the decimal representation of a 128 bit unsigned number to a string in reverse order. This is only because I didn't want to implement the whole printf

as a clone. However, rebuilding the string will help with trailing zeroes / spaces and other formats. Only decimal version is provided, as hex / bin can be easily emulated with the standard formatting specifier and octal ... came up with octal 128-bit numbers?

Use this code at your own risk. Also, I like the bad C code style



THIS IS A VERY BAD STYLISH CODE. STARTS DO NOT ALLOW TO GET THIS BUT OPEN WRITE IT FROM OPENING

#include <stdio.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>

__uint128_t strtoulll(const char *nptr, char **endptr, int base)
{
    int cbase = base ? base : 10;
    __uint128_t result = 0, tmp;
    int negate = 0, c, error=EINVAL;

    if (base < 0 || base > 36)
        goto error;

    while (isspace(*nptr++));
    nptr--;


    if (*nptr == '-' || *nptr == '+')
    {

        negate = *nptr^'+';
        nptr++;
    }

    if (*nptr == '0' && base == 0)
    {
        cbase = 8;
        nptr++;


        if (*nptr == 'x')
        {
            cbase = 16;
            nptr++;
        }
    }




    while (nptr)
    {
        c = *nptr-0x30;
        c = c < 10 ? c : c-7;
        c = c < 36 ? c : c-0x20;

        if (c < 0 || c >= cbase)
            goto error_setptr;

        error = 0;

        tmp = result*cbase + c;
        if ((tmp-c)/cbase != result)
        {
            error = ERANGE;
            goto error;
        }

        result = tmp;
        nptr++;
    }

    result = negate ? -result : result;

error_setptr:
    if (endptr)
        *endptr = (char*)nptr;

error:
    errno = error;
    return result;

}

void sprint_uint128(__uint128_t v, char* str)
{
    int c;
    do
    {
        c = v % 10;
        v = v / 10;
        *str++ = c+'0'; 
    }
    while (v);
    *str = 0;
}

int main()
{
    __uint128_t t;
    unsigned long long l, h;
    int e;
    char* p;
    char buf[100];  


    t = strtoulll("         340282366920938463463374607431768211455", &p, 0);
    e = errno;

    l = t & 0xffffffffffffffffULL;
    h = t >> 64;

    printf("Hex value %llx%016llx\nerr %d\nfirst unrecog char %d(%c)\nERANGE=%d, EINVAL=%d\n", 
        l, h, e, *p, *p, ERANGE, EINVAL);

    sprint_uint128(t, buf);

    printf("\n\nPrinted dec reversed %s\n", buf);

    return 0;
}

      

Of course, this is only for GCC.

Note Any idea for better checking for 128 bit overflow in C?

+1


source


After rethinking my question, I came out with this answer.

gcc info: gcc version 4.9.1 (Ubuntu 4.9.1-16ubuntu6)

Target: x86_64-linux-gnu

Compiled with: gcc -o / without flags

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>

typedef __uint128_t         uint128_t;
typedef __uint64_t          uint64_t;
typedef __uint8_t           uint8_t;

const char *hex = "ffffffffffffffff0000000000000000";

uint8_t ascii2hex( char s )
{
    uint8_t r = 0xf;

    if( (s >= 48) && (s <= 57) )
        r = s - 48;
    else if( (s >= 65) && ( s <= 70) )
        r = s - 55;
    else if( (s >= 97) && (s <= 102) )
        r = s - 87;
    return r;
}

uint128_t hex_strtoulll(const char *nptr, int len)
{
    int i;
    uint128_t r = 0, p;

    for(i = len; i >= 0; i--)
    {
        p = (uint128_t)ascii2hex(*(nptr + i));
        p = p << ((len - i) << 2);
        r |= p;
    }
    return r;
}

int printf_uint128_t(uint128_t x)
{
    return printf("%016"PRIx64"%016"PRIx64"\n",(uint64_t)(x>>64),(uint64_t)x);
}

int main(int argc, char **argv)
{
    uint128_t x = hex_strtoulll(hex, strlen(hex) - 1);
    uint128_t a = 0xffffffffffffffff;
    uint128_t b = 0xf;
    uint128_t c = a * b;

    printf_uint128_t(x);
    printf_uint128_t(c);
    printf_uint128_t(x - (c));

    return 0;
}

      

The only question in my opinion is why can't I directly load the uint128_t value? This message appears: warning: integer constant is too large for its type. Warning message true above uint128_t value 2 ^ 64 - 1.

  uint128_t x = 0xfffffffffffffffffffffffffffffff;

  sizeof(uint128_t) == 16

      

whereas this works as expected:

 uint128_t x = -1;

      

+1


source







All Articles