Строку в число

Тема в разделе "WASM.A&O", создана пользователем LZNT1, 24 июл 2005.

  1. LZNT1

    LZNT1 New Member

    Публикаций:
    0
    Регистрация:
    12 июл 2005
    Сообщения:
    45
    Адрес:
    Ukraine
    Кто то когда то рано или поздно пытался написать модули для работы с большими числами. Вот и моё время подошло (ну я философ прямо :)).



    Вобщем есть 256-битное число (массив из 32-х байт). Как в него запихнуть соответсвующее его диапазону десятичное число в строчном представлении?
     
  2. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Т.е. надо в число запихнуть строку? Это как???

    Может в буфер запихнуть? Или преобразовать строку в число?



    Что-то задача не очень конкретная. Что на входе есть? Что должно быть на выходе?
     
  3. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    Неиначе как требуется шустрый вариант atoi для 256-битных чисел. На входе число в виде строки, порядка 78 символов (целое со знаком, но очень большое), на выходе его двоичный эквивалент. Сразу приходит на ум, насколько удобны шестнадцетиричные числа...
     
  4. _BC_

    _BC_ БЦ

    Публикаций:
    0
    Регистрация:
    20 янв 2005
    Сообщения:
    759
    atoi для bigint'ов... Скорость по идее не критична, можно решить проблему в лоб: сделать классическую функцию atoi, но все ариф. операции в ней тоже на bigint'ах.
     
  5. LZNT1

    LZNT1 New Member

    Публикаций:
    0
    Регистрация:
    12 июл 2005
    Сообщения:
    45
    Адрес:
    Ukraine
    alpet

    Не то чтобы они были как то особенно удобны, просто их можно привести к двоичным. Десятичные наверное тоже можно, но как неясно. Единственная закономерность (которую я нашёл) то что предел диапазона десятичных укладывается в границы 0-4-7 бит.

    000009 - 0000 0000 0000 0000 1001 - 4

    000099 - 0000 0000 0000 0110 0011 - 7

    000999 - 0000 0000 0011 1110 0111 - 10

    009999 - 0000 0010 0111 0000 1111 - 14

    099999 - 0001 1000 0110 1001 1111 - 17

    999999 - 1111 0100 0010 0011 1111 - 20



    0009999999 - 0000 0000 1001 1000 1001 0110 0111 1111 - 24

    0099999999 - 0000 0101 1111 0101 1110 0000 1111 1111 - 27

    0999999999 - 0011 1011 1001 1010 1100 1001 1111 1111 - 30



    _BC_

    Тоже тут порыскал в инете в поисках альтернативных способов пробразования DEC->BIN, но ничего кроме деления с остатком не нашёл. Можно ли такое сделать с помощью булевых операций? Может быть маски какие посоставлять?
     
  6. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    По моим прикидкам наиболее простое и производительное решение будет с использованием таблички (порядка 780 256-битных значения: 1,2,3..9,10,20..90,100,200..900 и т.д.), тогда на долю binint математики выпадает только сложение, что конечно не так ресурсоемко, как умножение на 10.

    Вобщем сначала лучше обозначить общий алгоритм на HLL, а потом реализовать и оптимировать его на асме.
     
  7. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    alpet

    Вроде умножение на 10 не так трудно реализовать: это можно свести к копированию буфера, содержащего число, в новый destination со смещением в 3 бита, и затем к новому буферу дважды добавить исходный.
     
  8. LZNT1

    LZNT1 New Member

    Публикаций:
    0
    Регистрация:
    12 июл 2005
    Сообщения:
    45
    Адрес:
    Ukraine
    780*256=199680 - многовато получается.

    По поводу умножения на 10, это значит конвертировать значение разряда в двоичный формат, а потом возводить в нужную степень? Вроде подходит.



    cresta

    Хороший алгоритм, попробую им воспользоваться.
     
  9. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Умножение 256-битного числа на 10. Младшие биты числа - по младшим адресам (в dw_0).


    Код (Text):
    1. .data
    2.             dw_0    dd 0        ;8 двордов - 256 бит
    3.             dw_1    dd 1
    4.             dw_2    dd 2
    5.             dw_3    dd 3
    6.             dw_4    dd 4
    7.             dw_5    dd 5
    8.             dw_6    dd 6
    9.             dw_7    dd 7
    10.            
    11.             temp_0  dd 0        ;temp-буфер
    12.             temp_1  dd 0
    13.             temp_2  dd 0
    14.             temp_3  dd 0
    15.             temp_4  dd 0
    16.             temp_5  dd 0
    17.             temp_6  dd 0
    18.             temp_7  dd 0
    19.             temp_t  dd 0
    20.            
    21.         .code  
    22.             lea     esi,dw_0
    23.             lea     edi,temp_0
    24.             mov     edx,7
    25.         _copy:      ; - копирование 256-битного числа в temp-буфер
    26.             mov     eax,[esi+edx*4]
    27.             mov     [edi+edx*4],eax
    28.             dec     edx
    29.             jns     _copy
    30.             mov     ecx,3
    31.         _shift:     ; - сдвиг temp-буфера на 3 разряда влево (умножение на 8)
    32.             shl     dword ptr[edi+0],1
    33.             rcl     dword ptr[edi+4],1
    34.             rcl     dword ptr[edi+8],1
    35.             rcl     dword ptr[edi+12],1
    36.             rcl     dword ptr[edi+16],1
    37.             rcl     dword ptr[edi+20],1
    38.             rcl     dword ptr[edi+24],1
    39.             rcl     dword ptr[edi+28],1
    40.             dec     ecx
    41.             jnz     _shift
    42.            
    43.             mov     ecx,2
    44.         _adc:       ; - добавление дважды к temp-буферу исходного числа
    45.             clc
    46.             mov     eax,[esi]
    47.             adc     [edi],eax
    48.             mov     eax,[esi+4]
    49.             adc     [edi+4],eax
    50.             mov     eax,[esi+8]
    51.             adc     [edi+8],eax
    52.             mov     eax,[esi+12]
    53.             adc     [edi+12],eax
    54.             mov     eax,[esi+16]
    55.             adc     [edi+16],eax
    56.             mov     eax,[esi+20]
    57.             adc     [edi+20],eax
    58.             mov     eax,[esi+24]
    59.             adc     [edi+24],eax
    60.             mov     eax,[esi+28]
    61.             adc     [edi+28],eax
    62.             dec     ecx
    63.             jnz     _adc
    64.            
    65.             mov     edx,7
    66.         _copy_back:  ; - копирование числа из temp-буфера (умноженного на 10) в исходный
    67.             mov     eax,[edi+edx*4]
    68.             mov     [esi+edx*4],eax
    69.             dec     edx
    70.             jns     _copy_back




    Вроде ничего не напутал :) Надо конечно потестить. Я проверял только на тех значениях, что прописаны в dw_0 - dw_7. Насчёт скорости - сравнивать пока не с чем.
     
  10. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    LZNT1

    Ничего не много = 25 Кбайт всего. На многих процессоров это влезет в пределы кэша L1.



    Вот кстати свойство что поможет в большей упаковке и существенному повышению производительности:



    10000 = 0x2710, 20000 = 0x4E20, 30000 = 0x7530, 40000 = 0x9C4... младшие 4 бита = 0

    100 млн. = 0x5F5E100, 200 = 0xBEBC200, 300 = 11E1A300... младшие 8 бит = 0

    1e12 = E8D4A51000, 2e12 = 1D1A94A2000, 3e12 = 2BA7DEF3000... младшие 12 бит = 0

    1e16 = 2386F26FC10000, 2e16 = 470DE4DF820000, 3e16 = 6A94D74F430000... младшие 16 бит = 0



    Иными словами дес. числа большие (и кратные при этом) 1e(x) имеют как минимум x нулевых бит. Помере увеличения x конечно будет требоваться все больше и больше байт для сохранения одного числа из таблички, но никогда оно не достигнет 32 байт. Соответственно размер таблички у нас будет существенно меньше 780*32 (25Kb), а все операции сложения также будут проводится над меньшими по разядности числами (т.е. меньше 2500 сложений на 78 значное дес. число).
     
  11. LZNT1

    LZNT1 New Member

    Публикаций:
    0
    Регистрация:
    12 июл 2005
    Сообщения:
    45
    Адрес:
    Ukraine
    Громоздковато получается. Это если допустим 100-значное число, получается 5150 умножений на 10, а каждое умножение включает один сдвиг и два сложения.



    cresta

    У меня байтовый массив. Лучше я сдвиг сделаю через промежуточное слово.



    alpet

    Забыл поделить на 8 :)

    Про нулевые биты я чёт не понял к чему это?

    В принципе сократить табличку можно урезав разрядность чисел меньшего диапазона, но при этом нужно будет задавать с какого элемента он расширяется.
     
  12. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    LZNT1

    С умножениями несколько проще - ненадо табличку изначально генерить, да и алгоритм настолько просто, что хоть сразу в асме пиши. А сложность у него несколько другая - сколько знаков в числе, столько и умножений.



    Вот примерный алгоритм с умножениями:
    Код (Text):
    1.  
    2. char bdig [] = "1234567891284381627175239549165324123647123";
    3. int l = strleng (bdig);
    4. char bin [32]; // 256 bit digit
    5. memset (bin, 32, 0); // = 0
    6. bin [0] = bdig [0] - 0x30; // set first
    7. for (int i = 1; i < l; i ++)}
    8. {
    9.    L256MUL10 (bin); // multiple 10
    10.    L256ADD (bin, bdig [i] - 0x30); // add digit
    11. }
    12.  




    Табличку очень сильно можно урезать, наверное даже до 10Кб - считай первые числа 2 и 4 байта, а последние будут порядка 20 байт.
     
  13. LZNT1

    LZNT1 New Member

    Публикаций:
    0
    Регистрация:
    12 июл 2005
    Сообщения:
    45
    Адрес:
    Ukraine
    alpet

    А почему всего 20? А остальные 12 как?



    Наверное всё таки сложениями быстрее будет. Меньше вычислений.
     
  14. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Уменьшить таблицу можно, используя только 1,2,4,8 на декаду. Правда, за счёт некоторого усложнения алгоритма.





    Что в этом массиве? Исходная строка или конечный результат?





    Пока тут в аттаче преобразование строки в 256 битное число.



    Тестировал на правильность на разрядности 64 бита, нормально. По идее и дальше будет работать, т.к. переход между двордами отработал нормально.



    При длине строки 78 символов время получил 18000 тактов.

    Первый проход (с загрузкой в кэш кода) - 19000.



    Удастся ли если делать вариант с таблицей, уложиться в это время, чтобы подгрузить таблицу в кэш и выполнить код?





    [​IMG] _274607943__str_to_256bit.inc
     
  15. LZNT1

    LZNT1 New Member

    Публикаций:
    0
    Регистрация:
    12 июл 2005
    Сообщения:
    45
    Адрес:
    Ukraine
    cresta

    Конечный результат у меня в байтовом массиве. Только байты могут быть беззнаковыми, остальные типы вызывают переполнение.

    Усложнять алгоритм наверное не нужно. Нужно просто при загрузке таблицы а память расширять сжатые значения до 32-х байт.

    С таблицей таки быстрее будет. Всего одна операция на порядок. Ведь каждое число есть в таблице. Например 951.

    Берём из таблицы значения для 900,50 и 1, а затем складываем их вместе.
     
  16. LZNT1

    LZNT1 New Member

    Публикаций:
    0
    Регистрация:
    12 июл 2005
    Сообщения:
    45
    Адрес:
    Ukraine
    Кстати, а как таблицу составлять? Алгоритм есть? Может через умножение на 10?
     
  17. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257




    совсем не факт. А с таблицей такого размера - думаю, наверняка медленнее будет. Хотя это надо мерять. Делать вариант с таблицей и сравнивать. То, что просто сложение (вернее не просто, а с учётом переноса) - это понятно. Я и сам по возможности использую таблицы, но что-то мне подсказывает, что в данном случае таблица - не лучший вариант.



    Если это преобразование строки будет делаться в цикле многократно (десятки и сотни раз), причем непрерывно, то тогда есть смысл потратить время на то, чтобы сделать таблицу и подгрузить в кэш. Если преобразование будет выполняться периодически (однократно), потери времени на таблицу могут оказаться непомерно высокими.

    Да и пока не видно, что такого можно сэкономить за счет таблицы.



    О скорости: данные (как строку, так и принимающий число буфер) желательно выравнять на 16. Я как всегда, об этом забыл :)

    Сейчас подравнял буфера на 16 и получил для процедуры в аттаче почти в 2 раза быстрее: 78-символьная строка обрабатывается за 10400 тактов (первый проход - 12000).







    Как будешь использовать конечный массив - дело твоё, хочешь как байтовый, а то, как он формируется, никак не скажется на том, как его потом использовать. Если конечно не на VB его формировать, а так как предлагается - на асме. Судя по тому, что остальные типы будут вызывать переполнение, использоваться будет в VB?
     
  18. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    Приготовил приблизительный вариант с табличкой (не упакованной пока). Результат сначала напугал - 11200 тиков, но потом перекомпилил в релиз (обертка то HLL) - стало нормально - 6000 тиков на строку из 78 символов. Ну естно пример еще можно оптимизировать, переписав целиком на асм.

    [​IMG] _1005411386__fscan2.zip
     
  19. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    cresta

    В VB есть аналоги ADC и RCL? Что-то я плохо себе представляю без этих операций быструю функцию...
     
  20. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    alpet



    К сожалению, сравнить не получилось :dntknw: Исходник твой у меня не компилится, по причине #include "stdafx.h".

    Перенес все функции, структуры и переменные в другой свой исходник, скомпилил нормально, но при попытке преобразовать строку в число прога молча вылетает :dntknw:

    Я вот один момент не пойму: чего ты постоянно GetTickCount'ом пользуешь :). Тот коэффициент, на который делится полученное от неё значение, никак не дает количества тактов.



    Пока немного доработал свой код, заменив rcl mem32,1 на

    mov reg32,mem32

    rcl reg32,1

    mov mem32,reg32



    В тактах получается 8800.

    Если мерять GetTickCount'ом, то на 100000 вызовов функции преобразования строки уходит 470-500 мс.



    Насколько я знаю, в VB аналогов ADC и RCL, позволяющих учитывать перенос, нет. Только по ошибке переполнения реагировать через обработчик ошибок, но это медленно.