1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

visual studio(msvc) c++でも __int128を使いたい!

1
Last updated at Posted at 2026-02-13

visual studio(c++モード)で、__int128を使おうとすると、

C4235: 非標準の拡張機能が使用されています: '__int128' キーワードはこのアーキテクチャではサポートされていません

と怒られます。
しかしながら、visual studio c++にはよく似た機能があるらしく、次のコードで使用できるようになります。

#if defined(_MSC_VER) && !defined(__clang__)
#include <__msvc_int128.hpp>
using u128 = std::_Unsigned128;
using i128 = std::_Signed128;
#else
using u128 = unsigned __int128;
using i128 = __int128;
#endif

しかしながら、参考文献によると、std::_Unsigned128はいつ使えなくなってもおかしくない機能のようです。
これをどこかに置くと、i128u128型が利用可能になります。

また、iostreamで扱いたい場合は、独自のoperator<<を定義すればokです
(AIが書いたコードです。外部入力安全ではないかもしれません。)

static std::ostream& print_u128(std::ostream& os, u128 value) {
    if (value == 0)
        return os << '0';

    int base = 10;
    auto f = os.flags() & std::ios_base::basefield;
    if (f == std::ios_base::hex) base = 16;
    else if (f == std::ios_base::oct) base = 8;

    std::string s;
    while (value) {
        unsigned digit = static_cast<unsigned>(value % base);
        s.push_back(
            digit < 10 ? '0' + digit
                       : (os.flags() & std::ios_base::uppercase ? 'A' : 'a') + (digit - 10)
        );
        value /= base;
    }

    std::reverse(s.begin(), s.end());
    return os << s;
}

std::ostream& operator<<(std::ostream& os, u128 value) {
    return print_u128(os, value);
}

std::ostream& operator<<(std::ostream& os, i128 value) {
    if (value < 0) {
        os << '-';
        return print_u128(os, static_cast<u128>(-(value + 1)) + 1);
    }
    return print_u128(os, static_cast<u128>(value));
}

あとは煮たり焼いたりすればokです

int main() {
    u128 m = 0x5d588b656c078965;
    u128 a = 0x269ec3;
	u128 base = 0x12345;
	auto result = base * m + a;

    std::cout << "u128 test: " << result << std::endl;
    std::cout << "u128 test: " << std::hex << result << std::dec << std::endl;

	i128 val1 = -100;
	i128 val2 = 5;

	auto result2 = val1 * val2;
	std::cout << "i128 test: " << result2 << std::endl;
    
    return 0;
}

i128 test: 501545016839495783052796
i128 test: 6a34cf51dc22af5c75fc
i128 test: -500

コンパイル結果

最適化を防ぐため、noop関数のヘッタを参照している。

#include <cstdint>
#include <iostream>
#include <cstdint>

#if defined(_MSC_VER) && !defined(__clang__)
#include <__msvc_int128.hpp>
using u128 = std::_Unsigned128;
using i128 = std::_Signed128;
#else
using u128 = unsigned __int128;
using i128 = __int128;
#endif

class data {
    public:
        static u128 noop();
};

int main() {
    u128 m = data::noop();
    u128 a = data::noop();
    u128 base = data::noop();
    auto result = base * m + a;

    std::cout << static_cast<uint64_t>(result >> 64);

    return 0;
}

msvc

/O2 では、std::_Unsigned128の演算が関数呼び出しではなく、64bit整数命令を組み合わせた多倍長演算として展開されていることがわかる。

