web-gelistirme-sc.com

gcc kullanarak __uint128_t sayısını nasıl yazdırabilirsiniz?

PRIu128'dan PRIu64'a benzer şekilde davranan <inttypes.h> var mı:

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

Ya da el ile rakamı basamağa çevirme:

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

tek seçenek nedir?

uint128_t, __uint128_t için kendi typedef değerimin olduğunu unutmayın.

45
jfs

Hayır, bu tür baskıları almak için kütüphanede destek yoktur. Hatta C standardı anlamında genişletilmiş tam sayı türleri bile değildir.

Yazdırmayı arkadan başlatma fikriniz iyidir, ancak çok daha büyük parçalar kullanabilirsiniz. P99 için bazı testlerde, kullanan bir fonksiyon var 

uint64_t const d19 = UINT64_C(10000000000000000000);

uint64_t 'a uyan en büyük 10 güç.

Ondalık olarak, bu büyük sayılar çok yakında okunamaz hale gelir, bu nedenle başka, daha kolay, seçenek onaltılı olarak yazdırmaktır. O zaman gibi bir şey yapabilirsin

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

alt yarısını almak ve sonra temelde aynı

  uint64_t high = (x >> 64);

üst yarı için.

14
Jens Gustedt

GCC 4.7.1 kılavuzu diyor:

6.8 128 bit tam sayılar

Bir uzantı olarak, skalar türü __int128 tamsayısı, tamsayı karakterine sahip olan hedefler için desteklenir. 128-bit tutacak kadar geniş bir mod. İmzalı bir 128-bit tamsayı için __int128 yazmanız yeterlidir, veya İmzasız bir 128 bit tam sayı için unsigned __int128. GCC'de ifade etmek için destek yoktur. [sic] .__ 'dan daha az olan __int128 tamsayılı olan hedefler için long long türünde bir tamsayı sabiti. 128 bit genişlik.

İlginçtir, bu __uint128_t'dan bahsetmese de, sıkı uyarılar ayarlanmış olsa bile, bu tür kabul edilir:

#include <stdio.h>

int main(void)
{
    __uint128_t u128 = 12345678900987654321;
    printf("%llx\n", (unsigned long long)(u128 & 0xFFFFFFFFFFFFFFFF));
    return(0);
}

Derleme:

$ 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]
$

(Bu, Mac OS X 10.7.4'teki evde derlenmiş bir GCC 4.7.1 iledir.)

Sabitini 0x12345678900987654321 olarak değiştirin ve derleyici der ki:

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

Yani, bu yaratıkları manipüle etmek kolay değil. Ondalık sabit ve onaltılık sabitler ile çıkışlar:

ab54a98cdc6770b1
5678900987654321

Ondalık basmak için en iyi bahis, değerin UINT64_MAX değerinden büyük olup olmadığını görmektir; öyleyse, o zaman UINT64_MAX'tan daha küçük olan en büyük 10 gücüne bölün, bu sayıyı yazdırın (ve işlemi ikinci kez tekrarlamanız gerekebilir), daha sonra 10 değerinden daha küçük olan kalıntı modulünü yazdır UINT64_MAX, baştaki sıfırlarla doldurmayı hatırla.

Bu gibi bir şeye yol açar:

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

Bundan çıktı:

24197857200151252746022455506638221840
38 digits
321944928255972408260334335944939549199
39 digits

bc kullanarak doğrulama yapabiliriz:

$ 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
$

Açıkçası, altıgen için işlem daha basittir; Sadece iki işlemde kaydırma ve maskeleme ve yazdırma yapabilirsiniz. Sekizli için, 64, 3'ün katı olmadığı için, ondalık basamaklara benzer adımlardan geçmek zorundasınız.

print_u128_u() arayüzü ideal değildir, ancak en azından printf() gibi yazdırılan karakter sayısını döndürür. Sonucu bir dizge tamponuna biçimlendirmek için kodun uyarlanması, programlamada tamamen önemsiz bir alıştırma değildir, ama çok da zor değildir.

28
Jonathan Leffler

Dahili bir çözümüm yok, ancak bölüm/modül pahalı. Sadece vardiyalarla ikiliyi ondalık sayıya dönüştürebilirsiniz.

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

(Fakat görünüşe göre 128-bit bölme/modül düşündüğüm kadar pahalı değil. -O2 konumunda GCC 4.7 ve Clang 3.1 içeren bir Phenom 9600'de, bu OP'nin yönteminden 2x-3x daha yavaş çalışıyor gibi görünüyor.)

5
ephemient

Bence print_uint128 fonksiyonunuz oldukça karmaşık.

Bu yazması ve çalıştırması daha kolay değil mi?

void print_uint128(uint128_t n)
{
    if (n == 0) {
      return;
    }

    print_uint128(n/10);
    putchar(n%10+0x30);
}
3
abelenky

Sebastian'ın cevabına dayanarak, bu konu güvenli değil, g ++ imzalı int128 içindir.

// 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;
} 
3
mosh

