Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

16 bit fixed point SQRT for PIC16

199 views
Skip to first unread message

Dmitry Orlov

unread,
Feb 19, 2002, 10:05:00 AM2/19/02
to
Hi All!

А не завалялось ли у кого асмовской реализации (желательно или вообще без
использования стека или с минимальным) subj? Я пока что использую где-то
откопанный такой код:

word src;
word tmp_src;
word tmp_dst;
word mask;
byte count;

/* Square root calculation */
dst = 0;
tmp_src = 0;
tmp_dst = 0;
mask = 0xFFFF;
src = SVoutRMS;

for(count = 0; count < 8; count++)
{
src &= (mask >> count*2);
tmp_src <<= 2;
tmp_src |= src >> (7 - count)*2;
tmp_dst = dst;
dst <<= 1;
if((tmp_src != 0 )||(dst!=0))
{
tmp_dst <<= 2;
tmp_dst += 1;
if(tmp_dst <= tmp_src)
{
dst += 1;
tmp_src -= tmp_dst;
}
}
}
tmp_src <<= 2;
tmp_dst = dst;
tmp_dst <<= 2;
tmp_dst += 1;
if (tmp_dst <= tmp_src) dst += 1;

В принципе он меня устраивает, но может быть есть что-то существенно более
эффективное по скорости, или размеру?


С уважением, Дима Орлов.

Alexandr A. Redchuck

unread,
Feb 19, 2002, 3:12:56 PM2/19/02
to
19-Feb-02 18:05 Dmitry Orlov wrote to All:

DO> А не завалялось ли у кого асмовской реализации (желательно или вообще
DO> без
DO> использования стека или с минимальным) subj? Я пока что использую
DO> где-то откопанный такой код:
[^Y]
DO> for(count = 0; count < 8; count++) {
DO> src &= (mask >> count*2);
DO> tmp_src <<= 2;
DO> tmp_src |= src >> (7 - count)*2;
DO> tmp_dst = dst;
DO> dst <<= 1;
DO> if((tmp_src != 0 )||(dst!=0)) {
DO> tmp_dst <<= 2;
DO> tmp_dst += 1;
DO> if(tmp_dst <= tmp_src) {
DO> dst += 1;
DO> tmp_src -= tmp_dst;
DO> }
DO> }
DO> }
DO> tmp_src <<= 2;
DO> tmp_dst = dst;
DO> tmp_dst <<= 2;
DO> tmp_dst += 1;
DO> if (tmp_dst <= tmp_src) dst += 1;

DO> В принципе он меня устраивает, но может быть есть что-то существенно
DO> более эффективное по скорости, или размеру?
Думаю, что и по скорости, и по размеру. Здесь совсем не оптимизированы
сдвиги, разве можно делать столько многобайтных сдвигов на переменную
величину. Мог бы хоть их почистить...

В ru.algorithms когда-то этот вопрос поднимался, Винокуров Андрей приводил
объяснение "школьного" метода "цифра за цифрой" ("корень в столбик") для
десятичной системы и его асм386 реализацию в двоичном виде.
Ниже выдержка из моего письма туда же с несколько другим методом (регистр
последовательных приближений).
В асмовой реализации для 386 он дал сравнимое быстродействие.
Тут я приведу только С-шный кусок письма.

From: "Alexandr A. Redchuck" <re...@real.kiev.ua>
Newsgroups: fido7.ru.algorithms
Subject: [NEWS] isqrt
Date: 12 Apr 1998 16:51:28 +0400

