gcc中是否有128位整数?

77

我需要一个128位整数,因为我要存储两个64位数相乘的结果。在gcc 4.4及以上版本中有这样的东西吗?


3
请看:https://dev59.com/CXA75IYBdhLWcg3wYH7I - Valeri Atamaniouk
@chux:你为什么重新打开这个问题?这里的最佳答案是错误的,声称uint128_t已定义,而事实上gcc提供了unsigned __int128__uint128_t。目前只在64位目标上提供128位,只需要2个整数寄存器。 - Peter Cordes
1
@PeterCordes 我选择 VTO 是因为那两个重复的回答没有解决我的问题。我的 VTO 与任何回答都无关。 - chux - Reinstate Monica
@chux:好的,这很公平,但它不是被关闭为Does gcc support 128-bit int on amd64?的重复吗?在我看来,那看起来像是一个重复。 - Peter Cordes
@PeterCordes 这个问题被关闭是由于有两个重复的问题:question 更加狭窄,因此不是这个问题的重复 - 和另一个问题。那个答案一般涉及gcc 4.6及之前的版本,但不包括这个问题关于4.4以后的情况。当然,这些和许多其他相关的问题非常相似,并且在足够相似/不同的边缘上。 - chux - Reinstate Monica
@chux:这个问题说“或更高版本”,而gcc4.6现在已经相当古老了。(尽管我今年看到过在RHEL上使用gcc4.4输出汇编代码的答案)。无论如何,https://stackoverflow.com/posts/16088282/timeline 没有显示其他(非)重复问题。当重复关闭通过时,评论被自动删除,关闭本身似乎也没有记录重复列表。 - Peter Cordes
3个回答

62
对于 C23 之前的 GCC,原始的 128 位整数类型仅在 64 位目标上可用,因此即使您已经检测到最新的 GCC 版本,仍需要检查其可用性。理论上,gcc可能支持 TImode 整数,在这些机器上,它需要使用 4 个 32 位寄存器来保存一个整数,但我不认为有任何情况下它会这样做。
在 C++ 中,考虑使用诸如 boost::multiprecision::int128_t 这样的库,如果可用,它将使用编译器内置的宽类型,与使用自己的 typedef(例如 GCC 的 __int128 或 Clang 的 _BitInt(128))相比,具有零开销。另请参见@phuclv's answer中的另一个问题。
ISO C23将允许您使用typedef unsigned _BitInt(128) u128,这是基于 clang的功能建模的,最初称为_ExtInt(),即使在32位机器上也可以工作;请参见 简短介绍。当前的GCC -std=gnu2x甚至还不支持该语法。
GCC 4.6及更高版本中定义了__int128/unsigned __int128作为内置类型。使用#ifdef __SIZEOF_INT128__来检测它。

