128-битное деление, присущее Visual C++
Мне интересно, нет ли в Visual C++ встроенной функции 128-битного деления?
Существует встроенная функция умножения 64x64=128 битов, называемая _umul128(), которая хорошо соответствует инструкции ассемблера MUL x64.
Естественно, я предполагал, что будет также встроенное 128/64=64-битное деление (моделирование инструкции DIV), но, к моему изумлению, ни Visual C++, ни Intel C++, похоже, не имеют его, по крайней мере, его нет в intrin.h.
Кто-нибудь может это подтвердить? Я попробовал grep'ing для имен функций в исполняемых файлах компилятора, но не смог найти _umul128, поэтому, думаю, я посмотрел не в ту точку.
Обновление: по крайней мере, теперь я нашел шаблон "umul128" (без начального подчеркивания) в c1.dll Visual C++ 2010. Вокруг него перечислены все другие встроенные функции, но, к сожалению, нет "udiv128" или тому подобного:(кажется, они действительно "забыли" реализовать это.
Для пояснения: я ищу не только 128-битный тип данных, но и способ делить 128-битное скалярное int на 64-битное int в C++. Либо встроенная функция, либо собственная поддержка 128-битного целого числа решит мою проблему.
Редактировать: ответ - нет, в Visual Studio 2010 или 2012 нет встроенного _udiv128.
5 ответов
I am no expert, but I dug this up:
http://research.swtch.com/2008/01/division-via-multiplication.html
Интересные вещи. Надеюсь, поможет.
EDIT: This is insightful too: http://www.gamedev.net/topic/508197-x64-div-intrinsic/
Если вы не возражаете против небольших взломов, это может помочь (только в 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;
}
Небольшое улучшение - на одну инструкцию меньше
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
Это доступно сейчас. Вы можете использовать _div128
а также _udiv128
В
_div128
intrinsic делит 128-битное целое число на 64-битное целое число. Возвращаемое значение содержит частное, а внутренняя функция возвращает остаток через параметр указателя._div128
специфичен для Microsoft.
В прошлом году было сказано, что он будет доступен с "Dev16", но я не уверен, какая это версия. Я предполагаю, что это VS 16.0 AKA VS2019, но документация на MSDN показывает, что он идет дальше VS2015
Спасибо @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;
}