реклама на сайте
подробности

 
 
> Вычисления на ATmega88
koluna
сообщение Dec 2 2008, 14:00
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Здравствуйте!

Использую WinAVR, ATmega88, сигнал синхронизации - внтутренняя RC-цепь (частота 8 МГц).
Необходимо как можно быстрее вычислить значение следующего выражения:

C = Y * C' / 255.
Причём:
C, Y, C' - unsigned char, т. е., действительные числа мне не нужны...

В данный момент вычисления производятся приблизительно за 92 мкс (в программе три выражения подряд).

Можно, конечно, кварц поставить на большую частоту, но хотелось бы пока без него smile.gif

Фрагмент программы:

Код
...
OCR0A = n_red * dmx[0] / 255;
OCR0B = n_green * dmx[0]  / 255;
OCR2B = n_blue * dmx[0] / 255;
...



Спасибо заранее!


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
3 страниц V   1 2 3 >  
Start new topic
Ответов (1 - 34)
Непомнящий Евген...
сообщение Dec 2 2008, 14:12
Сообщение #2


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



а если поделить на 256? Компилер должен превратить такое деление в сдвиг (или можно руками написать >>8).
Разница будет довольно мала...
Go to the top of the page
 
+Quote Post
Арк К
сообщение Dec 2 2008, 14:16
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 45
Регистрация: 8-05-08
Пользователь №: 37 363



А надо делить именно на 255? не на 256?
тогда было бы нужно просто выкинуть младший байт
Критичные ко времени места лучше делать на ассемблере.
Go to the top of the page
 
+Quote Post
Непомнящий Евген...
сообщение Dec 2 2008, 14:24
Сообщение #4


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



Цитата(Арк К @ Dec 2 2008, 17:16) *
Критичные ко времени места лучше делать на ассемблере.


Тут засада будет скорее именно в делении. И если от него не избавиться - врядли на асме получится сильно быстрее...
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Dec 2 2008, 14:24
Сообщение #5


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(n_bogoyavlensky @ Dec 2 2008, 18:00) *
Код
...
OCR0A = n_red * dmx[0] / 255;
OCR0B = n_green * dmx[0]  / 255;
OCR2B = n_blue * dmx[0] / 255;
...

Спасибо заранее!


Используйте предварительно посчитанные значения K=M*dmx[0]/255
где М выбирается таким, чтобы получалось то, что делается быстрее, т.е. умножение на К и взятие старшего байта произведения.
асмовый текст, раз уж заговорили про скорость
Код
  lds r24,K
  lds r25,n_red
  mul r24,r25
  out OCR0A,r1
  lds r25,n_green
  mul r24,r25
  out OCR0A,r1
  lds r25,n_blue
  sts OCR2A,r1

Надеюсь, принцип понятен.
Go to the top of the page
 
+Quote Post
=GM=
сообщение Dec 2 2008, 14:52
Сообщение #6


Ambidexter
*****

Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282



Цитата(n_bogoyavlensky @ Dec 2 2008, 14:00) *
Необходимо как можно быстрее вычислить значение следующего выражения: C = Y * C' / 255

C=(Y*C1*257) >> 16;

Или совсем уж убыстрить

С2=Y*C1;
C=((C2 << 8)+С2) >> 16;

На асме просто взять два старших байта.


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
Непомнящий Евген...
сообщение Dec 2 2008, 15:00
Сообщение #7


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



Цитата(=GM= @ Dec 2 2008, 17:52) *
C=(Y*C1*257) >> 16;


Тогда уж так

C=((unsigned long)Y*C1*257) >> 16;

- у топик стартера Y и С1 - unsigned char

Кстати, классное решение a14.gif
Go to the top of the page
 
+Quote Post
=GM=
сообщение Dec 2 2008, 15:05
Сообщение #8


Ambidexter
*****

Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282



