2011-12-09 9 views
8

Zastanawiam się, czy tak naprawdę nie ma wewnętrznej funkcji dzielenia 128-bitowego w Visual C++?128-bitowy podział wewnętrzny w Visual C++

Istnieje wewnętrzna funkcja mnożenia 64x64 = 128 bitów o nazwie _umul128(), która ładnie pasuje do instrukcji asemblera MUL x64.

Oczywiście założyłem, że istnieje również podział wewnętrzny 128/64 = 64 bitów (modelowanie instrukcji DIV), ale ku mojemu zdumieniu ani Visual C++, ani Intel C++ nie wydają się mieć tego, przynajmniej nie ma na liście intrin.h.

Czy ktoś może to potwierdzić? Próbowałem grep'ing dla nazw funkcji w plikach wykonywalnych kompilatora, ale nie mogłem znaleźć _umul128 w pierwszej kolejności, więc domyślam się, że patrzyłem w niewłaściwym miejscu.

Aktualizacja: przynajmniej znalazłem wzór "umul128" (bez wiodącego podkreślenia) w pliku c1.dll programu Visual C++ 2010. Wszystkie inne elementy wewnętrzne są wymienione wokół niego, ale niestety nie ma "udiv128" lub podobnych :(Tak wygląda na to, że naprawdę "zapomnieli" go zaimplementować:

Aby wyjaśnić: szukam nie tylko 128-bitowego typu danych, ale sposób na podzielenie 128-bitowego int intela skalarnego przez 64-bitowe int w C++. OSOBĄ wewnętrzna funkcja lub rodzimy 128-bitowego całkowitą rozwiąże mój problem.

Edi T: Odpowiedź brzmi: nie, nie ma _udiv128 wewnętrzna w Visual Studio 2010 lub 2012.

+1

Nie jest częścią CRT. Jest nieodłączny, przychodzi za darmo z procesorem. Ale tylko w trybie 64-bitowym. Żadnego freebiego dla div, dopóki nie otrzymasz 128-bitowego procesora. Biorąc pod uwagę absurdalnie szeroki zakres pow (2, 128), powinieneś szukać dowolnej biblioteki precyzji. Mnóstwo ludzi wokół. –

+0

@TreeMonkie: __int18 nie jest obsługiwany przez VS, zobacz http://stackoverflow.com/questions/6759592/how-to-enable-int128-on-visual-studio – cxxl

+2

@Hans: sorry, nie rozumiem. To po prostu NIE jest samoistne, nawet w trybie 64-bitowym. I potrzebuję go do * zapisu * arbitralnej, precyzyjnej biblioteki :) – cxxl

Odpowiedz

1

Nie jestem ekspertem, ale wykopali ten up:

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

Ciekawe rzeczy. Mam nadzieję, że to pomoże.

EDIT: To jest zbyt wnikliwe: (tryb 64-bitowy tylko, nie testowane) http://www.gamedev.net/topic/508197-x64-div-intrinsic/

+0

To naprawdę bardzo bolesny problem.Nawet jeśli okaże się, że konieczne jest obustronne przesunięcie + przesunięcie, nie musisz pomnażać swojego 128-bitowego nom z odwrotnością i przyjąć 64 bitów z wyniku, co jest poważną PITA – yonil

+0

. Również trudno mi w to uwierzyć, że cała sprawa w jakiś sposób przewyższają instrukcje DIV/IDIV. – yonil

10

Jeśli nie przeszkadza trochę hacki, może to pomóc:

#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("0x0000ABCDEF000000000000/0x0001000000000000 = 0x%llX\n", 
     udiv128(0x0000AB, 0xCDEF000000000000, 0x0001000000000000, &ur)); 
    printf("-6/-2 = %lld\n", 
     sdiv128(-1, -6, -2, &sr)); 
    return 0; 
} 
+1

Dla MSVC można użyć sekcji #pragma do umieszczenia tych funkcji w segmencie kodu podczas kompilacji –

+0

@Maratyszcza: masz rację. –

+0

Dlaczego nie można użyć wbudowanego zestawu? –

2

Niewielka poprawa - jeden mniej instrukcji

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