programing

gcc를 사용하여 __uint128_t 번호를 인쇄하는 방법

randomtip 2022. 8. 17. 20:49
반응형

gcc를 사용하여 __uint128_t 번호를 인쇄하는 방법

그곳 있습니까 거기에.PRIu128는행동을 같은과 비슷한 행동 하는PRIu64부터에서<inttypes.h>:

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

유일한 선택지인가?

참고 주의:uint128_t에대한 저만의 형식입니다를 위해 혼자서 typedef 있다.__uint128_t.

아니요, 라이브러리에서 이러한 유형의 인쇄를 지원하지 않습니다.C 표준에서는 확장 정수형도 아닙니다.

뒷면부터 인쇄를 시작하는 것은 좋지만, 더 큰 덩어리가 필요할 수도 있습니다.P99의 일부 테스트에서는 다음과 같은 기능을 사용합니다.

uint64_t const d19 = UINT64_C(10000000000000000000);

10에10의 최대제곱으로서에 맞는 가장 큰 강대국으로서.uint64_t.

소수점 이하에서는, 이러한 큰 숫자는 곧바로 판독할 수 없게 되기 때문에, 보다 간단하게 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);

윗부분을 위해서.

GCC 4.7.1 매뉴얼에는 다음과 같이 기재되어 있습니다.

6.8 128비트 정수

연장선상으로 정수 스칼라 타입 확장으로서정수 스칼라 타입.__int128목표 정수 모드 충분히 128비트를 개최할 넓이가에 지원된다.는 128비트를 유지할 수 있는 충분한 폭의 정수 모드를 가진 타깃으로 지원됩니다.단순히 간단하게 쓰기 쓰__int128들어 서명된 128비트 정수, 또는 정수의경우 또는 있는 128비트 부호.unsigned __int128부호 없는 128비트 정수.부호 없는 128비트정수의 경우.에는 GCC에 지원 GCC에서는 유형의 정수 정수를 나타내는 것을 지원하지 않습니다 형식의 정수 상수를 표현하는 것이다.__int128우리가 목표 를 가진 타겟에 대해서long long128비트보다 작은 정수입니다.

흥미롭게도, 비록 그것이 다음 사항을 언급하지는 않지만__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의 최대제곱으로 나누어 그 숫자를 인쇄합니다(이 과정을 두 번 반복해야 할 수도 있습니다).그리고 UINT64_MAX보다 작은 10의 최대제곱을 인쇄합니다.선두에 0을 붙입니다.

그 결과 다음과 같은 결과가 초래됩니다.

#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진수에서는 프로세스가 더 단순합니다. 단 두 번의 작업으로 이동 및 마스크 및 인쇄가 가능합니다.8진수에서는 64는 3의 배수가 아니기 때문에 10진수 연산과 유사한 단계를 거쳐야 합니다.

print_u128_u() 인쇄된 문자 됩니다.printf()결과를 문자열 버퍼로 포맷하기 위해 코드를 조정하는 것은 프로그래밍에서 완전히 단순한 연습은 아니지만 매우 어려운 것은 아닙니다.

내장된 솔루션은 없지만 분할/계수는 비쌉니다.이동만으로 이진수를 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비트 나눗셈/모듈러스는 생각보다 비싸지 않은 것 같습니다. 4 및 GCC 4.7의 3. "Clang 3.1" "Phenom 9600"-O2OP 2-3번입니다.)

gcc를 사용하여 하는 방법_uint128_t 입니다.
PRIu64 의와하는 PRIu128 이 ?

아니요. 대신 10진수로 인쇄하려면 문자열로 인쇄하십시오.

는 이 합니다.x

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

테스트 최악의 경우 버퍼 크기: 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;
}

산출량

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

부호 없는 64/128비트 숫자를 십진법으로 인쇄하고 싶었고 바퀴를 다시 만들고 싶지 않았습니다.따라서 "pu128()"에는 <10^19, <10^38, 그 이외의 경우)의 3가지 케이스가 있습니다.아마 가장 빠르지는 않겠지만, 휴대할 수 있어야 합니다.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("");
}
$ 

위의 아벨렌키의 대답에 따라, 나는 이것을 생각해냈다.

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

의도한 대로 작동하는 것 같습니다.

Sebastian의 답변에 따르면, 이것은 스레드 세이프가 아닌 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;
} 

__int128_t를 사용하는 경우 연산자 cin과 cout을 재정의할 수 있습니다.__int128_t는 문자열 및 cin/cout 문자열로만 변환해야 합니다.

