САБЖ Нужно без div/idiv разделить число на 10, 100.... 10^n В общем на степень 10. Как это сделать проще всего?
Может быть, так? Код (Text): MOV EDX, EAX; SHR EDX, 5; SHR EAX, 6; ADD EAX, EDX; Правда, этот алгос не обеспечит нужной точности =( Это будет не 0.1*ЕАХ, а всего 0.09375*ЕАХ. А вообще, задачка интересная. Будем думать дальше
Если число -- дворд, то можно так: разделить на 2, умножить результат на (0хСССССССD)^n mod 2^32 и взять старший дворд из edx, т.к. 0хСССССССD = 1/5 mod 2^32 вру, но не пойму почему не так... сорри, работает только для нечетных, и брать нужно результат из eax
логарифмы вычисляет только FPU, причём делает это в среднем медленнее чем CPU - деление. Тем более неизбежно возникнут потери при переводе целых чисел в формат регистра FPU и обратно. Но если уж заговорили мы о сопре, то может тупо пользовать ?
Насчёт замены (целочисленного) деления (целочисленным) умножением: 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) - там больше вариантов, знаковое и беззнаковое деления А о том, почему всё это работает, и всевозможных случаях, очень подробно написано в книге Уоррена "Алгоритмические трюки для программистов", книгу очень рекомендую.
ну оть как просто, доступно и понятно оптимизируют си-шные компили сие выражение Код (Text): int div10 (int a) { return a / 10; } в асемблерный вид Код (Text): public _div10 _div10 proc near ; arg_0 = dword ptr 4 mov ecx, [esp+arg_0] mov eax, 66666667h imul ecx mov eax, ecx sar eax, 1Fh sar edx, 2 sub edx, eax imul eax, edx, 0Ah sub ecx, eax mov eax, edx mov edx, ecx retn _div10 endp
bugaga, что за компилятор? VS2005 на Код (Text): int div10(int a) { return a / 10; } выдаёт Код (Text): mov ecx, [esp+arg_0] mov eax, 66666667h imul ecx sar edx, 2 mov eax, edx shr eax, 1Fh add eax, edx retn а на Код (Text): unsigned div10(unsigned a) { return a / 10; } соответственно Код (Text): mov eax, 0CCCCCCCDh mul [esp+arg_0] shr edx, 3 mov eax, edx retn
Код (Text): int div10(int x) { return x/10; } Visual C++ 6.0 Professional Код (Text): mov ecx, DWORD PTR _x$[esp-4] mov eax, 1717986919 ; 66666667H imul ecx mov eax, edx sar eax, 2 mov ecx, eax shr ecx, 31 ; 0000001fH add eax, ecx ret 0 Intel C++ Compiler v10 Код (Text): mov ecx, DWORD PTR [esp+4] ;1.5 mov eax, 1717986919 ;3.11 imul ecx ;3.11 sar ecx, 31 ;3.11 sar edx, 2 ;3.11 sub edx, ecx ;3.11 mov eax, edx ;3.11 ret ;3.11 Не понятно зачем в коде bugaga второе умножение понадобилось. Да и умножение на десять можно побыстрее сделать. Вместо Код (Text): imul eax, edx, 0Ah быстрее будет Код (Text): lea eax,[edx*8+edx] add eax,edx Кстати, а в сети есть где-нибудь подробное описание почему работает замена деления на умножение? В смысле как оно работает, выбор магического числа и т.д.
Остаток от деления зачем-то вычисляет Насчет сети не знаю, в манах у Фога и AMD есть. Общий смысл простой - множитель берется равным round(2^n/10) и затем результат edx:eax сдвигается вправо на n бит (при n >= 32 это эквивалентно сдвигу edx на 32-n)
Я так подозреваю что компилятор решил div заэмулировать При делении ведь получается целая часть и остаток, вот компилятор и через умножение то же вычисляет. И пофиг ему что остаток никому не нужен Угу, это понятно. А как именно степень выбирается? В смысле все компиляторы константу генерируют одну и ту же. По идее чем больше n тем меньше будет погрешность. В мануалах AMD я видел, но там по-моему как-то не расписано. Типа делай так и будет тебе счастье. А почему так - не расписано. К примеру n можно взять 32, и тогда edx вообще двигать не надо. Но так почему-то не делают...
Это вычисления с целыми числами. Никакой погрешности быть не может, иначе получилась бы неэквивалентная замена, и её нужно было бы выбросить подальше. На "магическое число" и n есть два условия: первое - вычисления должны быть всегда точными (это даёт ограничение снизу на n), второе - "магическое число" таки должно влезать в 32 бита (это даёт ограничение сверху на n). И то - это не всегда возможно, и для беззнакового деления иногда точная замена получается только начиная с 33 бит (например, для делителя 7). Подробно о том, почему замена точная и как нужно выбирать числа и почему именно так, написано в уже упомянутой книге Уоррена, и написано довольно много (в имеющемся у меня издании глава 10 называется "Целое деление на константы" и занимает 42 страницы).
перевести двоичное A в десятичную систему и сдвинуть результат вправо. ИМХО не стоило приводить задачу к такому условию, деление на 10 реализуется на сопроцессоре.
cppasm Ты видать ман АМД невнимательно читал (см.Simpler Code for Restricted Dividend) n=32 можно юзать только для чисел меньших 40000005h
Код (Text): mov eax,Твое_число mov ebx,10 ; буду делить на 10 xor ecx,ecx lab0: sub eax,ebx jc exit inc ecx jmp lab0 exit:
Мои 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) далее по аналогии