Цитата(Непомнящий Евгений @ Dec 2 2008, 15:00) *
Тогда уж так C=((unsigned long)Y*C1*257) >> 16;
Кстати, классное решение

А то! Но всё-ж-таки, нормальный компилер должен сам типы приводить.


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
Непомнящий Евген...
сообщение Dec 2 2008, 15:14
Сообщение #9


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



Цитата(_Pasha @ Dec 2 2008, 17:24) *
Используйте предварительно посчитанные значения K=M*dmx[0]/255
где М выбирается таким, чтобы получалось то, что делается быстрее, т.е. умножение на К и взятие


Не могли бы вы пояснить эту мысль? К зависит от двух 1-байтных чисел, объем таблицы получится 65536 байт... Или я вас не так понял?

Цитата(=GM= @ Dec 2 2008, 17:52) *
C=(Y*C1*257) >> 16;

Или совсем уж убыстрить

С2=Y*C1;
C=((C2 << 8)+С2) >> 16;


А кстати, нифига smile.gif

(С2*257) >> 16 =( (С2 <<8) + C2)>>16 = ((C2<<8)>>16) + (C2>>16) = C2>>8 = C2 / 256 ...

сдвиг на 16 - это ж деление на 65536, а не на 65535 smile.gif
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Dec 2 2008, 15:20
Сообщение #10


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(Непомнящий Евгений @ Dec 2 2008, 19:14) *
Не могли бы вы пояснить эту мысль? К зависит от двух 1-байтных чисел, объем таблицы получится 65536 байт... Или я вас не так понял?

Я обратил внимание на то, что эта часть dmx[0] / 255; должна быть вычислена один раз и больше не участвовать в окончательных выражениях. Таблицей, конечно, не получится
ЗЫ - наверное, автору все-таки нужно сразу делить на 256, потому что разница в 3 промилле, наверняка, не важна.
Go to the top of the page
 
+Quote Post
LordVader
сообщение Dec 2 2008, 16:48
Сообщение #11


Частый гость
**

Группа: Участник
Сообщений: 127
Регистрация: 18-10-06
Пользователь №: 21 418



Вот что нагуглилось: http://www.sharpthinking.net/cv/div255.pdf
Быстрое деление на 255. Вкратце: a/255 = ((a>>8)+a+128)>>8 с округлением к ближайшему целому.
Go to the top of the page
 
+Quote Post
=GM=
сообщение Dec 2 2008, 16:53
Сообщение #12


Ambidexter
*****

Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282



Цитата(Непомнящий Евгений @ Dec 2 2008, 15:14) *
(С2*257) >> 16 =( (С2 <<8) + C2)>>16 = ((C2<<8)>>16) + (C2>>16) = C2>>8 = C2 / 256 ...
сдвиг на 16 - это ж деление на 65536, а не на 65535 smile.gif

Так и задумывалось, делить на 65536, чтобы был сдвиг на 16 бит, а не деление на 65535.

Вот здесь ошибочка у вас ((C2<<8)>>16) + (C2>>16) = C2>>8+(C2>>16).

Округление можно так сделать C=((C2 << 8)+С2+32768) >> 16;


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
VDG
сообщение Dec 2 2008, 22:02
Сообщение #13


Знающий
****

Группа: Участник
Сообщений: 845
Регистрация: 10-02-06
Пользователь №: 14 193



Человек скорее всего управляет RGB-светодиодом, а деление на 255 - это соответственно "нормализация в восьмибитное число" после умножения на коэффициент яркости. В таком случае, деление на 255 - ошибка. На 256 надо делить, или как выше сказали - взять HIBYTE.


--------------------
Go to the top of the page
 
+Quote Post
Непомнящий Евген...
сообщение Dec 3 2008, 05:26
Сообщение #14


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



Цитата(=GM= @ Dec 2 2008, 19:53) *
Вот здесь ошибочка у вас ((C2<<8)>>16) + (C2>>16) = C2>>8+(C2>>16).


С2 = У * С1

