问题
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