Operations on hexadecimal strings in context of uint128_t integers

老子叫甜甜 提交于 2019-12-12 09:25:02

问题


The upper limit for standard Linux library function strtoull is 64 bits.

Does there exist an implementation for strtoX-family function for Linux x86_64 environment that operates on uint128_t integers?

Possible prototype:

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

Also, how can you print uint128_t integers using printf/sprintf?


回答1:


There is no type uint128_t as of the standard. A type __int128 is mentioned as "common extension" only in J.5.6, but still not part of the official standard (and the type listed is apparently signed, not unsigned).

Note that strtoull & family are part of the standard, not Linux (whatever "Linux" means here).

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




回答2:


As far as I know there is no uint128_t type in the C11 stardard.
GCC has a built in type __uint128_t, however nor printf nor strtoull (being standard functions) offer support for this type.

You may look for some bigint library for C.

I have wrote a strtoulll function that should behave like to standard strtoull except for the widened return type. I have also write a very very very simple sprint_uint128t function that print the decimal representation of a 128 bit unsigned number to a string in the reversed order. This is just because I didn't feel like implementing a whole printf like clone. Having the string reversed will help with trailing zeros/spaces and other formatting things however.
Only the decimal version is provided as hex/bin can be emulated easy with standard formatting specifier and octal... came on octal 128 bit numbers?

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

THIS IS VERY BAD STYLED CODE. BEGINNERS SHOULD NOT COPY PASTE IT BUT RATHER WRITE IT ENTIRELY FROM SCRATCH

#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 for GCC only.

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




回答3:


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 that is on my mind is that why i can't load uint128_t value directly ? This message appears : warning: integer constant is too large for its type. The warning message is true above uint128_t value of 2^64 - 1.

  uint128_t x = 0xfffffffffffffffffffffffffffffff;

  sizeof(uint128_t) == 16

whereas this works as expected:

 uint128_t x = -1;


来源:https://stackoverflow.com/questions/31089069/operations-on-hexadecimal-strings-in-context-of-uint128-t-integers

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!