GCC 4.1及更高版本将__int128_t__uint128_t定义为内置类型。(这些类型不需要#include <stdint.h>,可以在Godbolt上证明。)

我第一次使用Godbolt编译器浏览器测试支持这3个功能的编译器的早期版本(在x86-64上)。Godbolt只支持gcc4.1、ICC13和clang3.0及以上版本,因此我使用“<= 4.1”来表示实际的第一次支持可能更早。请注意,保留HTML标签。
         legacy               recommended(?)    |  One way of detecting support
        __uint128_t   |  [unsigned]  __int128   |  #ifdef __SIZEOF_INT128__
gcc        <=  4.1    |       4.6               |     4.6
clang      <=  3.0    |       3.1               |     3.3
ICC        <=  13     |     <= 13               |     16.  (Godbolt doesn't have 14 or 15)

如果你编译的是32位架构,如ARM或使用-m32的x86架构,即使是最新版本的这些编译器也不支持128位整数类型。因此,如果你的代码没有这个类型的支持,就需要在使用之前进行检测,以确保代码可以正常工作。

我所知道的唯一直接检测宏是__SIZEOF_INT128__,但不幸的是,一些旧版本的编译器支持它却没有定义它。(而且没有__uint128_t的宏,只有gcc4.6风格的unsigned __int128)。如何确定是否已定义__uint128_t

有些人仍在使用像gcc4.4这样的古老编译器版本在RHEL(RedHat Enterprise Linux)或类似的老旧系统上。如果你关心那样过时的gcc版本,你可能想坚持使用__uint128_t。也许可以通过检测sizeof(void*) == 8来确定64位性,作为__SIZEOF_INT128__未定义的后备方案。 (我认为GNU系统始终具有CHAR_BIT==8,尽管我可能对某些DSP存在误解)。 这将在64位ISA上的ILP32 ABI上产生错误的负面影响(例如x86-64 Linux x32或AArch64 ILP32),但这已经是针对不定义__SIZEOF_INT128__的旧编译器用户的后备/奖励了。
可能有一些64位ISA gcc未定义__int128,甚至可能有一些32位ISA gcc定义了__int128,但我不知道。
GCC内部是整数TI模式(GCC内部手册)。(四整数=32位int的4倍宽度,双模式=DImode=双倍宽度,普通int=SImode。)正如GCC手册所指出的,在支持128位整数模式(TImode)的目标上支持__int128
// __uint128_t is pre-defined equivalently to this
typedef unsigned uint128 __attribute__ ((mode (TI)));

手册中有一个 OImode,oct-int = 32 字节,但当前的 x86-64 GCC 如果尝试使用它,则会提示“无法模拟 'OI'”。
随机事实:ICC19和g++/clang++ -E -dM 定义:
#define __GLIBCXX_TYPE_INT_N_0 __int128
#define __GLIBCXX_BITSIZE_INT_N_0 128

@MarcGlisse发表评论,指出这是告诉libstdc++如何处理额外的整数类型(重载abs、特化类型特征等)的方法。

icpc定义了即使使用-xc(编译为C而不是C ++),而g ++ -xc和clang ++ -xc则不定义。但是使用实际的icc进行编译(例如,在Godbolt语言下拉菜单中选择C而不是C ++)不会定义此宏。


测试函数为:

#include <stdint.h>   // for uint64_t

#define uint128_t __uint128_t
//#define uint128_t unsigned __int128

uint128_t mul64(uint64_t a, uint64_t b) {
    return (uint128_t)a * b;
}

编译器支持它并且能够高效地编译它。
    mov       rax, rdi
    mul       rsi
    ret                  # return in RDX:RAX which mul uses implicitly

4
unsigned __int128 - 全除法是最慢的。经常情况下(特别是在div / mod算术例程中),我们知道商将适合于64位结果,但C运行时不能假设它。 我编写了一个基于__int128的参考“modexp”(64位基数,指数,模数)......相对于使用64位intrinsic,倒数除法等版本,加速了18倍! 3倍或4倍是可观的,但请记住总是有调用开销,并且[u]int128函数无法像我们一样进行算法断言! - Brett Hale
1
@BrettHale:有趣。GCC的辅助函数可能只检查上半部分是否为零,而不是(对于无符号数)检查“除数>高半位被除数”。 - Peter Cordes
1
快速类型并不是了解架构位数的好方法。例如,在x86_64上,musl的{,u}int_fast{16,32}_t是32位,而glibc的则是64位(同样也不适合包含在API中)。 - Petr Skocik
1
@PSkocik:我不知道为什么我一开始会建议那个。我想我一直在希望找到一些甚至可以在ILP32 ABI上工作的东西,比如x86-64 Linux的x32或AArch64 ILP32,但事实并非如此。很高兴听到MUSL使其在x86-64上成为32位;这对我来说更有意义。我没有意识到它没有被ABI固定,因此不适合在API中使用。 - Peter Cordes
1
@PSkocik:64位整数有时可以在用作数组索引时节省符号扩展指令,但除此之外严格劣于32位整数。大代码大小(REX前缀),以及英特尔CPU上的更慢的div(约为2.5倍)。在Zen之前的AMD上,64位mul/imul比32位慢。此外,某些CPU上64位popcnt也较慢。(所有这些都与32位进行比较,即x86-64机器代码中的默认操作数大小,它免费进行零扩展到64位。) - Peter Cordes
显示剩余3条评论

34

啊,大整数并不是 C 语言的长项。

GCC 版本从 4.x 开始引入了 unsigned __int128/__int128 类型(不太确定),但我似乎记得在此之前有一个 __int128_t 的定义。

这些类型仅适用于 64 位目标平台。

(编者注:该回答曾声称 GCC 定义了 uint128_tint128_t 这两种类型,但我在 Godbolt 编译器浏览器上测试过的所有版本中都没有对这些类型进行定义,从 gcc4.1 到 8.2,以及 clang 和 ICC。)


1
在我使用的所有实现中,包括x86-64的GCC,long long int都是64位的。而且我相信,GCC的128位int仅适用于64位平台。 - interjay
1
Linux x86_64上的gcc 4.7.2没有[]int128_t。我猜测gcc 4.8.0可能会有它。 - Keith Thompson
4
尝试使用typedef int really_long __attribute__ ((mode (TI)));。在具有本地64位支持的架构上已经运作了很长一段时间。 - Pascal Cuoq
2
gcc-4.1及以上版本的编译器直接支持__uint128_t类型,并且还支持以下typedef:typedef unsigned uint128_t __attribute__ ((mode(TI))); - pts
GCC自4.1.2版本起就支持128位整型是否有办法在gcc <4.4上使用128位整型 - phuclv
显示剩余4条评论

17

Reed为非64位机器提供了一个完全有效的答案。这个问题是搜索“C int128”的排名第一响应,因此对于任何平台都有一个通用答案是一件好事。也许下次如果你对这个主题感到如此强烈,可以写一个答案而不是批评别人(这样你也可以获得声望的好处)。 - CrazyCasta
2
@CrazyCasta 专门为一个128位类型拥有一个任意精度库太浪费了,而且开销太大。类似于Boost.Multiprecision或calccrypto/uint128_t的定宽库将会更小、更快速。 - phuclv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接