C 中 x64 的 128 位算术

发布于 2024-10-21 13:20:45 字数 429 浏览 5 评论 0原文

在 x86 上实现 bignum 时,显然数字大小最有效的选择是 32 位。但是,您需要进行最多两倍数字大小的算术运算(即 32+32=33、32*32=64、64/32=32)。幸运的是,x86 不仅提供了这一点,而且还可以通过可移植 C (uint64_t) 访问它。

同样,在 x64 上,最好使用 64 位数字。这将需要 128 位算术(即 64+64=65、64*64=128、128/64=64)。幸运的是,x64 提供了这一点。不幸的是,它不能从可移植的 C 语言访问,尽管显然人们可以深入了解汇编。

所以我的问题是它是否可以从不可移植的 C 访问。x64 上的任何 C 编译器是否提供对此的访问,如果是,语法是什么?

(请注意,我不是在谈论严格视为 32 或 64 位字的集合且它们之间没有进位传播的 128 位向量,而是在谈论实际的 128 位整数运算。)

When implementing bignums on x86, obviously the most efficient choice for digit size is 32 bits. However, you need arithmetic up to twice the digit size (i.e. 32+32=33, 32*32=64, 64/32=32). Fortunately, not only does x86 provide this, but it's also accessible from portable C (uint64_t).

Similarly, on x64 it would be desirable to use 64-bit digits. This would require 128 bit arithmetic (i.e. 64+64=65, 64*64=128, 128/64=64). Fortunately, x64 provides this. Unfortunately, it's not accessible from portable C, though obviously one could dip into assembly.

So my question is whether it's accessible from non-portable C. Do any C compilers on x64 provide access to this, and if so, what's the syntax?

(Note that I'm not talking about 128-bit vectors that are strictly treated as collections of 32 or 64-bit words with no carry propagation between them, but about actual 128-bit integer operations.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

抹茶夏天i‖ 2024-10-28 13:20:46

GCC 4.1 通过 __int128_t__uint128_t 内置类型引入了初始 128 位整数支持,但 128 位类型自 GCC 4.6__int128 / unsigned __int128

Clang 也支持这些类型,尽管我不知道从什么时候开始。 但 Godbolt (3.0.0) 上的第一个版本确实支持 __int128_t

尽管ICC 获得了支持, 自版本 13.0.0 以来支持相同:128 位整数在英特尔 C 编译器中支持 +、-、*、/ 和 %?

另请参阅


如果您使用的是 MSVC,则不直接支持 128 位类型,但有许多内在函数可以帮助您执行 128 位类型操作:

  • 64*64→128:<代码>_mul128()_umul128()__mulh()__umulh()

  • 128/64→64:_div128()_udiv128()

  • 64+64→65:通过将和的低位部分与任意操作数进行比较,可以轻松获得加法中的进位:

    struct uint128 {
        uint64_t H、L;
    };
    
    内联 uint128 add(uint128 a, uint128 b)
    {
        uint128 c;
        cL = aL + bL; // 添加低部分
        cH=aH+bH+(cL<aL); // 添加高位部分并进位
        返回c;
    }
    
  • 64-64→65:我们可以使用与加法​​中相同的减法方法< /p>

    内联 uint128 sub(uint128 a, uint128 b)
    {
        uint128 c;
        cL = aL - bL;
        cH=aH-bH-(cL>aL);
        返回c;
    }
    

还有一些用于移位的内在函数,尽管实现这些是微不足道的:__shiftleft128()__shiftright128()


如果您使用的是不受支持的编译器,则只需使用一些固定宽度类型从许多可用的库中,这会快得多。例如 ttmath:UInt<4> (128 位 int 类型,四个 32 位肢体),或 Boost.Multi precisioncalccrypto/uint128_t。像 GMP 这样的任意精度算术库对此来说成本太高。一个例子:优化故事:从 GMP 切换到 gcc 的 __int128 减少了运行时间95%

GCC 4.1 introduced initial 128-bit integer support with the __int128_t and __uint128_t built-in types but 128-bit type was officially released since GCC 4.6 as __int128 / unsigned __int128

Clang also supports those types although I don't know since when. The first version on Godbolt (3.0.0) does support __int128_t though

ICC gained the same support since version 13.0.0: 128-bit integers supporting +, -, *, /, and % in the Intel C Compiler?

See also


If you're on MSVC then there's no direct support for a 128-bit type but there are many intrinsics helping you do 128-bit operations:

  • 64*64→128: _mul128(), _umul128(), __mulh(), __umulh()

  • 128/64→64: _div128(), _udiv128()

  • 64+64→65: The carry in an addition can be easily obtained by comparing the low part of the sum with any of the operands:

    struct uint128 {
        uint64_t H, L;
    };
    
    inline uint128 add(uint128 a, uint128 b)
    {
        uint128 c;
        c.L = a.L + b.L;                // add low parts
        c.H = a.H + b.H + (c.L < a.L);  // add high parts and carry
        return c;
    }
    
  • 64-64→65: We can use the same method for subtraction as in addition

    inline uint128 sub(uint128 a, uint128 b)
    {
        uint128 c;
        c.L = a.L - b.L;
        c.H = a.H - b.H - (c.L > a.L);
        return c;
    }
    

There are also intrinsics for shifting although implementing these is trivial: __shiftleft128(), __shiftright128()


If you're on an unsupported compiler then just use some fixed-width types from many available libraries, that would be much faster. For example ttmath:UInt<4> (a 128-bit int type with four 32-bit limbs), or (u)int128_t in Boost.Multiprecision and calccrypto/uint128_t. An arbitrary-precision arithmetic library like GMP is just too costly for this. One example: Optimization story: Switching from GMP to gcc's __int128 reduced run time by 95%

情丝乱 2024-10-28 13:20:46

您可能需要检查 GNU 多精度算术库:

http://gmplib.org/

You may want to check the GNU Multiple Precision Arithmetic Library:

http://gmplib.org/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文