У и С1 - unsigned char, т.е. их произведение <=65535. Сдвиг на 16 влево такого числа даст 0. Поэтому (C2>>16) == 0

Цитата
Так и задумывалось, делить на 65536, чтобы был сдвиг на 16 бит, а не деление на 65535.


Ну просто это получается эквивалентно C2/256. Просто малость замаскировано smile.gif
Go to the top of the page
 
+Quote Post
koluna
сообщение Dec 3 2008, 08:26
Сообщение #15


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Цитата(Арк К @ Dec 2 2008, 18:16) *
А надо делить именно на 255? не на 256?
тогда было бы нужно просто выкинуть младший байт
Критичные ко времени места лучше делать на ассемблере.


Понятно, что на ассемблере быстрее будет работать...
Но я не знаю как сопрячь WinAVR C и ассемблер sad.gif

Цитата(_Pasha @ Dec 2 2008, 18:24) *
Используйте предварительно посчитанные значения K=M*dmx[0]/255
где М выбирается таким, чтобы получалось то, что делается быстрее, т.е. умножение на К и взятие старшего байта произведения.
асмовый текст, раз уж заговорили про скорость
Код
  lds r24,K
  lds r25,n_red
  mul r24,r25
  out OCR0A,r1
  lds r25,n_green
  mul r24,r25
  out OCR0A,r1
  lds r25,n_blue
  sts OCR2A,r1

Надеюсь, принцип понятен.


К сожалению принцип не понятен пока.
Сколько нужно памяти для предварительно посчитанных значений?

Цитата(_Pasha @ Dec 2 2008, 19:20) *
Я обратил внимание на то, что эта часть dmx[0] / 255; должна быть вычислена один раз и больше не участвовать в окончательных выражениях. Таблицей, конечно, не получится
ЗЫ - наверное, автору все-таки нужно сразу делить на 256, потому что разница в 3 промилле, наверняка, не важна.


f = dmx[0] / 255 делается действительно 1 раз.
Я вынес эту операцию в другое место.
Теперь делаю только С = C' * f.
Время выполнения сократилось приблизительно на 10 мкс (кстати, я ещё кварц на 20 МГц поставил).
Но хотелось бы ещё побыстрее...


Цитата(VDG @ Dec 3 2008, 02:02) *
Человек скорее всего управляет RGB-светодиодом, а деление на 255 - это соответственно "нормализация в восьмибитное число" после умножения на коэффициент яркости. В таком случае, деление на 255 - ошибка. На 256 надо делить, или как выше сказали - взять HIBYTE.


Ими и управляю. Поясню.

Регулирую яркость 8-битным ШИМом микроконтроллера.
Имеем 4 внешних контролла, от которых зависит результирующий цвет луча и его интенсивность.

1 - Общая световая интенсивность dmx[0].
2 - Интенсивность красного n_red.
3 - Интенсивность зелёного n_green.
4 - Интенсивность синего n_blue.

Т. е., моя задача - вычислить значение 8-битного регистра сравнения OCR микроконтроллера, которое зависит от dmx[0] и n_red/n_green/n_blue.

Придумал управлять так:
Код
OCR0A = n_red * dmx[0] / 255;
OCR0B = n_green * dmx[0]  / 255;
OCR2B = n_blue * dmx[0] / 255;


Но подвело меня деление...

Почему надо делить на 256?
Ясное дело, это сдвиг вправо на 8 - выполняется молниеносно, но почему 256?
Т. е., просто согласиться с появляющейся ошибкой?

Сообщение отредактировал n_bogoyavlensky - Dec 3 2008, 08:26


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Dec 3 2008, 08:55
Сообщение #16


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(n_bogoyavlensky @ Dec 3 2008, 12:26) *
Т. е., просто согласиться с появляющейся ошибкой?

Я ж говорю 256/255 = 1.0039 Точнее - 4 промилле.
Цитата
Но я не знаю как сопрячь WinAVR C и ассемблер