msvc.s
# License: MSVC Proprietary
# The use of this compiler is only permitted for internal evaluation purposes and is otherwise governed by the MSVC License Agreement.
# See https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/
base$ = 32
m$ = 48
a$ = 64
__$ArrayPad$ = 96
main    PROC                                            ; COMDAT
$LN89:
        sub     rsp, 120                      ; 00000078H
        mov     rax, QWORD PTR __security_cookie
        xor     rax, rsp
        mov     QWORD PTR __$ArrayPad$[rsp], rax
        lea     rcx, QWORD PTR m$[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        lea     rcx, QWORD PTR a$[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        lea     rcx, QWORD PTR base$[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        mov     rax, QWORD PTR m$[rsp]
        mul     QWORD PTR base$[rsp]
        add     rax, QWORD PTR a$[rsp]
        mov     r8, rdx
        mov     rax, QWORD PTR base$[rsp+8]
        mov     rdx, QWORD PTR m$[rsp+8]
        setb    cl
        imul    rdx, QWORD PTR base$[rsp]
        imul    rax, QWORD PTR m$[rsp]
        add     rax, r8
        add     rdx, rax
        add     cl, -1
        mov     rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
        adc     rdx, QWORD PTR a$[rsp+8]
        call    QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(unsigned __int64)
        xor     eax, eax
        mov     rcx, QWORD PTR __$ArrayPad$[rsp]
        xor     rcx, rsp
        call    __security_check_cookie
        add     rsp, 120                      ; 00000078H
        ret     0
main    ENDP

O0では、ライブラリに頼っていることがわかる。

msvc.s
$LN3:
        sub     rsp, 168                      ; 000000a8H
        mov     rax, QWORD PTR __security_cookie
        xor     rax, rsp
        mov     QWORD PTR __$ArrayPad$[rsp], rax
        lea     rcx, QWORD PTR m$[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        lea     rcx, QWORD PTR a$[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        lea     rcx, QWORD PTR base$[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        npad    1
        lea     r8, QWORD PTR m$[rsp]
        lea     rdx, QWORD PTR base$[rsp]
        lea     rcx, QWORD PTR $T2[rsp]
        call    std::_Unsigned128 std::operator*(std::_Base128 const &,std::_Base128 const &) ; std::operator*
        lea     r8, QWORD PTR a$[rsp]
        mov     rdx, rax
        lea     rcx, QWORD PTR result$[rsp]
        call    std::_Unsigned128 std::operator+(std::_Base128 const &,std::_Base128 const &) ; std::operator+
        npad    1
        mov     edx, 64                             ; 00000040H
        lea     rcx, QWORD PTR $T1[rsp]
        call    std::_Base128::_Base128<int,0>(int)       ; std::_Base128::_Base128<int,0>
        lea     r8, QWORD PTR $T1[rsp]
        lea     rdx, QWORD PTR result$[rsp]
        lea     rcx, QWORD PTR $T3[rsp]
        call    std::_Unsigned128 std::operator>>(std::_Unsigned128 const &,std::_Base128 const &) ; std::operator>>
        mov     rcx, rax
        call    std::_Base128::operator<unsigned __int64,0> unsigned __int64(void)const       ; std::_Base128::operator<unsigned __int64,0> unsigned __int64
        mov     rdx, rax
        mov     rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
        call    QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(unsigned __int64)
        npad    1
        xor     eax, eax
        mov     rcx, QWORD PTR __$ArrayPad$[rsp]
        xor     rcx, rsp
        call    __security_check_cookie
        add     rsp, 168                      ; 000000a8H
        ret     0
main    ENDP

GCC

unsigned __int128 は2つの64bitレジスタ(RDX:RAXなど)で保持され、演算は64bit命令を組み合わせた多倍長演算として展開されていることが分かる。

gcc.s
main:
        push    r13
        push    r12
        push    rbp
        push    rbx
        sub     rsp, 8
        call    data::noop()
        mov     rbx, rax
        mov     rbp, rdx
        call    data::noop()
        mov     r12, rax
        mov     r13, rdx
        call    data::noop()
        mov     edi, OFFSET FLAT:std::cout
        imul    rdx, rbx
        mov     rcx, rax
        imul    rbp, rax
        mov     rax, rbx
        add     rbp, rdx
        mul     rcx
        add     rbp, rdx
        add     rax, r12
        mov     rdx, rbp
        adc     rdx, r13
        mov     rsi, rdx
        call    std::ostream& std::ostream::_M_insert<unsigned long>(unsigned long)
        add     rsp, 8
        xor     eax, eax
        pop     rbx
        pop     rbp
        pop     r12
        pop     r13
        ret

constexpr

コンパイル時定数化も対応しているようです。

constexpr.cpp
#include <cstdint>
#include <iostream>

#if defined(_MSC_VER) && !defined(__clang__)
#include <__msvc_int128.hpp>
using u128 = std::_Unsigned128;
using i128 = std::_Signed128;
#else
using u128 = unsigned __int128;
using i128 = __int128;
#endif

constexpr u128 x = 100000000;
constexpr u128 y = x * 100000000000000000ull;


class data{
    public:
        static void test(u128 val);
};

int main() {
    data::test(y);

    return 0;
}

gcc

gcc.s
main:
        sub     rsp, 8
        mov     esi, 542101
        movabs  rdi, 1590897978359414784
        call    data::test(unsigned __int128)
        xor     eax, eax
        add     rsp, 8
        ret

msvc

msvc.s
# License: MSVC Proprietary
# The use of this compiler is only permitted for internal evaluation purposes and is otherwise governed by the MSVC License Agreement.
# See https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/
std::_Unsigned128 const y DQ 161401484a000000H      ; y
        DQ      0000000000084595H

$T1 = 32
main    PROC                                            ; COMDAT
$LN4:
        sub     rsp, 56                             ; 00000038H
        movaps  xmm0, XMMWORD PTR std::_Unsigned128 const y
        lea     rcx, QWORD PTR $T1[rsp]
        movdqa  XMMWORD PTR $T1[rsp], xmm0
        call    static void data::test(std::_Unsigned128)    ; data::test
        xor     eax, eax
        add     rsp, 56                             ; 00000038H
        ret     0
main    ENDP

AVX2モードでの差

constexpr.cpp
#include <cstdint>
#include <iostream>

#if defined(_MSC_VER) && !defined(__clang__)
#include <__msvc_int128.hpp>
using u128 = std::_Unsigned128;
using i128 = std::_Signed128;
#else
using u128 = unsigned __int128;
using i128 = __int128;
#endif

constexpr u128 x = 100000000;
constexpr u128 y = x * 100000000000000000ull;


class data{
    public:
        static void test(u128 val);
        static u128 noop();
};

int main() {
    auto z = y * data::noop() + data::noop();
    data::test(z);

    return 0;
}

gcc

-mavx2を使用してコンパイルした。ペクトル化は認められない。

gcc.s
main:
        push    rbp
        push    rbx
        sub     rsp, 8
        call    data::noop()
        mov     rbx, rax
        mov     rbp, rdx
        call    data::noop()
        mov     rdi, rdx
        mov     rsi, rax
        movabs  rdx, 1590897978359414784
        imul    rax, rbx, 542101
        imul    rbp, rdx
        add     rbp, rax
        mov     rax, rbx
        mul     rdx
        add     rbp, rdx
        add     rax, rsi
        mov     rdx, rbp
        adc     rdx, rdi
        mov     rdi, rax
        mov     rsi, rdx
        call    data::test(unsigned __int128)
        add     rsp, 8
        xor     eax, eax
        pop     rbx
        pop     rbp
        ret

msvc

/arch:AVX2を使用してコンパイルした。
XMMレジスタや VEXエンコード命令が使用されるが、128bit整数演算そのものが SIMD化されるわけではない。

msvc.s
# License: MSVC Proprietary
# The use of this compiler is only permitted for internal evaluation purposes and is otherwise governed by the MSVC License Agreement.
# See https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/
z$ = 32
$T2 = 32
$T3 = 32
$T4 = 48
$T5 = 64
__$ArrayPad$ = 80
main    PROC                                            ; COMDAT
$LN75:
        mov     QWORD PTR [rsp+8], rbx
        push    rdi
        sub     rsp, 96                             ; 00000060H
        mov     rax, QWORD PTR __security_cookie
        xor     rax, rsp
        mov     QWORD PTR __$ArrayPad$[rsp], rax
        lea     rcx, QWORD PTR $T4[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        mov     r9, 1590897978359414784             ; 161401484a000000H
        vpxor   xmm0, xmm0, xmm0
        vmovups XMMWORD PTR $T3[rsp], xmm0
        mov     r8, QWORD PTR [rax]
        mov     rcx, QWORD PTR [rax+8]
        mov     rax, r8
        mul     r9
        imul    rcx, r9
        imul    rbx, r8, 542101               ; 00084595H
        add     rcx, rdx
        mov     rdi, rax
        add     rbx, rcx
        lea     rcx, QWORD PTR $T5[rsp]
        call    static std::_Unsigned128 data::noop(void)   ; data::noop
        vpxor   xmm0, xmm0, xmm0
        vmovups XMMWORD PTR z$[rsp], xmm0
        lea     rcx, QWORD PTR $T2[rsp]
        add     rdi, QWORD PTR [rax]
        mov     QWORD PTR z$[rsp], rdi
        adc     rbx, QWORD PTR [rax+8]
        mov     QWORD PTR z$[rsp+8], rbx
        vmovups xmm0, XMMWORD PTR z$[rsp]
        vmovdqa XMMWORD PTR $T2[rsp], xmm0
        call    static void data::test(std::_Unsigned128)    ; data::test
        xor     eax, eax
        mov     rcx, QWORD PTR __$ArrayPad$[rsp]
        xor     rcx, rsp
        call    __security_check_cookie
        mov     rbx, QWORD PTR [rsp+112]
        add     rsp, 96                             ; 00000060H
        pop     rdi
        ret     0
main    ENDP
1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?