Уф, не поленился, написал. Code (Text): org 100h mov ax, 0xFFFF ; ffff = 1.999999 push ax mov bx, 0x2AAA ; 1/6 factor mul ax ; ax = x*x : ffff = 3,9999 xchg ax, dx ; dx = ax push ax mul bx shr dx, 2 xor dx, 0x0FFF mov cx, dx ; cx = 1 - x*x/6 (в рамках, что ffff = 15,9999) pop ax mov bx, 0x0222 mul ax ; ax = x^4 xchg ax, dx mul bx ; dx = x^4 / 120 (рамки ffff = 15,9999) add cx, dx ; cx = 1 - x^2/6 + x^4/120 pop ax ; ax = x mul cx ; ax = x*(1 - x^2/6 + x^4/120) xchg ax, dx ; выход: диапазон ax: ffff = 31,999... ; 0001 = 0,00048828125 ; знак ставить в зависимости от того, какой был знак изначально ; то есть лучше брать положительное значение ; Такая вот точность на 16-ти разрядных вычислениях Конечно, оптимизацию по скорости не делал, но точность приличная. Это еще с учетом того, что сам x-x^3/6+x^5/120 не очень хорошую точность выдает. Сам от себя не ожидал, если честно.
Добрый вечер всем Ассемблерщикам! Прошу простить за долгое молчание! Государева служба выбила из колеи на три недели! Большое спасибо всем, кто просматривал тему, особое спасибо всем, кто принял участие в обсуждении. Уважаемый Mikl___! Ваш пример можно брать за основу! Но следует быть осторожным с операцией деления содержимого таблицы на 10000. Дело в том, что если делить «в лоб» BX на 10000d по классическим правилам деления для процессоров ix86, то получим все нули. Необходимо продумать о перемещение содержимого выбранной ячейки таблицы в удвоенное делимое DX:AX для получения наилучшей точности. То есть поиграть с масштабами представления результатов. Вообще-то можно отказаться от деления, если в ячейки сразу записать двоичные значения синусов. И еще один нюанс! Практически во всех технических расчетах используют углы в радианах, а не в градусах. Быстродействие табличного метода получения значения функции самое большое. Однако сама таблица занимает очень много место в памяти! Уважаемый murder! Откровенно говоря, я мало, что понял в Вашей демке! Не могли бы Вы прокомментировать ее работу!? Уважаемый Ken! Будет ли Ваше программа (ой простите - разумеется прога) работать для отрицательных промежуточных переменных. Например, после mov cx, dx ; cx = 1 - x*x/6 (в рамках, что ffff = 15,9999) – в сх может быть отрицательное число. Как быть дальше? Вот мой пример реализации полинома y= x^5/120 -x^3/6+x для аргумента изменяющегося в диапазоне от -2 до +2. После упрощения имеем y=x*((x^2/6)*((x^2/20)-1)+1) mov ax,x mov bx,ax mov di,ax imul bx shld dx,ax,1 sal ax,1 push dx ;y1=x*x push ax ;---------------------------- mov cx,5000h idiv cx ;y2=y1/20 ;------------------------------- sar ax,3 sub ax,4000h ;y3=y2-1 mov bx,ax ;---------------------------- pop ax pop dx mov cx,6000h idiv cx ;y4=y1/6 ;-------------------------- imul bx shld dx,ax,1 ; y5=y3*y4 ;--------------------------------- add dx,4000h ;y6=y5+1 ;------------------------------- mov ax,di imul dx shld dx,ax,3 ; y=y7=y6*x mov y,dx В этом случае не надо думать о логике запоминания знака аргумента, выбора квадранта синуса, аргумент может изменяться во всем диапазоне с шагом 0001F. Ошибка 16-ти разрядной программы по сравнению с Exel-полином - 0,01%. Ошибка по сравнению с Exel-SIN(X) – 2,4% на краях интервала. Если сократить интервал до +-1,65, то есть приблизить к +- ПИ/2, при этом программу не переделывать - ошибка по SIN(X) уменьшится до 0,5% . Если чуть доработать программу для укороченного интервала аргумента с целью увеличения точности – ошибка уменьшиться до 0,2% Жду ваши комментарии коллеги! С уважением, 1212
1212 1) Фраза "Они должны быть разделены на 10000 перед использованием" подразумевала, что деление должно производится после умножения Sin(x) на что-то т.е. Y*Sin(x)/10000 2) "Практически во всех технических расчетах используют углы в радианах, а не в градусах" ну и заполните таблицу значениями от Sin(x) в радианах 3) "Однако сама таблица занимает очень много место в памяти!" в примере показано, как используя симметрию и значения синуса от 0 до 90º расчитывают синус от 0 до 360º - первоначальный вариант таблицы сокращен в 4 раза - аналогично, используя например тригонометрическое преобразование Sin(2*x)=2*Sin(x)*Cos(x)= 2*Sin(x)*Sin(x+90º) можно сократить число значений и в этой таблице в два раза - держать в таблице нечетные значения x, а четные равные 2*x считать по формуле либо расчитывать значение для четного значения х как среднее от двух нечетных значений между которыми он находится - уменьшение величины таблицы приведет в свою очередь к увеличению вычислительного алгоритма, продолжая уменьшать количество значений в таблице прийдем в конечном итоге к формуле Sin(x) = x - x³/3! + (x^5)/5! - (x^7)/7! + (x^9)/9! - ... уменьшение размера таблицы, приведет к пропроциональному возрастанию времени обработки и увеличению размера программы - ищите компромиссное решение
Уважаемый Mikl___ , спасибо за ответ! Вы совершенно правы, когда советуете искать оптимальный вариант программы определения значения функции в соединении табличного и вычислительного методов. В каждом конкретном случае необходимо искать свой оптимум. Методы сокращения объема таблиц функций хорошо описаны, например, в книге А.М. Оранского «Аппаратные методы в цифровой вычислительной технике» . Известные свойства синуса-косинуса можно использовать для сокращения числа основных ячеек таблицы. Достаточно рассмотреть значения в диапазоне от 0 до ПИ/4. Более подробно этот алгоритм расписан у Григорьева «Микропроцессор i486». Однако в приводимом у него практическом варианте есть ошибка в логике. Очень интересна книга В.В. Чекушин, О.В. Юрин, В.В. Булкин «Реализация вычислительных процессов в информационно-измерительных системах» (2005 года). Авторы этой книги, как я (я с ними не знаком), пришли к выводу, что для многих современных микропроцессоров лучше программировать вычислительные алгоритмы, тратя время на разработку математики. В этом смысле и была мною открыта тема. Мои примеры - вычисление с высокой точностью полиномов на машинах с фиксированной запятой, то есть с малой разрядностью. Меня очень интересует, вполне ли мои подходы приемлемы, есть ли другие подходы?