1

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?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Dawid Szymański
  • 775
  • 6
  • 15
  • [how to print __uint128_t number using gcc?](https://stackoverflow.com/questions/11656241/how-to-print-uint128-t-number-using-gcc) – cremno Jun 27 '15 at 14:09

3 Answers3

2

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.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
1

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?

  • "`*str++ = c+0x30;`" Please use more clear values (`'0'` here). **no magic numbers**. (there might be more such flaws). Also: `goto` is not very useful here. Many coding standards forbid it. You should **not** post "bad style" code here! – too honest for this site Jun 27 '15 at 15:28
  • @Olaf, I like it. You are free to copy the code, edit the way you like the more and post your answer. BTW isn't `goto` used even in the Linux kernel? I think here helps preventing code duplication. Anyway 0x30 changed to '0'. –  Jun 27 '15 at 15:41
  • TBH this looks like an entirely reasonable use of `goto` – abligh Jun 27 '15 at 15:44
  • I do not mind if you use it. But code posted here should present some quality at least; do no forget this will be read by many beginners taking this as normal. Do you really want to fly in an airplane with code written like this? Or just think you had to maintain such code someone else wrote? – too honest for this site Jun 27 '15 at 15:48
  • @abligh: a simple `errno = X; return 0;` would be much better and have likely the same code. Note that this overwrite `errno` unconditional. Not sure if that should be if `errno` already contains an error. For using `0x30` to subtract `'0'` is plain obfuscation; nothing is gained, but renders this unusable for non-ASCII. I would not accept such code for homework/an assignment - and mostly not for using `goto` (I do not take _this_ religiously). – too honest for this site Jun 27 '15 at 15:52
  • @Olaf, what's the matter? I can add a disclaimer to my answer for beginners. This is free code, the OP can use it as a base and further improve it. Would you me to write only code with the same quality I do when i *got paid* for it? This is reasonably quality, and there is no obfuscation intended. You can copy this code and edit it and use it in your answer, please do that. I just wanted to give the OP a possible `strtoulll`. if you give him the best `strtoulll` we both win. –  Jun 27 '15 at 15:57
  • @Olaf I think errno should be overwritten as it is the *last* error. May got it wrong though. –  Jun 27 '15 at 16:00
  • A for my observations, people writing bad code do not write better _good_ code if they are paid. The code might slightly improve, but only if they are very enthusiastic. However, people writing good gode do not write bad (or just worse) code if they are not paid. Well, that is my observation at least. Just based on many years in the job. – too honest for this site Jun 27 '15 at 16:01
  • @Olaf, we can try. Should i give you my bank account coordinates? :) –  Jun 27 '15 at 16:05
  • You're also wrong. There is no requirement on this subject (actually even no need to set `errno`). It is just my intended, as it would simplify debugging if you do not just see a follow-up error. But as the stdlib might also not work this way, it is useless. – too honest for this site Jun 27 '15 at 16:06
  • Thanks, but I think we would be even, as consulting is more expensive than cleaning up that code. However, take it for free; I can write such myself. But welcome to ask the OP. – too honest for this site Jun 27 '15 at 16:08
  • @Olaf, look since I know that my code above is not very well written (I wrote it in hurry) why don't we do this: You copy the code and save it. When done, you tell me. I delete my answer. You improve the code and post it. So the OP the code and a good answer! Now I have other things coming up and I don't have time to improve it. –  Jun 27 '15 at 16:10
  • I think, **I** can just live with the bold disclaimer. :-) I still do not understand why `0x30` is more simple to write than `'0'`, etc. – too honest for this site Jun 27 '15 at 16:11
  • No one is arguing about `0x30`, @Olaf. @knm241 fixed it right after you pointed it out. – abligh Jun 27 '15 at 17:50
1

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;
Dawid Szymański
  • 775
  • 6
  • 15