如何使用gcc打印__uint128_t数字?

PRIu128行为与<inttypes.h> PRIu64类似:

 printf("%" PRIu64 "\n", some_uint64_value); 

或者手动逐位转换:

 int print_uint128(uint128_t n) { if (n == 0) return printf("0\n"); char str[40] = {0}; // log10(1 << 128) + '\0' char *s = str + sizeof(str) - 1; // start at the end while (n != 0) { if (s == str) return -1; // never happens *--s = "0123456789"[n % 10]; // save last digit n /= 10; // drop it } return printf("%s\n", s); } 

是唯一的select?

请注意, uint128_tuint128_t的我自己的typedef。

没有在图书馆没有支持打印这些types。 它们甚至不是C标准意义上的扩展整型。

你从后面开始打印的想法是好的,但你可以使用更大的块。 在P99的一些testing中,我有这样一个使用的function

 uint64_t const d19 = UINT64_C(10000000000000000000); 

作为适合uint64_t的10的最大功率。

作为十进制,这些大数字很快就不可读,所以另一个更简单的select是以hex打印它们。 那么你可以做一些类似的事情

  uint64_t low = (uint64_t)x; // This is UINT64_MAX, the largest number in 64 bit // so the longest string that the lower half can occupy char buf[] = { "18446744073709551615" }; sprintf(buf, "%" PRIX64, low); 

得到下半部分,然后基本相同

  uint64_t high = (x >> 64); 

为上半部分。

GCC 4.7.1手册说:

6.8 128位整数

作为扩展,整数标量types__int128被支持用于具有足够的128位整数模式的目标。 只需将__int128写为一个有符号的128位整数,或者将unsigned __int128为一个无符号的128位整数。 在GCC中,对于具有long long小于128位宽度的long long整数的目标expression__int128types的整数常量是不支持的。

有趣的是,虽然没有提及__uint128_t ,但是即使在设置了严格警告的__uint128_t ,该types也被接受:

 #include <stdio.h> int main(void) { __uint128_t u128 = 12345678900987654321; printf("%llx\n", (unsigned long long)(u128 & 0xFFFFFFFFFFFFFFFF)); return(0); } 

汇编:

 $ gcc -O3 -g -std=c99 -Wall -Wextra -pedantic xxx.c -o xxx xxx.c: In function 'main': xxx.c:6:24: warning: integer constant is so large that it is unsigned [enabled by default] $ 

(这是在Mac OS X 10.7.4上的一个家庭编译的GCC 4.7.1。)

将常量改为0x12345678900987654321 ,编译器说:

 xxx.c: In function 'main': xxx.c:6:24: warning: integer constant is too large for its type [enabled by default] 

所以,操纵这些生物是不容易的。 具有十进制常量和hex常量的输出是:

 ab54a98cdc6770b1 5678900987654321 

对于以十进制打印,最好的办法是看看该值是否大于UINT64_MAX; 如果是,那么除以10的最大功率,即小于UINT64_MAX,打印该数字(并且可能需要再次重复该过程),然后打印剩余模数为10的最大幂的小于UINT64_MAX,记住填充前导零。

这导致类似于:

 #include <stdio.h> #include <inttypes.h> /* ** Using documented GCC type unsigned __int128 instead of undocumented ** obsolescent typedef name __uint128_t. Works with GCC 4.7.1 but not ** GCC 4.1.2 (but __uint128_t works with GCC 4.1.2) on Mac OS X 10.7.4. */ typedef unsigned __int128 uint128_t; /* UINT64_MAX 18446744073709551615ULL */ #define P10_UINT64 10000000000000000000ULL /* 19 zeroes */ #define E10_UINT64 19 #define STRINGIZER(x) # x #define TO_STRING(x) STRINGIZER(x) static int print_u128_u(uint128_t u128) { int rc; if (u128 > UINT64_MAX) { uint128_t leading = u128 / P10_UINT64; uint64_t trailing = u128 % P10_UINT64; rc = print_u128_u(leading); rc += printf("%." TO_STRING(E10_UINT64) PRIu64, trailing); } else { uint64_t u64 = u128; rc = printf("%" PRIu64, u64); } return rc; } int main(void) { uint128_t u128a = ((uint128_t)UINT64_MAX + 1) * 0x1234567890ABCDEFULL + 0xFEDCBA9876543210ULL; uint128_t u128b = ((uint128_t)UINT64_MAX + 1) * 0xF234567890ABCDEFULL + 0x1EDCBA987654320FULL; int ndigits = print_u128_u(u128a); printf("\n%d digits\n", ndigits); ndigits = print_u128_u(u128b); printf("\n%d digits\n", ndigits); return(0); } 

从这个输出是:

 24197857200151252746022455506638221840 38 digits 321944928255972408260334335944939549199 39 digits 

我们可以使用bc来validation:

 $ bc bc 1.06 Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. ibase = 16 1234567890ABCDEFFEDCBA9876543210 24197857200151252746022455506638221840 F234567890ABCDEF1EDCBA987654320F 321944928255972408260334335944939549199 quit $ 

显然,对于hex,这个过程更简单。 你可以在两个操作中移动和掩码和打印。 对于八进制,由于64不是3的倍数,所以你必须通过类似的步骤来进行小数运算。

print_u128_u()接口并不理想,但它至less返回打印的字符数,就像printf()一样。 调整代码以将结果格式化为string缓冲区在编程中并不是完全无关紧要的,但不是非常困难。

我没有内置的解决scheme,但分割/模数是昂贵的。 您可以将二进制转换为十进制,只需移位。

 static char *qtoa(uint128_t n) { static char buf[40]; unsigned int i, j, m = 39; memset(buf, 0, 40); for (i = 128; i-- > 0;) { int carry = !!(n & ((uint128_t)1 << i)); for (j = 39; j-- > m + 1 || carry;) { int d = 2 * buf[j] + carry; carry = d > 9; buf[j] = carry ? d - 10 : d; } m = j; } for (i = 0; i < 38; i++) { if (buf[i]) { break; } } for (j = i; j < 39; j++) { buf[j] += '0'; } return buf + i; } 

(但显然128位的分割/模量并不像我想象的那么昂贵,在一台配备GCC 4.7和Clang 3.1的Phenom 9600上,这似乎比OP的方法慢了2倍到3倍。)

你可以使用这个简单的macros:

 typedef __int128_t int128 ; typedef __uint128_t uint128 ; uint128 x = (uint128) 123; printf("__int128 max %016"PRIx64"%016"PRIx64"\n",(uint64)(x>>64),(uint64)x); 

我认为你的print_uint128函数非常复杂。

这不是一个简单的写和运行?

 void print_uint128(uint128_t n) { if (n == 0) { return; } print_uint128(n/10); putchar(n%10+0x30); } 

基于塞巴斯蒂安的答案,这是用g ++签名的int128,不是线程安全的。

 // g++ -Wall fact128.c && a.exe // 35! overflows 128bits #include <stdio.h> char * sprintf_int128( __int128_t n ) { static char str[41] = { 0 }; // sign + log10(2**128) + '\0' char *s = str + sizeof( str ) - 1; // start at the end bool neg = n < 0; if( neg ) n = -n; do { *--s = "0123456789"[n % 10]; // save last digit n /= 10; // drop it } while ( n ); if( neg ) *--s = '-'; return s; } __int128_t factorial( __int128_t i ) { return i < 2 ? i : i * factorial( i - 1 ); } int main( ) { for( int i = 0; i < 35; i++ ) printf( "fact(%d)=%s\n", i, sprintf_int128( factorial( i ) ) ); return 0; } 

在abelenky的回答上面,我提出了这个问题。

 void uint128_to_str_iter(uint128_t n, char *out,int firstiter){ static int offset=0; if (firstiter){ offset=0; } if (n == 0) { return; } uint128_to_str_iter(n/10,out,0); out[offset++]=n%10+0x30; } char* uint128_to_str(uint128_t n){ char *out=calloc(sizeof(char),40); uint128_to_str_iter(n, out, 1); return out; } 

这似乎按预期工作。

这里是Leffler答案的一个修改版本,支持从0到UINT128_MAX

 /* UINT64_MAX 18446744073709551615ULL */ #define P10_UINT64 10000000000000000000ULL /* 19 zeroes */ #define E10_UINT64 19 #define STRINGIZER(x) # x #define TO_STRING(x) STRINGIZER(x) int print_uint128_decimal(__uint128_t big) { size_t rc = 0; size_t i = 0; if (big >> 64) { char buf[40]; while (big / P10_UINT64) { rc += sprintf(buf + E10_UINT64 * i, "%." TO_STRING(E10_UINT64) PRIu64, (uint64_t)(big % P10_UINT64)); ++i; big /= P10_UINT64; } rc += printf("%" PRIu64, (uint64_t)big); while (i--) { fwrite(buf + E10_UINT64 * i, sizeof(char), E10_UINT64, stdout); } } else { rc += printf("%" PRIu64, (uint64_t)big); } return rc; } 

并试试这个:

 print_uint128_decimal(-1); // Assuming -1's complement being 0xFFFFF...