有PRIu128,其行为类似于PRIu64来自<inttypes.h>:
printf("%" PRIu64 "\n", some_uint64_value);
Run Code Online (Sandbox Code Playgroud)
或者手动逐位转换:
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);
}
Run Code Online (Sandbox Code Playgroud)
是唯一的选择吗?
请注意,这uint128_t是我自己的typedef __uint128_t.
Jon*_*ler 30
在GCC 4.7.1手册说:
6.8 128位整数
作为扩展,整数标量类型
__int128支持具有足够宽的整数模式以保持128位的目标.只需写入__int128带符号的128位整数,或 写入unsigned __int128无符号的128位整数.在GCC中不支持表示__int128具有long long小于[ sic ] 128位宽度的整数的目标的类型的整数常量.
有趣的是,虽然没有提及,但__uint128_t即使设置了严格的警告,该类型也被接受:
#include <stdio.h>
int main(void)
{
__uint128_t u128 = 12345678900987654321;
printf("%llx\n", (unsigned long long)(u128 & 0xFFFFFFFFFFFFFFFF));
return(0);
}
Run Code Online (Sandbox Code Playgroud)
汇编:
$ 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]
$
Run Code Online (Sandbox Code Playgroud)
(这是在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]
Run Code Online (Sandbox Code Playgroud)
因此,操纵这些生物并不容易.十进制常量和十六进制常量的输出为:
ab54a98cdc6770b1
5678900987654321
Run Code Online (Sandbox Code Playgroud)
对于十进制打印,最好的办法是查看该值是否大于UINT64_MAX; 如果是,则除以小于UINT64_MAX的10的最大功率,打印该数字(并且您可能需要再次重复该过程),然后以小于10的最大功率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);
}
Run Code Online (Sandbox Code Playgroud)
那个输出是:
24197857200151252746022455506638221840
38 digits
321944928255972408260334335944939549199
39 digits
Run Code Online (Sandbox Code Playgroud)
我们可以验证使用bc:
$ 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
$
Run Code Online (Sandbox Code Playgroud)
显然,对于hex,过程更简单; 只需两次操作即可移动,遮罩和打印.对于八进制,由于64不是3的倍数,因此必须通过类似的步骤进行十进制运算.
该print_u128_u()接口并不理想,但它至少返回打印的字符数,就像printf()做.调整代码以将结果格式化为字符串缓冲区在编程中并不是一件非常简单的工作,但并不是非常困难.
Jen*_*edt 14
没有在库中不支持打印这些类型.它们甚至不是C标准意义上的扩展整数类型.
你从后面开始打印的想法很好,但你可以使用更大的块.在P99的一些测试中,我有这样一个使用的功能
uint64_t const d19 = UINT64_C(10000000000000000000);
Run Code Online (Sandbox Code Playgroud)
作为最适合10的力量uint64_t.
作为十进制,这些大数字很快就会变得难以理解,因此另一个更容易的选择是以十六进制打印它们.然后你可以做类似的事情
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);
Run Code Online (Sandbox Code Playgroud)
获得下半部分然后基本相同
uint64_t high = (x >> 64);
Run Code Online (Sandbox Code Playgroud)
对于上半部分.
我没有内置解决方案,但划分/模数很昂贵.只需移位就可以将二进制转换为十进制.
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;
}
Run Code Online (Sandbox Code Playgroud)
(但显然128位除法/模数并不像我想象的那么昂贵.在带有GCC 4.7和Clang 3.1的Phenom 9600上-O2,这似乎比OP的方法慢了2倍-3倍.)
| 归档时间: |
|
| 查看次数: |
47783 次 |
| 最近记录: |