Bu basit makroyu kullanabilirsiniz:

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

Abelenky'nin yukarıdaki cevabını vererek, bununla karşılaştım. 

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

Bu amaçlandığı gibi çalışıyor gibi görünüyor.

1
Perkins

gcc kullanarak __uint128_t sayısını nasıl yazdırabilirsiniz?
PRIu64'e benzer davranış gösteren PRIu128 var mı:

Hayır. ondalık içinde yazdırmak yerine, bir dizeye yazdırın. 

Gerekli dize arabelleği boyutu x değeri başına işi yapmak için yeterli. 

typedef signed __int128 int128_t;
typedef unsigned __int128 uint128_t;

// Return pointer to the end
static char *uint128toa_helper(char *dest, uint128_t x) {
  if (x >= 10) {
    dest = uint128toa_helper(dest, x / 10);
  }
  *dest = (char) (x % 10 + '0');
  return ++dest;
}

char *int128toa(char *dest, int128_t x) {
  if (x < 0) {
    *dest = '-';
    *uint128toa_helper(dest + 1, (uint128_t) (-1 - x) + 1) = '\0';
  } else {
    *uint128toa_helper(dest, (uint128_t) x) = '\0';
  }
  return dest;
}

char *uint128toa(char *dest, uint128_t x) {
  *uint128toa_helper(dest, x) = '\0';
  return dest;
}

Ölçek. En kötü durum tampon boyutu: 41.

int main(void) {
  char buf[41];
  puts("1234567890123456789012345678901234567890");
  puts(uint128toa(buf, 0));
  puts(uint128toa(buf, 1));
  puts(uint128toa(buf, (uint128_t) -1));
  int128_t mx = ((uint128_t) -1) / 2;
  puts(int128toa(buf, -mx - 1));
  puts(int128toa(buf, -mx));
  puts(int128toa(buf, -1));
  puts(int128toa(buf, 0));
  puts(int128toa(buf, 1));
  puts(int128toa(buf, mx));
  return 0;
}

Çıktı

1234567890123456789012345678901234567890
0
1
340282366920938463463374607431768211455
-170141183460469231731687303715884105728
-170141183460469231731687303715884105727
-1
0
1
170141183460469231731687303715884105727
0
chux

Bu C++ için ama ben burada bırakacağım çünkü imzasız 128-bit ints için bu sorunun C++ sürümünü bulamadım.

Bir uint128'i base-10 dizgisine dönüştürmek için basit ve okunabilir bir yol (daha sonra istediğiniz şeyi yazdırabilir veya yapabilirsiniz):

std::string toString(__uint128_t num) {
    std::string str;
    do {
        int digit = num % 10;
        str = std::to_string(digit) + str;
        num = (num - digit) / 10;
    } while (num != 0);
    return str;
}

Gerekirse, rakamları her seferinde bir tane yerine daha büyük boyutlarda toplayarak birkaç kat daha hızlı hale getirebiliriz. Ancak, her bir öbeği kaybolan baştaki sıfırlar için kontrol etmemizi ve bunları tekrar eklememizi gerektirir:

std::string toString(__uint128_t num) {
    auto tenPow19 = 10000000000000000000;
    std::string str;
    do {
        uint64_t digits = num % tenPow19;
        auto digitsStr = std::to_string(digits);
        auto leading0s = (digits != num) ? std::string(19 - digitsStr.length(), '0') : "";
        str = leading0s + digitsStr + str;
        num = (num - digits) / tenPow19;
    } while (num != 0);
    return str;
}
0
Gumby The Green

C++ değişkeni. Fonksiyonun özel C-sürümünü elde etmek için bunu bir şablon olarak kullanabilirsiniz:

template< typename I >
void print_uint(I value)
{
    static_assert(std::is_unsigned< I >::value, "!");
    if (value == 0) {
        putchar_unlocked('0');
        return;
    }
    I rev = value;
    I count = 0;
    while ((rev % 10) == 0) {
        ++count;
        rev /= 10;
    }
    rev = 0;
    while (value != 0) {
        rev = (rev * 10) + (value % 10);
        value /= 10;
    }
    while (rev != 0) {
        putchar_unlocked('0' + (rev % 10));
        rev /= 10;
    }
    while (0 != count) {
        --count;
        putchar_unlocked('0');
    }
}
0
Orient

İşte, 0'dan UINT128_MAX'a kadar destekleyen Leffler'in yanıtının değiştirilmiş bir sürümü

/*      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;
}

Ve şunu dene:

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