Сишка:
Код
uint8_t n_red,n_green,n_blue;
uint8_t precomputed;
extern void fast_out(void);

Добавляете в проект файл fast_out.S
Код
#include <avr/io.h>
.extern n_red,n_blue,n_green
.extern precomputed

fast_out:
  lds r24,precomputed
  lds r25,n_red
  mul r24,r25
  out OCR0A,r1
  lds r25,n_green
  mul r24,r25
  out OCR0B,r1
  lds r25,n_blue
  sts OCR2A,r1
  ret
// put the global declaration to export function name

.global fast_out


Уже в таком виде должно собраться.
В данном примере precomputed - это Ваше С'
Но, скорее всего, Вам придется эту часть, с предварительными вычислениями тоже в асм внести.
Успехов!

В догонку.
Деление на 255 в данном случае можно заменить делением на 256 и учетом одного исключения:
Код
if (c !=255) a = (b*c)/256; else a=b;

Просьба не воспринимать это как сишный текст smile.gif
Go to the top of the page
 
+Quote Post
koluna
сообщение Dec 3 2008, 09:46
Сообщение #17


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Цитата
В догонку.
Деление на 255 в данном случае можно заменить делением на 256 и учетом одного исключения:
Код
if (c !=255) a = (b*c)/256; else a=b;

Просьба не воспринимать это как сишный текст smile.gif


И как паскалевский, и как ассемблерный... ясен перец smile.gif

За быстрое деление на 255 - всем спасибо.
Работает. Быстро работает smile.gif

Сообщение отредактировал n_bogoyavlensky - Dec 3 2008, 09:47


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
=GM=
сообщение Dec 3 2008, 11:00
Сообщение #18


Ambidexter
*****

Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282



Цитата(Непомнящий Евгений @ Dec 3 2008, 05:26) *
Ну просто это получается эквивалентно C2/256. Просто малость замаскировано smile.gif

Вы не правы, это НЕ эквивалентно C2/256, это примерно эквивалентно C2/257. Судите сами, C2/255 = C2/(65536/257)=С2*257/65536=(С2*257)>>16. А примерно эквивалентно потому, что вы делите на 65536/257=255.0039, а не точно на 255.

Что касается вашей формулы (С2*257) >> 16 =( (С2 <<8) + C2)>>16 = ((C2<<8)>>16) + (C2>>16) = C2>>8 = C2/256, то она неверна. Два слагаемых перекрываются на 8 бит, и меньшее слагаемое нельзя просто отбросить, поскольку при суммировании оно может дать перенос в старший байт, который и будет окончательным результатом.

Вот пример. Пусть С2=0x8899=34969, тогда C2*256=0x889900,
C2*257=C2*256+C2=0x889900+0x8899=0x892199
и, наконец, С=C2*257>>16=0x89=137. Простая проверка: С=34969/255=137.1

По вашей формуле С=С2>>8=0x88=136

Вывод такой. Если хотите поделить на 255, то лучше делить на 255.0039, чем на 256. Всяко точнее будет. Вообще такой подход хорош при делении на константу.


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
koluna
сообщение Dec 12 2008, 20:39
Сообщение #19


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Вот ещё одна задача.
Как аналогичным образом целочисленно поделить одно 8-битное число на другое 8-битное число?
Более конкретно - деление на 10.


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
singlskv
сообщение Dec 12 2008, 21:13
Сообщение #20


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Дык все точно так же,
умножте на 26(~256/10) или на 6554(~65536/10) ну и если нужно скоректируйте...
Go to the top of the page
 
+Quote Post
Александр Куличо...
сообщение Dec 12 2008, 23:29
Сообщение #21


Местный
***

Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017



Цитата(n_bogoyavlensky @ Dec 12 2008, 22:39) *
Вот ещё одна задача.
Как аналогичным образом целочисленно поделить одно 8-битное число на другое 8-битное число?
Более конкретно - деление на 10.


