Умножение больших чисел

Тема в разделе "WASM.A&O", создана пользователем serious, 23 май 2005.

  1. serious

    serious New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    33
    Адрес:
    Russia
    Необходимо перемножить 2 64-хбайтных числа с наименьшими временными затратами. Думаю делать так: a0b0, b0a1 + b1a0, b0a2+b1a1+b2a0 и т.д., и по ходу дела суммировать их в результирующем буфере, учитывая перенос (команда adc). Значения индексов для a и b буду брать из регистров SSE2, куда перед вызовом функции буду их загонять из буфера в памяти (все значения вычислены заранее). Может быть, в дальнейшем написать макрос, продублировать его тело 256 раз, развернув тем самым цикл. А может, перемножать числа парами (есть там такая команда, название не помню). Кто занималься этим, подскажите плз.
     
  2. dr_dred

    dr_dred Сергей

    Публикаций:
    0
    Регистрация:
    12 мар 2005
    Сообщения:
    301
    Адрес:
    Russia
    64 байта??? Ты загнул. Может бита? Можно с помощью FPU, можно так (взято в книге Зубкова):


    Код (Text):
    1. ; беззнаковое умножение двух 64-битных чисел (X и Y) и сохранение
    2. ; результата в 128-битное число Z
    3.         mov        eax,dword ptr X
    4.         mov        ebx,eax
    5.         mul        dword ptr Y         ; перемножить младшие двойные слова
    6.         mov        dword ptr Z,eax     ; сохранить младшее слово произведения
    7.         mov        ecx,edx             ; сохранить старшее двойное слово
    8.         mov        eax,ebx             ; младшее слово "X" в еах
    9.         mul        dword ptr Y[4]      ; умножить младшее слово на старшее
    10.         add        еах,есх
    11.         adc        edx,0               ; добавить перенос
    12.         mov        ebx,eax             ; сохранить частичное произведение
    13.         mov        ecx,edx
    14.         mov        eax,dword ptr X[4]
    15.         mul        dword ptr Y         ; умножить старшее слово на младшее
    16.         add        eax,ebx             ; сложить с частичным произведением
    17.         mov        dword ptr Z[4],eax
    18.         adc        ecx,edx
    19.         mov        eax,dword ptr X[4]
    20.         mul        dword ptr Y[4]      ; умножить старшие слова
    21.         add        eax,ecx             ; сложить с частичным произведением
    22.         adc        edx,0               ; и добавить перенос
    23.         mov        word ptr Z[8],eax
    24.         mov        word ptr Z[12],edx




    Чтобы выполнить умножение со знаком, потребуется сначала определить знаки множителей, изменить знаки отрицательных множителей, выполнить обычное умножение и изменить знак результата, если знаки множителей были разными.
     
  3. serious

    serious New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    33
    Адрес:
    Russia
    Нее, именно байта. У меня операции в группе точек эллиптической кривой, там числа - офигеть какие здоровые. А то, что ты написал у меня по идее в цикле должно крутиться несколько раз.
     
  4. flankerx

    flankerx New Member

    Публикаций:
    0
    Регистрация:
    2 июл 2004
    Сообщения:
    423
    Адрес:
    Moscow, Russia
    1. можно использовать умножение столбиком (как в школе :) ) -- это, видимо, то что ты и предлагаешь.



    2. можно сделать быстрее: умножение 512x512 бит представляешь как 4 умножения 256x256 бит, каждое из которых как 4 128x128 и т.д. до 32x32. Умноженией 32x32 тогда потребуется 256 штук.



    Вообще рекомендую глянуть как подобные вещи реализованы в GMP (www.swox.com/gmp), OpenSSL или в любой другой более-менее серьезной библиотеке.
     
  5. Stiver

    Stiver Партизан дзена

    Публикаций:
    0
    Регистрация:
    18 дек 2004
    Сообщения:
    812
    Адрес:
    Germany
    flankerx





    Хм, а в случае "школьного" умножения сколько? Вроде те же самые 256 и будут..



    В принципе для (не очень) длинных чисел используется Karatsuba, в том числе по-моему и в GMP.
     
  6. flankerx

    flankerx New Member

    Публикаций:
    0
    Регистрация:
    2 июл 2004
    Сообщения:
    423
    Адрес:
    Moscow, Russia
    Упс. Ошибся немного :dntknw:(



    Метод Карацубы немного иначе выглядит.



    В нем для умножения двух 512-битных чисел нужно вполнить ТРИ умножения 256x256: A=a1 | a2, B=b1 | b2 (a1 -- старшие разряды, a2 -- младшие).

    Тогда AB = a1b1 | (a1-a2)(b2-b1) | a2b2



    Тогда для умножения 512x512 нужно 81 умножение 32x32.



    Теоретическая оценка "школьного" метода -- n^2, Карацубы -- n^lg3.



    В GMP используется несколько алгоритмов умножения: сначала "обычный", потом Карацуба, потом еще Тоома-Кука, а для очень больших чисел -- FFT.
     
  7. serious

    serious New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    33
    Адрес:
    Russia
    Метод Карацубы имхо не оправдан на 64-хбайтных числах, хотя я могу ошибаться.

    З.Ы. А что за метод Тоома-Кука?
     
  8. flankerx

    flankerx New Member

    Публикаций:
    0
    Регистрация:
    2 июл 2004
    Сообщения:
    423
    Адрес:
    Moscow, Russia
    serious

    См. во втором томе Кнута, например.

    Но это метод для еще бОльших (чем Карацуба) чисел применяется.
     
  9. dr_dred

    dr_dred Сергей

    Публикаций:
    0
    Регистрация:
    12 мар 2005
    Сообщения:
    301
    Адрес:
    Russia
    Я в MMX не разбираюсь, но все же может...
     
  10. serious

    serious New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    33
    Адрес:
    Russia
    Где-то видел, что метод Карацубы применяется в основном на 512-1024-хбайтных числах.

    З.Ы. Так всетаки, получается в данной ситуации лучше умножать "столбиком"?
     
  11. flankerx

    flankerx New Member

    Публикаций:
    0
    Регистрация:
    2 июл 2004
    Сообщения:
    423
    Адрес:
    Moscow, Russia
    serious

    ну почему?

    если столюиком -- 256 умножений,

    если карацубой -- 81.



    вот посмотрел в GMP -- там такая строка есть в конфигурационом файле

    #define MUL_KARATSUBA_THRESHOLD 18



    я уж не знаю 18 чего (скоре всего лимбов, т.е. машинных слов), но 18*4 = 72 по-любому меньше 512 )
     
  12. serious

    serious New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    33
    Адрес:
    Russia
    Сделал умножение таким образом:
    Код (Text):
    1.  
    2. __asm {
    3.  
    4.     xor     ecx,  ecx               // счетчик цикла
    5.  
    6.     up:
    7.     // TODO: Проверка операндов на 0.
    8.  
    9.     movdqa  xmm0, [multi_1 + ecx]   // загрузка 2-х пар множителей
    10.     pshufd  xmm1, xmm0, 00010000b   // Ai, Ai+1 (A0 | A1 | A0 | A0)
    11.  
    12.     movdqa  xmm2, [multi_2 + ecx]   // загрузка 2-х пар множителей
    13.     pshufd  xmm3, xmm2, 00010000b   // Bi, Bi+1 (B0 | B1 | B0 | B0)
    14.  
    15.     pmuludq xmm3, xmm1              // пакетное умножение первых 2-х пар
    16.                                     // xmm3 = (A1*B1) | (A0*B0)
    17.  
    18.     pshufd  xmm4, xmm2, 00000001b   // Bi, Bi+1 (B0 | B1 | B0 | B1)
    19.     pmuludq xmm4, xmm1              // пакетное умножение оставшихся 2-х пар
    20.                                             // xmm4 = (B0*A1) | (A0*B1)
    21.  
    22.     add     ecx, 16                 // смещение на 128 бит
    23.     cmp     ecx, 256
    24.     jnz     up
    25. }
    26.  




    Теперь вот непонятно, каким образом это все складывать, учитывая перенос. Если вытаскивать из xmm регистров дворды и потом их складывать при помощи adc, получается как-то гемморойно. Может, есть какой-нибудь другой способ?
     
  13. serious

    serious New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    33
    Адрес:
    Russia
    Блин, я лоханулся, здесь ведь еще 2 умножения можно сделать... Но суть вопроса от этого не меняется.