Visual C 中的 128 位除法内在函数

发布于 2024-12-20 12:08:46 字数 684 浏览 1 评论 0原文

我想知道Visual C++中是否真的没有128位除法内部函数?

有一个名为 _umul128() 的 64x64=128 位乘法内部函数,它与 MUL x64 汇编指令非常匹配。

当然,我假设也会有一个 128/64=64 位除法内在函数(对 DIV 指令进行建模),但令我惊讶的是 Visual C++ 和 Intel C++ 似乎都没有它,至少它是未在 intrin.h 中列出。

有人可以证实吗?我尝试在编译器可执行文件中查找函数名称,但一开始就找不到 _umul128 ,所以我想我找错了地方。

更新:至少我现在在 Visual C++ 2010 的 c1.dll 中找到了模式 umul128 (没有前导下划线)。所有其他内在函数都在它周围列出,但不幸的是没有“udiv128”或就像:(所以看来他们真的“忘记”实现它了。

澄清一下:我不仅在寻找 128 位数据类型,而且在寻找一种划分 128 位的方法C++ 中的 64 位 int 的标量 int。内在函数本机 128 位整数支持可以解决我的问题

。答案是否定的,直到 2017 年,Visual Studio 2010 中都没有 _udiv128 内在函数,但它在 Visual Studio 2019 RTM 中可用

I'm wondering if there really is no 128-bit division intrinsic function in Visual C++?

There is a 64x64=128 bit multiplication intrinsic function called _umul128(), which nicely matches the MUL x64 assembler instruction.

Naturally, I assumed there would be a 128/64=64 bit division intrinsic as well (modelling the DIV instruction), but to my amazement neither Visual C++ nor Intel C++ seem to have it, at least it's not listed in intrin.h.

Can someone confirm that? I tried grep'ing for the function names in the compiler executable files, but couldn't find _umul128 in the first place, so I guess I looked in the wrong spot.

Update: at least I have now found the pattern umul128 (without the leading underscore) in c1.dll of Visual C++ 2010. All the other intrinsics are listed around it, but unfortunately no "udiv128" or the like :( So it seems they really have "forgotten" to implement it.

To clarify: I'm not only looking for a 128-bit data type, but a way to divide a 128-bit scalar int by a 64-bit int in C++. Either an intrinsic function or native 128-bit integer support would solve my problem.

Edit: The answer is no, there is no _udiv128 intrinsic in Visual Studio 2010 up to 2017, but it is available in Visual Studio 2019 RTM

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

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

发布评论

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

评论(5

情泪▽动烟 2024-12-27 12:08:46

如果您不介意小技巧,这可能会有所帮助(仅限 64 位模式,未经测试):

#include <windows.h>
#include <stdio.h>

unsigned char udiv128Data[] =
{
  0x48, 0x89, 0xD0, // mov rax,rdx
  0x48, 0x89, 0xCA, // mov rdx,rcx
  0x49, 0xF7, 0xF0, // div r8
  0x49, 0x89, 0x11, // mov [r9],rdx
  0xC3              // ret
};

unsigned char sdiv128Data[] =
{
  0x48, 0x89, 0xD0, // mov rax,rdx
  0x48, 0x89, 0xCA, // mov rdx,rcx
  0x49, 0xF7, 0xF8, // idiv r8
  0x49, 0x89, 0x11, // mov [r9],rdx
  0xC3              // ret
};

unsigned __int64 (__fastcall *udiv128)(unsigned __int64 numhi,
                                       unsigned __int64 numlo,
                                       unsigned __int64 den,
                                       unsigned __int64* rem) =
  (unsigned __int64 (__fastcall *)(unsigned __int64,
                                   unsigned __int64,
                                   unsigned __int64,
                                   unsigned __int64*))udiv128Data;

__int64 (__fastcall *sdiv128)(__int64 numhi,
                              __int64 numlo,
                              __int64 den,
                              __int64* rem) =
  (__int64 (__fastcall *)(__int64,
                          __int64,
                          __int64,
                          __int64*))sdiv128Data;

int main(void)
{
  DWORD dummy;
  unsigned __int64 ur;
  __int64 sr;
  VirtualProtect(udiv128Data, sizeof(udiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
  VirtualProtect(sdiv128Data, sizeof(sdiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
  printf("0x00000123456789ABCDEF000000000000 / 0x0001000000000000 = 0x%llX\n",
         udiv128(0x00000123456789AB, 0xCDEF000000000000, 0x0001000000000000, &ur));
  printf("-6 / -2 = %lld\n",
         sdiv128(-1, -6, -2, &sr));
  return 0;
}

If you don't mind little hacks, this may help (64-bit mode only, not tested):

#include <windows.h>
#include <stdio.h>

unsigned char udiv128Data[] =
{
  0x48, 0x89, 0xD0, // mov rax,rdx
  0x48, 0x89, 0xCA, // mov rdx,rcx
  0x49, 0xF7, 0xF0, // div r8
  0x49, 0x89, 0x11, // mov [r9],rdx
  0xC3              // ret
};

unsigned char sdiv128Data[] =
{
  0x48, 0x89, 0xD0, // mov rax,rdx
  0x48, 0x89, 0xCA, // mov rdx,rcx
  0x49, 0xF7, 0xF8, // idiv r8
  0x49, 0x89, 0x11, // mov [r9],rdx
  0xC3              // ret
};

unsigned __int64 (__fastcall *udiv128)(unsigned __int64 numhi,
                                       unsigned __int64 numlo,
                                       unsigned __int64 den,
                                       unsigned __int64* rem) =
  (unsigned __int64 (__fastcall *)(unsigned __int64,
                                   unsigned __int64,
                                   unsigned __int64,
                                   unsigned __int64*))udiv128Data;

__int64 (__fastcall *sdiv128)(__int64 numhi,
                              __int64 numlo,
                              __int64 den,
                              __int64* rem) =
  (__int64 (__fastcall *)(__int64,
                          __int64,
                          __int64,
                          __int64*))sdiv128Data;

int main(void)
{
  DWORD dummy;
  unsigned __int64 ur;
  __int64 sr;
  VirtualProtect(udiv128Data, sizeof(udiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
  VirtualProtect(sdiv128Data, sizeof(sdiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
  printf("0x00000123456789ABCDEF000000000000 / 0x0001000000000000 = 0x%llX\n",
         udiv128(0x00000123456789AB, 0xCDEF000000000000, 0x0001000000000000, &ur));
  printf("-6 / -2 = %lld\n",
         sdiv128(-1, -6, -2, &sr));
  return 0;
}
尐偏执 2024-12-27 12:08:46

一个小改进——少一条指令

extern "C" digit64 udiv128(digit64 low, digit64 hi, digit64 divisor, digit64 *remainder);

; Arguments
; RCX       Low Digit
; RDX       High Digit
; R8        Divisor
; R9        *Remainder

; RAX       Quotient upon return

.code
udiv128 proc
    mov rax, rcx    ; Put the low digit in place (hi is already there)
    div r8      ; 128 bit divide rdx-rax/r8 = rdx remainder, rax quotient
    mov [r9], rdx   ; Save the reminder
    ret     ; Return the quotient
udiv128 endp
end

A small improvement - one less instruction

extern "C" digit64 udiv128(digit64 low, digit64 hi, digit64 divisor, digit64 *remainder);

; Arguments
; RCX       Low Digit
; RDX       High Digit
; R8        Divisor
; R9        *Remainder

; RAX       Quotient upon return

.code
udiv128 proc
    mov rax, rcx    ; Put the low digit in place (hi is already there)
    div r8      ; 128 bit divide rdx-rax/r8 = rdx remainder, rax quotient
    mov [r9], rdx   ; Save the reminder
    ret     ; Return the quotient
udiv128 endp
end
冷弦 2024-12-27 12:08:46

现在可以使用了。您可以使用_div128_udiv128

_div128 内在函数将 128 位整数除以 64 位整数。返回值保存商,内部函数通过指针参数返回余数。 _div128 是 Microsoft 特定的。

去年据说可以从“Dev16”获得,但我没有确定是哪个版本。我猜它是 VS 16.0 AKA VS2019,但 MSDN 上的文档显示它更进一步到 VS2015

It's available now. You can use _div128 and _udiv128

The _div128 intrinsic divides a 128-bit integer by a 64-bit integer. The return value holds the quotient, and the intrinsic returns the remainder through a pointer parameter. _div128 is Microsoft specific.

Last year it was said to be available from "Dev16" but I'm not sure which version is that. I guess it's VS 16.0 A.K.A VS2019, but the documentation on MSDN shows that it goes further to VS2015

贩梦商人 2024-12-27 12:08:46

我不是专家,但我发现了这一点:

http://research。 swtch.com/2008/01/division-via-multiplication.html

有趣的东西。希望有帮助。

编辑:这也很有洞察力: http://www.gamedev.net/topic /508197-x64-div-内在/

I am no expert, but I dug this up:

http://research.swtch.com/2008/01/division-via-multiplication.html

Interesting stuff. Hope it helps.

EDIT: This is insightful too: http://www.gamedev.net/topic/508197-x64-div-intrinsic/

我很OK 2024-12-27 12:08:46

感谢@alexey-frunze,它对 VS2017 进行了一些调整,并使用与 VS2019 相同的参数进行了检查:

#include <iostream>
#include <string.h>
#include <math.h>
#include <immintrin.h>
#define no_init_all
#include <windows.h>

unsigned char udiv128Data[] =
{
    0x48, 0x89, 0xD0, // mov rax,rdx
    0x48, 0x89, 0xCA, // mov rdx,rcx
    0x49, 0xF7, 0xF0, // div r8
    0x49, 0x89, 0x11, // mov [r9],rdx
    0xC3              // ret
};

unsigned char sdiv128Data[] =
{
    0x48, 0x89, 0xD0, // mov rax,rdx
    0x48, 0x89, 0xCA, // mov rdx,rcx
    0x49, 0xF7, 0xF8, // idiv r8
    0x49, 0x89, 0x11, // mov [r9],rdx
    0xC3              // ret
};

unsigned __int64(__fastcall* udiv128)(
    unsigned __int64 numhi,
    unsigned __int64 numlo,
    unsigned __int64 den,
    unsigned __int64* rem) =
    (unsigned __int64(__fastcall*)(
        unsigned __int64,
        unsigned __int64,
        unsigned __int64,
        unsigned __int64*))
        ((unsigned __int64*)udiv128Data);

__int64(__fastcall *sdiv128)(
    __int64 numhi,
    __int64 numlo,
    __int64 den,
    __int64* rem) =
    (__int64(__fastcall *)(
        __int64,
        __int64,
        __int64,
        __int64*))
        ((__int64*)sdiv128Data);

void test1()
{
    unsigned __int64 a = 0x3c95ba9e6a637e7;
    unsigned __int64 b = 0x37e739d13a6d036;
    unsigned __int64 c = 0xa6d036507ecc7a7;
    unsigned __int64 d = 0x7ecc37a70c26e68;
    unsigned __int64 e = 0x6e68ac7e5f15726;

    DWORD dummy;
    VirtualProtect(udiv128Data, sizeof(udiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
    e = udiv128(a, b, c, &d);

    printf("d = %llx, e = %llx\n", d, e);    // d = 1ed37bdf861c50, e = 5cf9ffa49b0ec9aa

}

void test2()
{
    __int64 a = 0x3c95ba9e6a637e7;
    __int64 b = 0x37e739d13a6d036;
    __int64 c = 0xa6d036507ecc7a7;
    __int64 d = 0x7ecc37a70c26e68;
    __int64 e = 0x6e68ac7e5f15726;

    DWORD dummy;
    VirtualProtect(sdiv128Data, sizeof(sdiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
    e = sdiv128(a, b, c, &d);

    printf("d = %llx, e = %llx\n", d, e);    // d = 1ed37bdf861c50, e = 5cf9ffa49b0ec9aa

}

int main()
{
    test1();
    test2();

    return 0;
}

Thanks @alexey-frunze, it worked with little tweak for VS2017, checked with same parameters with VS2019:

#include <iostream>
#include <string.h>
#include <math.h>
#include <immintrin.h>
#define no_init_all
#include <windows.h>

unsigned char udiv128Data[] =
{
    0x48, 0x89, 0xD0, // mov rax,rdx
    0x48, 0x89, 0xCA, // mov rdx,rcx
    0x49, 0xF7, 0xF0, // div r8
    0x49, 0x89, 0x11, // mov [r9],rdx
    0xC3              // ret
};

unsigned char sdiv128Data[] =
{
    0x48, 0x89, 0xD0, // mov rax,rdx
    0x48, 0x89, 0xCA, // mov rdx,rcx
    0x49, 0xF7, 0xF8, // idiv r8
    0x49, 0x89, 0x11, // mov [r9],rdx
    0xC3              // ret
};

unsigned __int64(__fastcall* udiv128)(
    unsigned __int64 numhi,
    unsigned __int64 numlo,
    unsigned __int64 den,
    unsigned __int64* rem) =
    (unsigned __int64(__fastcall*)(
        unsigned __int64,
        unsigned __int64,
        unsigned __int64,
        unsigned __int64*))
        ((unsigned __int64*)udiv128Data);

__int64(__fastcall *sdiv128)(
    __int64 numhi,
    __int64 numlo,
    __int64 den,
    __int64* rem) =
    (__int64(__fastcall *)(
        __int64,
        __int64,
        __int64,
        __int64*))
        ((__int64*)sdiv128Data);

void test1()
{
    unsigned __int64 a = 0x3c95ba9e6a637e7;
    unsigned __int64 b = 0x37e739d13a6d036;
    unsigned __int64 c = 0xa6d036507ecc7a7;
    unsigned __int64 d = 0x7ecc37a70c26e68;
    unsigned __int64 e = 0x6e68ac7e5f15726;

    DWORD dummy;
    VirtualProtect(udiv128Data, sizeof(udiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
    e = udiv128(a, b, c, &d);

    printf("d = %llx, e = %llx\n", d, e);    // d = 1ed37bdf861c50, e = 5cf9ffa49b0ec9aa

}

void test2()
{
    __int64 a = 0x3c95ba9e6a637e7;
    __int64 b = 0x37e739d13a6d036;
    __int64 c = 0xa6d036507ecc7a7;
    __int64 d = 0x7ecc37a70c26e68;
    __int64 e = 0x6e68ac7e5f15726;

    DWORD dummy;
    VirtualProtect(sdiv128Data, sizeof(sdiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
    e = sdiv128(a, b, c, &d);

    printf("d = %llx, e = %llx\n", d, e);    // d = 1ed37bdf861c50, e = 5cf9ffa49b0ec9aa

}

int main()
{
    test1();
    test2();

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