Для диапазона A=[0..255): (unsigned char)(А+1)*51>>9

Для диапазона A=[0..255] ((unsigned char)(А*51) + 51)>>9

или на асме соответственно
Код
    ldi    temp,51
    inc    A    
    mul    A,temp
    lsr    R1

и
    ldi    temp,51
    mul    A,temp
    add    R0,temp
    adc    R1,zero
    lsr    R1
Go to the top of the page
 
+Quote Post
koluna
сообщение Dec 13 2008, 11:33
Сообщение #22


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Цитата
Для диапазона A=[0..255): (unsigned char)(А+1)*51>>9

Для диапазона A=[0..255] ((unsigned char)(А*51) + 51)>>9


Спасибо! smile.gif
Кстати, откуда вы с такой лёгкостью извлекаете эти волшебные выражения? smile.gif
Они где-то перечислены или их можно достаточно просто придумать самому? Каков принцип?

А для диапазона A=[0..65535]?

Цитата(singlskv @ Dec 13 2008, 01:13) *
Дык все точно так же,
умножте на 26(~256/10) или на 6554(~65536/10) ну и если нужно скоректируйте...



Всмысле?
Что умножить и как скорректировать?


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
singlskv
сообщение Dec 13 2008, 12:06
Сообщение #23


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(n_bogoyavlensky @ Dec 13 2008, 14:33) *
Всмысле?
Что умножить и как скорректировать?

Именно так как в посте №21

51 ~= 256 * 2 / 10

+ 51 корректировка
Go to the top of the page
 
+Quote Post
koluna
сообщение Dec 13 2008, 12:16
Сообщение #24


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Цитата(singlskv @ Dec 13 2008, 16:06) *
Именно так как в посте №21

51 ~= 256 * 2 / 10

+ 51 корректировка


Всё равно пока не пойму каков принцип? sad.gif


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
ae_
сообщение Dec 13 2008, 12:56
Сообщение #25


Участник
***

Группа: Свой
Сообщений: 462
Регистрация: 2-04-07
Из: Иркутск
Пользователь №: 26 695



x/10 = x*(1/10) = x*(205/2048) = (x*205)>>11
x/10 = x*(1/10) = x*(52429/524288) = (x*52429)>>19
Go to the top of the page
 
+Quote Post
xemul
сообщение Dec 13 2008, 12:56
Сообщение #26



*****

Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731



Цитата(n_bogoyavlensky @ Dec 13 2008, 15:16) *
Всё равно пока не пойму каков принцип? sad.gif

x/10 ~= x*51/512 = (x*51)>>9
или x/10 ~= x*26/256 = (x*26)>>8
или ...
подберите что-нибудь, устраивающее по точности.
Для корректного целочисленного округления все-таки д.б. ((x*51)+51/2)>>9 ~= ((x*51)+26)>>9
Go to the top of the page
 
+Quote Post
SSerge
сообщение Dec 13 2008, 13:00
Сообщение #27


Профессионал
*****

Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528



Цитата(n_bogoyavlensky @ Dec 13 2008, 18:16) *
Всё равно пока не пойму каков принцип? sad.gif

Дык, a/b преобразуется в a*(N/b) / N, при подходящем выборе N (степень двойки) выражение N/b вычисляется ещё на этапе компиляции, а умножение и последующее деление на N (сдвигами или отбрасыванием младших байтов результата) выполняется быстрее чем просто деление на b, особенно при наличии аппаратного умножителя.

Добавлю ещё, что если b не константа то подобный метод тоже срабатывает - есть достаточно эффективные алгоритмы вычисления 1/b (или N/b). Всё из-за того, что аппаратный делитель выходит заметно сложнее умножителя, вот на нём и экономят, даже в суперкомпьютерах.


--------------------
Russia est omnis divisa in partes octo.
Go to the top of the page
 
