|
|
| Посл.отвђт | Сообщенiе |
|
|
Дата: Сен 16, 2003 16:16:08 Вот, наконец.. только что с отладчика!!! Мой алгоритм домножения на 5... Код глючный, но работает :))) Правда -- экспонента пока не переводится.. И в конце функция пока не верно завершена... Код непричёсан. Но пока интересны идеи по оптимизации ;; ################################################################## ;; FUN::Float_to_String ;;------------------------------ ; CONV::ASMCALL ; FORMALS:: ; st(0) = reg type:real10 ; - Число подлежащее преобразованию ; edx = reg type:pointer ; - Указатель на строку, которая будет выведена ; DPN:: ; Описание функции ;------------------------------ ; ALG: Comment # Алгоритм -= Преобразование числа в строку =- 1. Находим 10 экспоненту числа. Для того, чтобы это сделать достаточно получить логорифм числа по основанию 10. 2. Приводим степень до целого числа. Получаем число 10^степень 3. Делим исходное число на 10-ю экспоненту 4. Отделяем дробную от целой части. 5. Переводим целую часть (как Int_To_Str) в строку 6. Переводим дробную часть в строку (Алгоритм *5) 6.1 Дробрая часть имеет вид нормализованного числа (64 bit + биты на переполнение) При чём старший бит находится в 64-ой позиции (отсчёт от 0) 6.2 Число (64-bits+1) умножается на 5 6.3 Всё, что попало далее 63-bit - и есть первое число после запятой 6.4 В 63 битах находится оставшееся число 6.4 Мы подвигаем его на 1 бит. 6.4.1 Если старший бит не 1, значит следующий десятичный разряд = 0 6.4.2 Иначе, повторяем с 6.2 6.5 Цикл окончен, если число в 63 битах = 0. 7. Выводим экспоненту # ;; ========================================================== Float_to_String proc ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comment # # ; USE: ; eax,edx,ecx,edi ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ;; Создание стекового фрейма, таким, чтобы он был выровнен ;; на границу параграфа 16 байт. mov eax,esp sub esp,14+12+2 ;; (14 для переменных + 12 для хранения регистров) and esp,0fffffff0h ;; Сохраним действительное mov [esp+16],eax ;; Значение esp mov [esp+20],esi ;; Значения регистров, которые не изменяются mov [esp+24],ebx ;; ;; Определение стековых констант $__real10 EQU [esp] $__fpumode_1 EQU [esp+10] $__fpumode EQU [esp+12] ;; Установка режима точности вычислений 80-bits fnstcw word ptr $__fpumode ;; Создание копии для восстановления режима. fnstcw word ptr $__fpumode_1 mov byte ptr $__fpumode+1,0011b fldcw word ptr $__fpumode ;; 1. Вычисляем степень 10 ;; log10(x) = log10(2)*log2(x) = FYL2X(FLDLG2,x) fst st(1) ;; Копия исходного числа fldlg2 fxch st(1) fyl2x ;; 2. Теперь когда мы получили результат ;; Нам нужно извлеч из него только целую часть ;; Для этого нужно поменяв режим округления, округлить ;; результат ;; Режим округления к нулю mov byte ptr $__fpumode+1,1111b fldcw word ptr $__fpumode fistp dword ptr $__real10 ;; fstp st(0) ;; Удаляем значение log2(10) ;; Восстанавливаем режим округления mov byte ptr $__fpumode+1,0011b fldcw word ptr $__fpumode ;; 3. Теперь нужно получить экспоненту 10 - 10^st(0) mov eax,dword ptr $__real10 ;; Нужно сохранить это число для сравнения его на знак mov ecx,eax call _exp10 ;; 4. Деление на экспоненту ;; Теперь в st(0) то число, которое нам нужно ;; Мы должны: ;; Если число отрицательное умножить на него st(1) ;; Иначе, разделить @@: ;; if ecx < 0 neg ecx js short @F ;; Если знака не было. ;; then ;; Иначе знак был fmulp st(1),st(0) jmp next5 @@: ;; else fdivp st(1),st(0) ;; endif next5: ;; 5. После этого в st(0) оказывается нужное нам число ;; При чём это число оказывается в формате ;; x.xxxxxxx где x число от 0 до 9 ;; именно его и следует переводить в строку. ;; Для этого число раздляется на дробную и целую части, которые конвертируются ;; отдельно ;; !!! ;; Заметте, что в ecx хранится число с обратным знаком ;; знаку числа степени 10. ;; 5.1 Разбиваем число на дробную и целую части mov byte ptr $__fpumode+1,1111b fldcw word ptr $__fpumode fld st(0) frndint fsub st(1),st(0) fistp dword ptr $__real10 mov eax,$__real10 fstp tbyte ptr $__real10 ;; 5.2 В регистре eax хранится целая часть. Это одна цифра, а поэтому neg eax js short @F ;; Если знака не было. jz short @F ;; Если ноль ;;Если знак был add eax,'.' shl 8 ;;(знак точки) shl eax,8 add eax,'-' mov [edi],eax add edi,3 ;; Мы записали в строку 3 байта jmp short next5_3 @@: ;; Иначе в eax было беззнаковое число neg al mov byte ptr [edi], al mov byte ptr [edi+1], '.' add edi,2 next5_3: push ecx ;; 5.3 На этом этапе должно произойти преобразование числа ;; Это преобразование основано на переполнении 64-bit-ного числа ;; которое находится по адресу esp+4 (потому что ecx) $__int64 EQU [esp+4] $__exponent EQU [esp+4+8] ;;----------- Преобразование мантиссы ------------------------ ;;Это преобразование было бы намного проще, если ;;экспонента не содержала значение до -3 ;;А его нужно учитывать при переводе. ;;Поэтому вместо перемножения 64-bits числа ;;Мы перемножаем 96-bits ;; 0) Получим экспоненту в понятном нам виде movsx ecx,word ptr $__exponent mov eax,16383-1 sub ecx,eax ;; Преобразовываем знак экспоненты, поскольку она всегда меньше 0 neg ecx ;; 0*) Выполняем смещение всего числа в право на cl бит xor esi,esi mov edx,$__int64 shrd esi,edx,cl mov eax,$__int64+4 shrd edx,eax,cl shr eax,cl mov $__int64,edx mov $__int64+4,eax ;; 1) Умножить на 5 младшую половину 64-bits числа ;; Инициализируем счётчик точности mov eax,esi mov ecx,20 ;;++++++++++++++++++ Цикл ++++++++++++++++++++++++++++++ floatloop: mul CONSTANT_5 ;; 2) Запомнить младшую часть результата mov esi,eax mov ebx,edx ;; 3) Перемножить старшее слово mov eax,$__int64 mul CONSTANT_5 add eax,ebx adc edx,0 mov $__int64,eax mov ebx,edx ;; И ещё одно старшее слово mov eax,$__int64+4 mul CONSTANT_5 add eax,ebx adc edx,0 ;; mov $__int64+4,eax ;; 4) После этой команды в регистре edx (+ старший бит eax) ;; лежит то самое число ;; которое необходимо поместить в строку ;; Эти несколько команд так же осуществляют сдвиг ;; 96-bits числа на 1 бит в лево. shld edx,eax,1 mov $__int64+4,eax mov byte ptr [edi],dl ;; Помещаем символ в строку mov eax,$__int64 shld $__int64+4,eax,1 inc edi shld eax,esi,1 shl esi,1 mov $__int64,eax dec ecx mov eax,esi jnz short floatloop ;;----------- Преобразование мантиссы (Конец) ---------------- ;;----------- Преобразование экспоненты ecx ------------------ pop eax ;; Сделать error: pop eax fldcw word ptr [esp+2] pop eax xor eax,eax ret error_exp: pop eax jmp error Float_to_String endp ;; ########################################################## |
|
|
Дата: Сен 16, 2003 16:59:08 Edmond ;; 1. Вычисляем степень 10 ;; log10(x) = log10(2)*log2(x) = FYL2X(FLDLG2,x) а нас в школе учили вот так: log10(x) = log2(x)/log2(10) забавно получается, log2(10) = 1/log10(2) :)) |
|
|
Дата: Сен 16, 2003 17:33:23 DaemoniacaL Гм.. :))) И меня учили :)) Надо посмотреть, в чём я ошибся. Но код работает верно. |
|
|
Дата: Сен 16, 2003 17:36:20 Edmond Переведи этим алгоритмом число 10^4000 в строку и посмотри что получится. |
|
|
Дата: Сен 16, 2003 17:43:12 Edmond Ошибки там нет, log2(10) действительно равен 1/log10(2) |
|
|
Дата: Сен 16, 2003 17:47:20 Black_mirror А что разве в регистре ecx не оказывается 4000? ОК. Я проверю. |
|
|
Дата: Сен 16, 2003 17:49:04 Black_mirror Я и говорю забавно, сам проверил :) Думал ошибку у Edmond'a нашел... ан нет... хех :) |
|
|
Дата: Сен 16, 2003 17:50:41 ;; 3. Теперь нужно получить экспоненту 10 - 10^st(0) mov eax,dword ptr $__real10 ;; Нужно сохранить это число для сравнения его на знак mov ecx,eax call _exp10 ;; 4. Деление на экспоненту Вот тут работает ПРАВИЛЬНО!!! Где я ошибся? |
|
|
Дата: Сен 16, 2003 17:52:20 DaemoniacaL Мдя.. Ничего не понимаю :((( Только что посмотрел свою статью про FPU.. И там *!!!! Неужели я ошибся в описании с командой fyl2x???? |
|
|
Дата: Сен 16, 2003 18:27:27 Edmond дело в том что умножать надо на "обратный" логарифм. т.е. если в общем виде: log a (b) = log c (b) / log c (a) то это эквивалентно log a (b) = log c (b) * log a (c) т.к. log a (b) = 1 / log b (a) |
|
|
Дата: Сен 16, 2003 18:46:14 DaemoniacaL Ну вот, сам запутал, сам распутал :)) Спасибо!!! |
|
|
Дата: Сен 17, 2003 01:04:51 Edmond Если процедура _exp10 использует функцию fx2m1 то точность будет плохая. Или у тебя это делается умножениями? |
|
|
Дата: Сен 17, 2003 04:49:52 Edmond Считать логарифм нуля или отрицательного числа бессмысленно, а проверки перед fyl2x отсутствуют. js short @F ;; Если знака не было. jz short @F ;; Если ноль можно заменить на jle @F ;; 0) Получим экспоненту в понятном нам виде movsx ecx,word ptr $__exponent В самом старшем бите находится знак числа, а не экспоненты! Можно обойтись без умножения 96и-битного числа: Если привести мантисту к диапазону [1..10) то сдвигать ее придется не в право, а в лево. После первого сдвига мы получим целую часть в самом старшем слове и дробную в оставшихся 64х битах. Умножая в цикле дробную часть на 10 мы получим оставшиеся цифры. |
|
|
Дата: Сен 17, 2003 12:57:13 Black_mirror А как быть с периодическими дробями? Думаешь цифры закончатся после умножения на 2^64 ? |
|
|
Дата: Сен 17, 2003 16:16:21 DaemoniacaL После умножения на 10^64 они точно закончатся 8) Но выводит больше 20 цифр смысла все равно нет. У меня даже 20я цифра мусор. Нужно придумать как это правильно округлить. |
|
Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.066 |