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
?
source to share
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.
source to share
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?
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;
source to share