> РПП:
> поднимаем в регистре старший бит. Если функция содержимого регистра
> меньше цели, оставляем этот бит, если больше - сбрасываем.
> Повторяем вправо со всеми битами до младшего.
>
> В тупом варианте для корня:
>
> unsigned short sar_sqrt(unsigned long from) {
> unsigned short sqr,mask=0x8000;
> do {
> sqr |= mask;
> if( sqr*sqr > from ) sqr &= ~mask;
> } while( mask >>= 1 );
> return sqr;
> }
>
> Дальше для ухода от возведения в квадрат переходим к постепенному
> приближению. Используем
> (sqr+mask)*(sqr+mask) - sqr*sqr = 2*sqr*mask + mask*mask
> и учитываем, что в mask поднята только одна единичка, т.е. умножение
> на нее это сдвиг (в десятичном варианте таки надо множить -- стоИт
> (10*2*R+t)*t и t приходится искать "на глаз" среди 10 весов разряда).
> Можно гнать цикл по j от 15 до 0 и превратить
> 2*sqr*mask + mask*mask
> в
> (sqr << (j+1)) + ( 1 << (j+j) )
> но при этом много сдвигов на переменную величину, которые не везде
> легко делаются.
>
> После оптимизации сдвигов
>
> unsigned short isqrt( unsigned long from) {
> unsigned long mask = 0x40000000, sqr = 0, temp;
> do {
> temp = sqr | mask;
> sqr >>= 1;
> if( temp <= from ) {
> sqr |= mask;
> from -= temp;
> }
> } while( mask >>= 2 );
> // можно дать еще округление if( sqr < from ) ++sqr;
> return (unsigned short)sqr;
> }

Тут для корня из длинного, для корня из 16-битного надо соответственно
уменьшить разрядность переменных и начать с маски 0x4000.

wbr,
--
/* Alexandr Redchuck, Kyiv, Ukraine */
/* real на real тчк kiev тчк ua */

Dmitry Orlov

unread,
Feb 20, 2002, 3:26:00 AM2/20/02
to
Hello, Alexandr A. Redchuck !

> DO> В принципе он меня устраивает, но может быть есть что-то существенно
> DO> более эффективное по скорости, или размеру?

> Думаю, что и по скорости, и по размеру. Здесь совсем не оптимизированы
> сдвиги, разве можно делать столько многобайтных сдвигов на
> переменную величину. Мог бы хоть их почистить...

Я просто пробовал как есть. Скорость на самом деле мне не особо критична, за
20ms успевает, и ладно (реально тот кусок где-то 500us при моей тактовой
выполнялся)

> В ru.algorithms когда-то этот вопрос поднимался, Винокуров Андрей
> приводил объяснение "школьного" метода "цифра за цифрой" ("корень в
> столбик") для десятичной системы и его асм386 реализацию в двоичном виде.

> Hиже выдержка из моего письма туда же с несколько другим методом


> (регистр последовательных приближений).
> В асмовой реализации для 386 он дал сравнимое быстродействие.
> Тут я приведу только С-шный кусок письма.

>> unsigned short isqrt( unsigned long from) {


>> unsigned long mask = 0x40000000, sqr = 0, temp;
>> do {
>> temp = sqr | mask;
>> sqr >>= 1;
>> if( temp <= from ) {
>> sqr |= mask;
>> from -= temp;
>> }
>> } while( mask >>= 2 );
>> // можно дать еще округление if( sqr < from ) ++sqr;
>> return (unsigned short)sqr;
>> }

> Тут для корня из длинного, для корня из 16-битного надо соответственно
> уменьшить разрядность переменных и начать с маски 0x4000.

О, а вот это работает почти в 5 раз быстрей и занимает примерно на 100 слов
меньше места (всего 46 слов!). Большое спасибо, ты здорово помог.


С уважением, Дима Орлов.

Vladimir Poletaev

unread,
Feb 20, 2002, 1:01:08 PM2/20/02
to
Hi, Dmitry!

Dmitry Orlov wrote in a message to All:


DO> В принципе он меня устраивает, но может быть есть что-то
DO> существенно более эффективное по скорости, или размеру?

Если важен размер (но не скорость!), то можно суммировать нечетные числа:

1 = 1 = 1^2
1 + 3 = 4 = 2^2
1 + 3 + 5 = 9 = 3^2 и т.д.

Так что суммируем и сравниваем с исходным числом. Hомер итерации - искомый
корень. Для 16 бит - не более 256 итераций, так что счетчик может быть
байтовым.

With best regards...
Vladimir.

0 new messages