+Quote Post
koluna
сообщение Dec 13 2008, 18:34
Сообщение #28


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Цитата(xemul @ Dec 13 2008, 16:56) *
x/10 ~= x*51/512 = (x*51)>>9
или x/10 ~= x*26/256 = (x*26)>>8
или ...
подберите что-нибудь, устраивающее по точности.
Для корректного целочисленного округления все-таки д.б. ((x*51)+51/2)>>9 ~= ((x*51)+26)>>9


С делением теперь понятно. Спасибо smile.gif
С округлением до ближайшего целого не понятно...


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
xemul
сообщение Dec 13 2008, 20:41
Сообщение #29



*****

Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731



Цитата(n_bogoyavlensky @ Dec 13 2008, 21:34) *
С делением теперь понятно. Спасибо smile.gif
С округлением до ближайшего целого не понятно...

Спасибо, был неправ. Вместо
((x*51) + 51/2) >> 9 ~= ((x*51)+26)>>9
читать
((x*51) + 1<<8) >> 9 = (x*51)>>9 + 0.5

Правила округления до целого предполагают, что (н-р, применительно к десятичной системе счисления) если дробная часть = [0.0..0.4(9)], то число округляется "вниз" (дробная часть просто отбрасывается), если дробная часть = [0.5..0.9(9)], то число округляется "вверх" (дробная часть отбрасывается, к целой части добавляется 1).
Контроллер при целочисленных операциях может только отбросить дробную часть. Чтобы заставить его округлять "по-человечески", нужно перед целочисленными операциями с потерей точности (типа деление/сдвиг) добавить к операнду половину разряда, который станет младшим значащим разрядом после выполнения операции (== половину делителя).

Округляем до целого в десятичной системе:
половина значащего разряда = 0.5
trunc(31.49 + 0.5) = 31
trunc(31.5 + 0.5) = 32

Округляем до целого в двоичной системе:
пусть 0b10100101 (= 165 дес.) хочется сдвинуть вправо на 3 разряда
половина делителя = 1 << (3 - 1) = 0b100
0b10100101 >> 3 = 0b10100 (= 20 дес.)
(0b10100101 + 0b100) >> 3 = 0b10101 (= 21 дес.)
165/8 = 20.625 ~= 21
Go to the top of the page
 
+Quote Post
singlskv
сообщение Dec 13 2008, 21:33
Сообщение #30


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(xemul @ Dec 13 2008, 23:41) *
Спасибо, был неправ. Вместо
((x*51) + 51/2) >> 9 ~= ((x*51)+26)>>9
читать
((x*51) + 1<<8) >> 9 = (x*51)>>9 + 0.5

на самом деле рассчет коррекции проще всего делать так:
- пример с домножением на 51
- пусть коррекция = A, те ((x * 51) + A) / 512 ~= x / 10
- тогда д.б.:
1.) 0 * 51 +A >= 0
2.) 9 * 51 +A < 1 * 512
3.) 10 * 51 + A >= 1 * 512
4.)250 * 51 + A >= 25 * 512
5.)255 * 51 + A < 26 * 512
- преобразуем
1.) A >= 0
2.) A < 53
3.) A >= 2
4.) A >= 50
5.) A < 307

-итого: 50<= A < 53

то есть нам подойдут 50, 51, 52

выбор чисел 0, 9, 10, 250, 255 надеюсь понятен
Go to the top of the page
 
+Quote Post
xemul
сообщение Dec 13 2008, 22:41
Сообщение #31



*****

Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731



Цитата(singlskv @ Dec 14 2008, 00:33) *
на самом деле рассчет коррекции проще всего делать так:
...

Вы абсолютно правы - я забыл про исходную постановку задачи и плавно на простое округление.
Go to the top of the page
 
+Quote Post
Александр Куличо...
сообщение Dec 13 2008, 23:35
Сообщение #32


Местный
***

Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017



Цитата(singlskv @ Dec 13 2008, 23:33) *
-итого: 50<= A < 53
то есть нам подойдут 50, 51, 52

