64

fromPRIu128と同様に動作するものはありますか:PRIu64<inttypes.h>

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

または、手動で1桁ずつ変換します。

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);
}

唯一のオプションはありますか?

uint128_tこれは私自身のtypedefであることに注意してください__uint128_t

4

14 に答える 14

35

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()ます。結果を文字列バッファーにフォーマットするようにコードを適合させることは、プログラミングにおいて完全に簡単な作業ではありませんが、それほど難しくはありません。

于 2012-07-26T00:23:22.687 に答える
22

いいえ、これらの型を印刷するためのライブラリはサポートされていません。C 標準の意味での拡張整数型でさえありません。

後ろから印刷を開始するというあなたのアイデアは良いものですが、もっと大きなチャンクを使用することもできます. P99のいくつかのテストでは、

uint64_t const d19 = UINT64_C(10000000000000000000);

に収まる最大の 10 のべき乗としてuint64_t

これらの大きな数字は 10 進数としてすぐに読み取れなくなるため、別のより簡単なオプションは、それらを 16 進数で出力することです。次に、次のようなことができます

  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);

上半分用。

于 2012-07-25T21:21:22.327 に答える
6

組み込みのソリューションはありませんが、除算/モジュラスは高価です。シフトだけで2進数を10進数に変換できます。

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 では-O2、これは OP の方法よりも 2 倍から 3 倍遅く実行されるようです。)

于 2012-07-25T22:18:19.797 に答える
2

上記の 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;
}

これは意図したとおりに機能するようです。

于 2014-03-15T04:18:47.693 に答える
1

符号なしの 64/128 ビット数値を 10 進数で出力したかったので、車輪の再発明はしたくありませんでした。したがって、「pu128()」には 3 つのケースがあります: <10^19、<10^38、それ以外。おそらく最速ではありませんが、移植可能でなければなりません。UINT128_MAX および UINT128_C マクロを定義します。

$ gcc -Wall -Wextra -pedantic lu.c
$ ./a.out 
0
10000000000000000000
18446744073709551615
0
10000000000000000000
18446744073709551615
100000000000000000000000000000000000000
340282366920938463463374607431768211455
$ 
$ cat lu.c 
#include <stdio.h>
#include <inttypes.h>

#define UINT128_C(u)     ((__uint128_t)u)

void pu64(__uint64_t u)   { printf("%" PRIu64, u); }
void pu640(__uint64_t u)  { printf("%019" PRIu64, u); }

#define D19_ UINT64_C(10000000000000000000)
const __uint128_t d19_ = D19_;
const __uint128_t d38_ = UINT128_C(D19_)*D19_;

const __uint128_t UINT128_MAX = UINT128_C(UINT64_MAX)<<64 | UINT64_MAX;

void pu128(__uint128_t u)
{
       if (u < d19_) pu64(u);
  else if (u < d38_) { pu64(u/d19_); pu640(u%d19_); }
  else               { pu64(u/d38_); u%=d38_; pu640(u/d19_); pu640(u%d19_); }
}

int main()
{
  pu64(0); puts("");
  pu64(d19_); puts("");
  pu64(UINT64_MAX); puts("");

  pu128(0); puts("");
  pu128(d19_); puts("");
  pu128(UINT64_MAX); puts("");
  pu128(d38_); puts("");
  pu128(UINT128_MAX); puts("");
}
$ 
于 2021-06-05T00:54:27.187 に答える
0

0 から UINT128_MAX までをサポートする Leffler の回答の修正版を次に示します。

/*      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...
于 2017-01-16T06:05:35.727 に答える
-2

#3によく似ています

unsigned __int128 g = ...........;

printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);
于 2017-12-13T15:35:43.863 に答える