GCC 4.7.1 マニュアルには次のように書かれています。
6.8 128 ビット整数
拡張機能として、整数スカラー型__int128
は、128 ビットを保持するのに十分な幅の整数モードを持つターゲットに対してサポートされています。__int128
符号付き 128ビット整数、または
符号なし 128 ビット整数を単に記述しunsigned __int128
ます。[ sic ] 128 ビット幅未満の整数を__int128
持つターゲットの型の整数定数を表現するための GCC のサポートはありません。long long
興味深いことに、 については言及されていませんが__uint128_t
、厳しい警告が設定されていても、そのタイプは受け入れられます。
#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]
したがって、これらの生き物を操作するのは簡単ではありません。10 進定数と 16 進定数の出力は次のとおりです。
ab54a98cdc6770b1
5678900987654321
10 進数で出力するには、値が UINT64_MAX より大きいかどうかを確認するのが最善の策です。そうであれば、UINT64_MAX よりも小さい 10 の最大乗数で除算し、その数値を出力し (このプロセスを 2 回繰り返す必要がある場合があります)、次の値よりも小さい最大の 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
。
$ 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
$
明らかに、16 進数の場合、プロセスはより簡単です。わずか 2 つの操作で、シフトとマスクと印刷ができます。8 進数の場合、64 は 3 の倍数ではないため、10 進数演算と同様の手順を実行する必要があります。
インターフェースは理想的ではprint_u128_u()
ありませんが、少なくとも印刷された文字数を返しprintf()
ます。結果を文字列バッファーにフォーマットするようにコードを適合させることは、プログラミングにおいて完全に簡単な作業ではありませんが、それほど難しくはありません。