деление. Как разделить число А на степень 10ки ?

Тема в разделе "WASM.ASSEMBLER", создана пользователем Magnum, 28 июл 2008.

  1. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    САБЖ
    Нужно без div/idiv
    разделить число на 10, 100.... 10^n

    В общем на степень 10.
    Как это сделать проще всего?
     
  2. DEEP

    DEEP Андрей

    Публикаций:
    0
    Регистрация:
    27 апр 2008
    Сообщения:
    491
    Адрес:
    г. Владимир
    Может быть, так?
    Код (Text):
    1. MOV EDX, EAX;
    2. SHR EDX, 5;
    3. SHR EAX, 6;
    4. ADD EAX, EDX;
    Правда, этот алгос не обеспечит нужной точности =(
    Это будет не 0.1*ЕАХ, а всего 0.09375*ЕАХ.
    А вообще, задачка интересная. Будем думать дальше ;)
     
  3. Velheart

    Velheart New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2008
    Сообщения:
    526
    Если число -- дворд, то можно так:
    разделить на 2,
    умножить результат на (0хСССССССD)^n mod 2^32 и взять старший дворд из edx, т.к. 0хСССССССD = 1/5 mod 2^32
    вру, но не пойму почему не так...

    сорри, работает только для нечетных, и брать нужно результат из eax
     
  4. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    x/10 = exp(ln(x)-ln(10))
     
  5. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    GoldFinch

    на асме нужно
    и чем проще тем лучше
     
  6. DEEP

    DEEP Андрей

    Публикаций:
    0
    Регистрация:
    27 апр 2008
    Сообщения:
    491
    Адрес:
    г. Владимир
    логарифмы вычисляет только FPU, причём делает это в среднем медленнее чем CPU - деление. Тем более неизбежно возникнут потери при переводе целых чисел в формат регистра FPU и обратно.
    Но если уж заговорили мы о сопре, то может тупо пользовать
    ?
     
  7. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Насчёт замены (целочисленного) деления (целочисленным) умножением:
    http://wasm.ru/baixado.php?mode=tool&id=203
    http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/22007.pdf (самое начало главы 8) - там больше вариантов, знаковое и беззнаковое деления
    А о том, почему всё это работает, и всевозможных случаях, очень подробно написано в книге Уоррена "Алгоритмические трюки для программистов", книгу очень рекомендую.
     
  8. bugaga

    bugaga New Member

    Публикаций:
    0
    Регистрация:
    1 июл 2007
    Сообщения:
    361
    ну оть как просто, доступно и понятно оптимизируют си-шные компили
    сие выражение
    Код (Text):
    1. int div10 (int a)
    2. {
    3.  return a / 10;
    4. }
    5.  
    6. в асемблерный вид
    Код (Text):
    1.                  public _div10
    2.  _div10          proc near               ;
    3.  
    4.  arg_0           = dword ptr  4
    5.  
    6.           mov   ecx, [esp+arg_0]
    7.           mov   eax, 66666667h
    8.           imul   ecx
    9.           mov  eax, ecx
    10.           sar    eax, 1Fh
    11.           sar    edx, 2
    12.           sub    edx, eax
    13.           imul   eax, edx, 0Ah
    14.           sub    ecx, eax
    15.           mov    eax, edx
    16.           mov    edx, ecx
    17.           retn
    18. _div10 endp
     
  9. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    bugaga, что за компилятор?
    VS2005 на
    Код (Text):
    1. int div10(int a)
    2. {
    3.     return a / 10;
    4. }
    выдаёт
    Код (Text):
    1. mov ecx, [esp+arg_0]
    2. mov eax, 66666667h
    3. imul ecx
    4. sar edx, 2
    5. mov eax, edx
    6. shr eax, 1Fh
    7. add eax, edx
    8. retn
    а на

    Код (Text):
    1. unsigned div10(unsigned a)
    2. {
    3.     return a / 10;
    4. }
    соответственно
    Код (Text):
    1. mov eax, 0CCCCCCCDh
    2. mul [esp+arg_0]
    3. shr edx, 3
    4. mov eax, edx
    5. retn
     
  10. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Код (Text):
    1. int div10(int x)
    2. {
    3.     return x/10;
    4. }
    Visual C++ 6.0 Professional

    Код (Text):
    1.     mov ecx, DWORD PTR _x$[esp-4]
    2.     mov eax, 1717986919             ; 66666667H
    3.     imul    ecx
    4.     mov eax, edx
    5.     sar eax, 2
    6.     mov ecx, eax
    7.     shr ecx, 31                 ; 0000001fH
    8.     add eax, ecx
    9.     ret 0
    Intel C++ Compiler v10
    Код (Text):
    1.     mov ecx, DWORD PTR [esp+4]  ;1.5
    2.     mov eax, 1717986919     ;3.11
    3.     imul    ecx         ;3.11
    4.     sar ecx, 31         ;3.11
    5.     sar edx, 2          ;3.11
    6.     sub edx, ecx        ;3.11
    7.     mov eax, edx        ;3.11
    8.     ret             ;3.11
    Не понятно зачем в коде bugaga второе умножение понадобилось.
    Да и умножение на десять можно побыстрее сделать.
    Вместо
    Код (Text):
    1. imul   eax, edx, 0Ah
    быстрее будет
    Код (Text):
    1. lea eax,[edx*8+edx]
    2. add eax,edx
    Кстати, а в сети есть где-нибудь подробное описание почему работает замена деления на умножение?
    В смысле как оно работает, выбор магического числа и т.д.
     
  11. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Остаток от деления зачем-то вычисляет

    Насчет сети не знаю, в манах у Фога и AMD есть. Общий смысл простой - множитель берется равным round(2^n/10) и затем результат edx:eax сдвигается вправо на n бит (при n >= 32 это эквивалентно сдвигу edx на 32-n)
     
  12. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Я так подозреваю что компилятор решил div заэмулировать :)
    При делении ведь получается целая часть и остаток, вот компилятор и через умножение то же вычисляет.
    И пофиг ему что остаток никому не нужен :)
    Угу, это понятно.
    А как именно степень выбирается?
    В смысле все компиляторы константу генерируют одну и ту же.
    По идее чем больше n тем меньше будет погрешность.
    В мануалах AMD я видел, но там по-моему как-то не расписано.
    Типа делай так и будет тебе счастье. А почему так - не расписано.
    К примеру n можно взять 32, и тогда edx вообще двигать не надо.
    Но так почему-то не делают...
     
  13. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Это вычисления с целыми числами. Никакой погрешности быть не может, иначе получилась бы неэквивалентная замена, и её нужно было бы выбросить подальше.
    На "магическое число" и n есть два условия: первое - вычисления должны быть всегда точными (это даёт ограничение снизу на n), второе - "магическое число" таки должно влезать в 32 бита (это даёт ограничение сверху на n). И то - это не всегда возможно, и для беззнакового деления иногда точная замена получается только начиная с 33 бит (например, для делителя 7). Подробно о том, почему замена точная и как нужно выбирать числа и почему именно так, написано в уже упомянутой книге Уоррена, и написано довольно много (в имеющемся у меня издании глава 10 называется "Целое деление на константы" и занимает 42 страницы).
     
  14. t00x

    t00x New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2007
    Сообщения:
    1.921
    перевести двоичное A в десятичную систему и сдвинуть результат вправо.
    ИМХО не стоило приводить задачу к такому условию, деление на 10 реализуется на сопроцессоре.
     
  15. bugaga

    bugaga New Member

    Публикаций:
    0
    Регистрация:
    1 июл 2007
    Сообщения:
    361
    Digital Mars Compiler 8.53.
    угу. а если сапра нет то его нада эмулить :)
     
  16. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    cppasm
    Ты видать ман АМД невнимательно читал (см.Simpler Code for Restricted Dividend)
    n=32 можно юзать только для чисел меньших 40000005h
     
  17. Memphis

    Memphis New Member

    Публикаций:
    0
    Регистрация:
    23 окт 2008
    Сообщения:
    104
    Код (Text):
    1. mov eax,Твое_число
    2. mov ebx,10 ; буду делить на 10
    3. xor ecx,ecx
    4. lab0:
    5. sub eax,ebx
    6. jc exit
    7. inc ecx
    8. jmp lab0
    9. exit:
     
  18. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.787
    Мои 5 копеек
    1) делене на 10: если число А меньше 256 используйте AAM
    если допускается такая точность, тогда варианты 2, 3, 4, 5
    2) делене на 10: shr eax,4/mov ebx,eax/shr eax,1/add eax,ebx/mov ebx,eax/shr eax,4/add eax,ebx
    1/512+1/256+1/32+1/16=102/1024=0,099609375 почти 0,1
    2a) делене на 10: shr eax,4/mov ebx,eax/shr eax,1/mov ecx,eax/shr eax,2/add eax,ebx/add eax,ecx
    1/128+1/32+1/16=13/128=0,1015625 почти 0,1
    3) делене на 100: shr eax,7/mov ebx,eax/shr eax,2/add eax,ebx
    1/512+1/128=5/512=0,009765625 почти 0,01
    4) делене на 1000: shr eax,10 1/1024=0,0009765625 почти 0,001
    4a) делене на 1000: shr eax,10/mov ebx,eax/shr eax,4/mov ecx,eax/shr eax,3/mov edx,eax/shr eax,3/add eax,edx/add eax,ecx/add eax,ebx 1/1048576+1/131072+1/65536+1/1024=0,00100040435791015625 почти 0,001
    5) деление на 10000: shr eax,14/mov ebx,eax/shr eax,1/mov ecx,eax/shr eax,2/mov edx,eax/
    shr eax,3/add eax,edx/add eax,ecx/add eax,ebx 1/1048576+1/131072+1/32768+1/16384=
    =105/1048576=0,00010013580322265625 почти 0,0001
    6) далее по аналогии