programing

C에서 128비트 정수 할당

cafebook 2023. 9. 20. 20:44
반응형

C에서 128비트 정수 할당

gcc 4.9.1에서 128비트 정수를 할당하려고 하면 다음과 같은 메시지가 나타납니다.warning: integer constant is too large for its type.

예제 코드

int main(void) {
  __uint128_t p = 47942806932686753431;

  return 0;
}

산출량

저는 저와 함께 편집하고 있습니다.gcc -std=c11 -o test test.c알 수 있습니다.

test.c: In function ‘main’:
test.c:2:19: warning: integer constant is too large for its type
   __uint128_t p = 47942806932686753431;
               ^

제가 뭔가 잘못하고 있는 건가요 아니면 이게 gcc의 버그인가요?

제가 뭔가 잘못하고 있는 건가요 아니면 이게 gcc의 버그인가요?

문제는 에 있습니다.47942806932686753431부분이 아닌__uint128_t p. gcc 문서에 따르면 128비트 상수를 선언할 수 있는 방법은 없습니다.

GCC에서는 128비트 너비보다 긴 긴 정수의 대상에 대해 유형 __int128의 정수 상수를 표현할 수 없습니다.

따라서 128비트 변수는 사용할 수 있지만 128비트 상수는 사용하지 않는 한 사용할 수 없습니다.long long128비트 너비입니다.

해결 방법은 기본 산술 연산을 사용하여 "더 좁은" 적분 상수에서 128 비트 값을 구성하고 컴파일러가 상수 접기를 수행하기를 희망하는 것입니다.

이거 먹어봤어요?

__int128 p = *(__int128*) "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";

편집 11월 25일

이전 게시물에 대한 해명이 잘 되지 않아 죄송합니다.진짜로 장난삼아 올린 답이 아닙니다.GCC 문서에는 128비트 정수 상수를 표현할 방법이 없다고 나와 있지만, 이 게시물은 __uint128_t 변수에 값을 쉽게 할당하려는 사람들에게 해결책을 제공할 뿐입니다.

GCC(7.2.0) 또는 Clang(5.0.0)을 사용하여 아래의 코드를 컴파일 할 수 있습니다.원하는 결과를 인쇄합니다.

#include <stdint.h>
#include <stdio.h>

int main()
{
    __uint128_t p = *(__int128*) "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
    printf("HIGH %016llx\n", (uint64_t) (p >> 64));
    printf("LOW  %016llx\n", (uint64_t) p);
    return 0;
}

stdout:

HIGH 0f0e0d0c0b0a0908
LOW  0706050403020100

이는 .rodata 섹션에 "값"을 배치하여 포인터에 장난을 치기 때문에(objdumping을 하는 경우), 휴대용이 아닙니다(x86_64 및 arch64는 괜찮지만 arm 및 x86은 아닙니다).데스크톱 컴퓨터에서 코딩하는 것만으로도 충분하다고 생각합니다.

두 개의 64비트 값을 결합하는 간단한 매크로를 제안합니다.

#define UINT128(hi, lo) (((__uint128_t) (hi)) << 64 | (lo))

사용 예시:

#include <stdint.h>
#include <stdio.h>

#define UINT128(hi, lo) (((__uint128_t) (hi)) << 64 | (lo))

int main() {
  // Creates constant 0x010203040506070890a0b0c0d0e0f10
  __uint128_t x = UINT128(0x0102030405060708, 0x090a0b0c0d0e0f10);

  // Prints 1, 2, 3, 4, .. 15, 16
  for (int i = 0; i < 16; ++i) {
    printf("%d ", (int)((x >> (120 - 8*i)) & 255));
  }
  printf("\n");
}

또는 0x 접두사를 매크로(권장되지 않음)로 이동할 수도 있습니다.

#define UINT128(hi, lo) (((__uint128_t) (0x##hi)) << 64 | (0x##lo))

할 수 있습니다.UINT128(0102030405060708, 090a0b0c0d0e0f10)약간 짧긴 하지만 구문 강조를 엉망으로 만들 수도 있습니다.0102030405060708팔분상수로 잘못 해석될 수도 있고,090a0b0c0d0e0f10그 자체로는 유효한 토큰이 아니기 때문에 이를 추천하고 싶지 않습니다.

저도 같은 문제가 있었고 사용자 정의 리터럴을 사용하여 해결책을 마련했습니다.사용자 정의 리터럴을 인스턴스화하는 방법은 다음과 같습니다._xxl:

int main(int argc, char** argv) {

    auto a = 0xF0000000000000000000000000000000LLU;
    auto b = 0xF0000000000000000000000000000000_xxl;
    
    printf("sizeof(a): %zu\n", sizeof(a));
    printf("sizeof(b): %zu\n", sizeof(b));

    printf("a == 0? %s\n", a==0 ? "true":"false");
    printf("b == 0? %s\n", b==0 ? "true":"false");

    printf("b >> 124 = %x\n", b >> 124);

    return 0;
}

출력:

sizeof(a): 8
sizeof(b): 16
a == 0? true
b == 0? false
b >> 124 = f

다음은 사용자 정의 리터럴에 대한 구현입니다._xxl

#pragma once

#include <stdint.h>

#ifdef __SIZEOF_INT128__
    using uint_xxl_t = __uint128_t;
    using sint_xxl_t = __int128_t;
    
namespace detail_xxl
{
    constexpr uint8_t hexval(char c) 
    { return c>='a' ? (10+c-'a') : c>='A' ? (10+c-'A') : c-'0'; }

    template <int BASE, uint_xxl_t V>
    constexpr uint_xxl_t lit_eval() { return V; }
    
    template <int BASE, uint_xxl_t V, char C, char... Cs>
    constexpr uint_xxl_t lit_eval() {
        static_assert( BASE!=16 || sizeof...(Cs) <=  32-1, "Literal too large for BASE=16");
        static_assert( BASE!=10 || sizeof...(Cs) <=  39-1, "Literal too large for BASE=10");
        static_assert( BASE!=8  || sizeof...(Cs) <=  44-1, "Literal too large for BASE=8");
        static_assert( BASE!=2  || sizeof...(Cs) <= 128-1, "Literal too large for BASE=2");
        return lit_eval<BASE, BASE*V + hexval(C), Cs...>();
    }
    
    template<char... Cs > struct LitEval 
    {static constexpr uint_xxl_t eval() {return lit_eval<10,0,Cs...>();} };

    template<char... Cs> struct LitEval<'0','x',Cs...> 
    {static constexpr uint_xxl_t eval() {return lit_eval<16,0,Cs...>();} };

    template<char... Cs> struct LitEval<'0','b',Cs...> 
    {static constexpr uint_xxl_t eval() {return lit_eval<2,0,Cs...>();} };

    template<char... Cs> struct LitEval<'0',Cs...> 
    {static constexpr uint_xxl_t eval() {return lit_eval<8,0,Cs...>();} };
    
    template<char... Cs> 
    constexpr uint_xxl_t operator "" _xxl() {return LitEval<Cs...>::eval();}
}

template<char... Cs> 
constexpr uint_xxl_t operator "" _xxl() {return ::detail_xxl::operator "" _xxl<Cs...>();}
    
#endif // __SIZEOF_INT128__

에 사용할 수 있습니다.constexpr일반 정수 상수와 마찬가지로:

static_assert(   0_xxl == 0, "_xxl error" );
static_assert( 0b0_xxl == 0, "_xxl error" );
static_assert(  00_xxl == 0, "_xxl error" );
static_assert( 0x0_xxl == 0, "_xxl error" );

static_assert(   1_xxl == 1, "_xxl error" );
static_assert( 0b1_xxl == 1, "_xxl error" );
static_assert(  01_xxl == 1, "_xxl error" );
static_assert( 0x1_xxl == 1, "_xxl error" );

static_assert(      2_xxl == 2, "_xxl error" );
static_assert(   0b10_xxl == 2, "_xxl error" );
static_assert(     02_xxl == 2, "_xxl error" );
static_assert(    0x2_xxl == 2, "_xxl error" );

static_assert(      9_xxl == 9, "_xxl error" );
static_assert( 0b1001_xxl == 9, "_xxl error" );
static_assert(    011_xxl == 9, "_xxl error" );
static_assert(    0x9_xxl == 9, "_xxl error" );

static_assert(     10_xxl == 10, "_xxl error" );
static_assert(    0xa_xxl == 10, "_xxl error" );
static_assert(    0xA_xxl == 10, "_xxl error" );

static_assert( 0xABCDEF_xxl == 0xABCDEF, "_xxl error" );
static_assert( 1122334455667788_xxl == 1122334455667788LLu, "_xxl error" );
static_assert(0x80000000000000000000000000000000_xxl >> 126 == 0b10, "_xxl error");
static_assert(0x80000000000000000000000000000000_xxl >> 127 == 0b01, "_xxl error");
static_assert( 0xF000000000000000B000000000000000_xxl > 0xB000000000000000, "_xxl error" );

언급URL : https://stackoverflow.com/questions/31461318/assigning-128-bit-integer-in-c

반응형