выбор чисел 0, 9, 10, 250, 255 надеюсь понятен


Действительно, простой расчет smile.gif.
Но A = [50..52] (как и приведенная мной формула, где А=51)- это для случая деления с отбрасыванием дробной части, т.е для выражения X div 10.

Для деления с округлением, воспользовавшись Вашей методикой, получаем
1.) 0 * 51 +A >= 0
2.) 4 * 51 +A < 1 * 512
3.) 5 * 51 + A >= 1 * 512
4.)254 * 51 + A < 26 * 512
5.)255 * 51 + A >= 26 * 512
- преобразуем
1.) A >= 0
2.) A < 308
3.) A >= 257
4.) A <358
5.) A >= 307

Откуда A = 307.

P.S. Существуют ли алгоритмы для быстрого получения остатка от деления на 10, 100 (кроме варианта X - (X div 10)*10 )?

Цитата
Вместо
((x*51) + 51/2) >> 9 ~= ((x*51)+26)>>9
читать
((x*51) + 1<<8) >> 9 = (x*51)>>9 + 0.5

Здесь A = 1<<8 = 512. При x=11 получаем в результате 2.
Go to the top of the page
 
+Quote Post
ae_
сообщение Dec 14 2008, 11:51
Сообщение #33


Участник
***

Группа: Свой
Сообщений: 462
Регистрация: 2-04-07
Из: Иркутск
Пользователь №: 26 695



Так, что бы во всём диапазоне 0...255 / 10 выполнялось с округлением правильно:
Код
0....4 / 10 = 0
5...14 / 10 = 1
15...24 / 10 = 2
25...34 / 10 = 3
35...44 / 10 = 4
45...54 / 10 = 5
55...64 / 10 = 6
65...74 / 10 = 7
75...84 / 10 = 8
85...94 / 10 = 9
95..104 / 10 = 10
105..114 / 10 = 11
115..124 / 10 = 12
125..134 / 10 = 13
135..144 / 10 = 14
145..154 / 10 = 15
155..164 / 10 = 16
165..174 / 10 = 17
175..184 / 10 = 18
185..194 / 10 = 19
195..204 / 10 = 20
205..214 / 10 = 21
215..224 / 10 = 22
225..234 / 10 = 23
235..244 / 10 = 24
245..254 / 10 = 25
     255 / 10 = 26

У меня получилось только с множителем >= 205 по формуле (1+(x*205)>>10)>>1

Тоже самое на асме:
Код
     ldi R16, 205
     mul DATA, R16
     lsr R1
     lsr R1
     inc R1
     lsr R1
     mov DATA, R1
Go to the top of the page
 
+Quote Post
koluna
сообщение Dec 15 2008, 09:59
Сообщение #34


Профессионал
*****

Группа: Участник
Сообщений: 1 040
Регистрация: 3-01-07
Пользователь №: 24 061



Кстати, а что за метод в 11 посте?
Там логика другая...

Кстати, для делимого, находящегося в диапазоне unsigned long деление на 255 как будет выглядеть?


--------------------
Благодарю заранее!
Go to the top of the page
 
+Quote Post
SSerge
сообщение Dec 15 2008, 11:33
Сообщение #35


Профессионал
*****

Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528



Цитата(n_bogoyavlensky @ Dec 15 2008, 15:59) *
Кстати, а что за метод в 11 посте?
Там логика другая...

Там используют известное разложение в ряд 1/(1+x) ~ 1-x для x много меньше 1.
Получается
a/(256-1) = a/256 * 1/(1-1/256) ~ a/256 * (1 + 1/256) = (a + a/256)/256,
а 128 там прибавляют для правильного округления до ближайшего целого.


--------------------
Russia est omnis divisa in partes octo.
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 27th June 2025 - 10:38
Рейтинг@Mail.ru


Страница сгенерированна за 0.01725 секунд с 7
ELECTRONIX ©2004-2016