typedef __int128_t lint;

istream& operator >> (istream &in, lint &x) {
    string s;
    in >> s;
    for (lint i = s.size() - 1, p = 1; i >= 0; i--, p *= 10) x += p * (s[i] - '0');
    return in;
}

ostream& operator << (ostream &out, lint x) {
    string s;
    while (x > 0) {
        s.push_back(x % 10 + '0');
        x /= 10;
    }
    reverse(s.begin(), s.end());
    out << s;
    return out;
}

3번과 거의 같다

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

printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);

이것은 C++용이지만, 부호 없는 128비트 ints에 대한 이 질문의 C++ 버전을 찾지 못했기 때문에 여기에 남겨두겠습니다.

다음은 uint128을 Base-10 문자열로 변환하는 간단하고 읽기 쉬운 방법입니다(이 문자열은 인쇄 또는 원하는 작업을 수행할 수 있습니다).

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

필요한 경우 한 번에 하나씩이 아닌 더 큰 청크로 숫자를 가져오면 몇 배 더 빠르게 만들 수 있습니다.단, 각 청크에서 선두 0이 손실되지 않았는지 확인하고 다시 추가해야 합니다.

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

다음의 간단한 매크로를 사용할 수 있습니다.

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

이것은 0 ~ UINT128_을 지원하는 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...

C++ 배리언트이 기능을 템플릿으로 사용하여 특정 C 버전을 생성할 수 있습니다.

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

이전 답변에서는 "printf()"를 기반으로 128비트 숫자를 인쇄하는 방법을 보여드렸습니다.

256비트 부호 없는 정수형 uint256_t를 다음과 같이 구현했습니다.

typedef __uint128_t uint256_t[2];

필요한 조작을 실장했습니다.예를 들어 __uint128_t를 인수로 하고 그 결과 uint256_t를 계산합니다.

uint256_t에 대해 16진 인쇄를 했는데, 10진 인쇄를 원하게 되었습니다.그러나 현재 uint256_t에는 "mod_256()"만 있고 "div()"는 없기 때문에 많은 답변에서 볼 수 있는 "n/=10"은 옵션이 아닙니다.동작하는 (느린) 솔루션을 찾았습니다.또, 인쇄는 시간 외에만 사용하고 있기 때문에, 이것은 허용됩니다.코드는 다음 GIST에서 찾을 수 있습니다(컴파일 명령 세부 정보 포함).
https://gist.github.com/Hermann-SW/83c8ab9e10a0bb64d770af543ed08445

arg를 사용하여 sqr.cpp를 실행하면 UINT256_MAX만 출력되고 종료됩니다.

if (argc>1)  { pu256(UINT256_MAX); puts(""); return 0; }

$ ./sqr 1
115792089237316195423570985008687907853269984665640564039457584007913129639935
$

가장 까다로운 부분은 사용된 최대 자리까지 올라가 첫 번째 자리를 빼고 출력하기 위한 재귀 호출이었습니다.재귀가 나머지를 처리한다."pu256()" 함수는 10 "mul10()"에 의한 고속 곱셈을 사용했습니다.

...
void mul10(uint256_t d, uint256_t x)
{
  uint256_t t = { x[0], x[1] };
  shl_256(t, 2);
  add_256(d, x, t);
  shl_256(d, 1);
}

const uint256_t UINT256_MAX_10th = UINT256( UINT128(0x1999999999999999, 0x9999999999999999), UINT128(0x9999999999999999, 0x999999999999999A) );

void pu256_(uint256_t v, uint256_t t, const uint256_t o)
{
  if (!lt_256(v, t) && le_256(o, UINT256_MAX_10th))
  {
    uint256_t nt, no = { t[0], t[1] };
    mul10(nt, t);
    pu256_(v, nt, no);
  }
  char d = '0';
  while (le_256(o, v))
  {
    sub_256(v, v, o);
    ++d;
  }
  putchar(d);
}

void pu256(const uint256_t u)
{
  if ((u[1]==0) && (u[0]==0))  putchar('0');
  else
  {
    uint256_t v = { u[0], u[1] }, t = UINT256( 0, 10 ), o = UINT256( 0, 1 );
    pu256_(v, t, o);
  }
}
...

전술한 바와 같이 이 접근방식은 정수형 결측 나눗셈 연산에 대해서만 의미가 있습니다.

언급URL : https://stackoverflow.com/questions/11656241/how-to-print-uint128-t-number-using-gcc

반응형