gcc 在C中分配128位整数

nlejzf6q  于 2023-05-18  发布在  其他
关注(0)|答案(4)|浏览(193)

当我尝试在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中的一个bug?

9ceoxa92

9ceoxa921#

是我做错了什么,还是gcc中的一个bug?
问题出在47942806932686753431部分,而不是__uint128_t p。根据gcc docs,没有办法声明128位常量:
GCC不支持为long long整数小于128位宽的目标表示__int128类型的整数常量。
所以,看起来虽然你可以有128位 * 变量 *,但你不能有128位 * 常量 *,除非你的long long是128位宽的。
解决方法可以是使用基本的算术运算从“较窄”的整数常量构造128位值,并希望编译器执行constant folding

eoigrqb6

eoigrqb62#

你试过这个吗?

__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部分(如果你objdump它)来玩弄指针,而且它不是可移植的(x86_64和aarch64可以,但arm和x86不行)。我认为这对那些在桌面机器上编码的人来说已经足够了。

lp0sw83n

lp0sw83n3#

我也遇到了同样的问题,于是我想出了一个使用用户定义文字的解决方案。下面是如何示例化用户定义的literal _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

下面是用户定义的literal _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" );
ioekq8ef

ioekq8ef4#

我建议使用一个简单的宏来合并两个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本身并不是一个有效的标记,这就是为什么我不推荐这样做。

相关问题