Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Как рациональнее всего преобразовать float в int?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
zheka
Господа, есть небольшая задачка.
Работаю с дисплеем и тачскрином. Как вы сами понимаете, координаты дисплея и координаты тачскрина - это две большие разницы (или, как говорят в Одессе, четыре маленьких)) ).

координаты дислпея вычисляются следующим образом:
х=touch_x*800/256
y=touch_y*480/256

Но полученный результат является float, а функция, куда будут переданы координаты берет int.
Естественно, такой вариант как скажем Circle (touch_x*800/256, touch_y*480/256 .....) не прокатит.

Мне хочется с наименьшими процессорными затратами получить округленные числа типа int, как это сделать? Ибо способов существует несколько, а вот какой быстрее....

Спасибо.
_Pasha
QUOTE (zheka @ Dec 21 2010, 19:33) *
Но полученный результат является float, а функция, куда будут переданы координаты берет int.

Кто сказал? Если int x,y; то результат будет int sm.gif
fantex
Если touch_x и touch_y типа int, то и результат будет типа int. Все операции умножения и деления будут выполнятся с целыми числами. И в принципе на округление можно забить, так как палец на тачскрине всё округлит сам.
rezident
Зачем вам float вообще? Вычисляйте в целых числах. Ведь сами координаты-то точек являются целыми числами.
х=(touch_x*800)>>8;
y=(touch_y*480)>>8;
Если уж так непременно нужны дробные результаты, то и представляйте их в виде дроби с основанием двойки.
zheka
rezident, сейчас переварю, то что Вы написали, а пока для сравнения, вот что у меня было:

Код
_X=(float)*touch_x*800/256;
_Y=(float)*touch_y*480/256;
FillEllipse(800-(int)(_X),(int)(_Y),10,10,clRed);


Это затратнее чем Ваш вариант?

Проверил.
Мой вышеприведенный вариант позволял рисовать на экране стилусом.
Ваш вариант в моем использовании:
FillEllipse(800-((*touch_x*800)>>8),((*touch_y*480)>>8),10,10,clRed);

рисует в правом верхнем углу экрана, в квадрате со стороной 256х256 пикселей. У тачпанель 0,0 находятся в правом верхнем углу экрана.
touch_x у меня типа unsigned char
sergeeff
Вы, надеюсь, понимаете, что координаты вашего устройства отображения это сугубо целые числа? Так на кой вам float? Некуда процессорное время девать?

Да и написали бы лучше:

Код
х=((*touch_x)*800)>>8;
y=((*touch_y)*480)>>8;
FillEllipse(800-x, y, 10, 10, clRed);
zheka
Цитата
Вы, надеюсь, понимаете, что координаты вашего устройства отображения это сугубо целые числа?


Подозревать человека, который в состоянии собрать отладочную плату и подключить с ней дисплей глупо.


В свою очередь, я надеюсь что вы понимаете что произойдет если unsigned char умножить на 800.

Итог - мой вариант работает, но безусловно тратит процессорное время. Ваш вариант работает как и предложенный резидентом.
rezident
zheka, а вы дроби из курса начальной школы не забыли еще? Например, запись 0,125 означает 125/1000. И числитель и знаменатель дроби являются целыми числами.

Цитата(zheka @ Dec 21 2010, 21:29) *
В свою очередь, я надеюсь что вы понимаете что произойдет если unsigned char умножить на 800.
В соответствии со стандартом Си произойдет конвертация в тип signed integer, в котором эти числа и будут перемножены. Однако если переменные x и y у вас тоже типа char, то результат умножения будет преобразован к типу char. Вывод: вычислять нужно, приведя операнды к типу long.
zheka
Да нет же, я не о целых и дробях, я о том, что touch_x не может быть равен больше 255, если прибавить к 245 число 20, получится не 265, а 10.

У пишущих здесь есть возможность проверить это в компиляторе?

В моем варианте я оперирую с float, а затем по окончании привожу float к int, не только потому что я, как ни странно знаю, что координаты это целое число, а еще и потому, что функция не возьмет float.

Цитата
В соответствии со стандартом Си произойдет конвертация в тип signed integer, в котором эти числа и будут перемножены.


ОДнако на практике - в Вашем варианте, пока я веду стилусом вдоль х, курсор на экране 4 раза пробегает от координаты 544 к координате 800. Значит все таки не происходит конвертации.

Цитата
Вывод: вычислять нужно, приведя операнды к типу long.


То есть то же самое, что у меня, только приводя к long, так? СРазу вопрос работа с long менее затратна, чем с float ?
Поверьте, мне нужно не просто чтобы работало, а чтобы работало быстро настолько, насколько это возможно силами C.
rezident
Цитата(zheka @ Dec 21 2010, 21:48) *
ОДнако на практике - в Вашем варианте, пока я веду стилусом вдоль х, курсор на экране 4 раза пробегает от координаты 544 к координате 800. Значит все таки не происходит конвертации.
Нет смысла обсуждать видимые результаты, если неизвестны причины, порождающие их. Я имею в виду типы переменных и функции, которые вы применяете. Нам о них ничего не известно.
sergeeff
Цитата(zheka @ Dec 21 2010, 20:29) *
Подозревать человека, который в состоянии собрать отладочную плату и подключить с ней дисплей глупо.


В свою очередь, я надеюсь что вы понимаете что произойдет если unsigned char умножить на 800.

Итог - мой вариант работает, но безусловно тратит процессорное время. Ваш вариант работает как и предложенный резидентом.


Ну вы же нам исходя, видимо, из соображений секретности, даже не сказали, что за процессор/компилятор пользуете. Как вы там объявили свои х, тouch_x - тайна, покрытая мраком. У вас int, часом, не 16-бит?

Так почему надо упорно лезть во float, а не применить, действительно, что-нибудь типа long (хотя это тоже загадка, что этот long на вашем процессоре из себя представляет)?

Ну да, вы же в разделе AVR. int он и есть 16-бит. Сделайте так:


Код
х=(((long int)touch_x)*800L)>>8;
y=(((long int)touch_y)*480L)>>8;
FillEllipse(800-x, y, 10, 10, clRed);
zheka
У меня CodeVision 2, процессор - atmega32
Сейчас попробую то что вы написали.

Объявлял так
Код
unsigned char x;
unsigned char y;
unsigned char *touch_x;
unsigned char *touch_y;

int sX;
int sY;


.....

void main ()

.....

touch_x = &x;
touch_y = &y;


Получаю координаты функцией GetTouchStat(touch_x,touch_y);

То что вы предложили - не работает.
777777
Цитата(zheka @ Dec 21 2010, 21:53) *
То что вы предложили - не работает.

Код
unsigned char x;
unsigned char y;

int sX;
int sY;

.....

void main ()

.....

GetTouchStat(&x,&y);
sX = x;
sY = Y;

А так работает? С sX, sY выполняешь вычисления. По моим прикидкам 16-разрядного ште должно хватить sm.gif
sergeeff
Цитата(zheka @ Dec 21 2010, 22:53) *
У меня CodeVision 2, процессор - atmega32
Сейчас попробую то что вы написали.

Объявлял так
Код
unsigned char x;
unsigned char y;
unsigned char *touch_x;
unsigned char *touch_y;

int sX;
int sY;


.....

void main ()

.....

touch_x = &x;
touch_y = &y;


Получаю координаты функцией GetTouchStat(touch_x,touch_y);

То что вы предложили - не работает.


Делаем так:

Код
unsigned char x;
unsigned char y;


int sX;
int sY;


.....

void main ()

.....


GetTouchStat(&x, &y);

sX=(int)((((long int)x)*800L)>>8);
sY=(int)((((long int)y)*480L)>>8);
FillEllipse(800-sX, sY, 10, 10, clRed);


Кстати. Touchscreen выдает координаты в диапазоне 0...255 или более того?
zheka
Код
sX=(int)((((long int)x)*800L)>>8);
sY=(int)((((long int)y)*480L)>>8);
FillEllipse(800-sX, sY, 10, 10, clRed);


Вот это другое дело!
Работает, так как надо. И, судя по тому, что рисуемые на большой скорости передвижения стилуса точки получаются гуще - работает быстрее.
Спасибо!

Цитата
Touchscreen выдает координаты в диапазоне 0...255 или более того?


0-255. Иначе зачем я делил на 256?
sergeeff
Надеюсь вы поняли, что проблема была в ваших

Код
unsigned char x;
unsigned char y;


Я уже писал на форуме и вам советую - забудьте про float и double. Не нужны они в 99,99% случаев.
zheka
Не совсем в этом причина.
800/256 - я так написал, чтобы было понятно тем, кому я задаю вопрос.
Я же рассуждая, что каждый раз высчитывть константу затратно в своем коде изначально просто умножал на 3.125, отсюда и возникла работа с float.

Кстати, в полученном коде все-таки каждый раз косвенно высчитывается 800/256 и 400/256. Нельзя ли еще сократить?
Сергей Борщ
QUOTE (zheka @ Dec 22 2010, 08:52) *
Нельзя ли еще сократить?
Сократить что? Одно умножение и пересылку (сдвиг на 8 это пересылка байта 2 раза, ибо результат у вас двухбайтный)? Маловероятно. Разве что таблицей. У вас есть лишний килобайт флеша?
ReAl
Цитата(zheka @ Dec 21 2010, 21:28) *
Код
sX=(int)((((long int)x)*800L)>>8);
sY=(int)((((long int)y)*480L)>>8);
FillEllipse(800-sX, sY, 10, 10, clRed);

И, судя по тому, что рисуемые на большой скорости передвижения стилуса точки получаются гуще - работает быстрее.
Я так думаю, что сейчас львиную долю времени занимает FillEllipse. Попробуйте выводить одну точку или хотя бы крестик из двух отрезков.

Кстати, a*3.125 это a*25/8 aka (a*25)>>3 (впрочем, я бы может округление добавил, (a*25+4)/8), что вполне считается для Вашего диапазона даже не в long т.е. очень быстро.
ukpyr
Цитата
Кстати, в полученном коде все-таки каждый раз косвенно высчитывается 800/256 и 400/256. Нельзя ли еще сократить?
можно. арифметику, дроби в каком классе проходят ? тренируйте мозги:

800/256 = (800 / 32) / (256 / 32) = 25 / 8 = 25 >> 3;
400/256 = (400 / 16) / (256 / 16) = 25 / 16 = 25 >> 4;

вместо long теперь достаточно unsigned int.
zheka
ukpyr
спасибо.
~phase
вообще то на сколько я замечал то сдвиг для умножения/деления работает быстрее чем это множить или делить, но есть недостаток, множить или делить только кратное 2.
mempfis_
Цитата(~phase @ Dec 27 2010, 14:37) *
вообще то на сколько я замечал то сдвиг для умножения/деления работает быстрее чем это множить или делить, но есть недостаток, множить или делить только кратное 2.


Если делимое - переменная величина то сложно чтото придумать кроме как x/y. Но если делимое константа или Вы хотите умножить число на другое дробное число-константу, то всегда можно подобрать такое число что y=a/b где a будет степенью двойки, тогда останется только перемножить на b и результат сдвинуть на log2(cool.gif вправо (эквивалент деления результат на a). Подобные манипуляции существенно ускоряют математику за счёт избавления от деления на число не кратное 2.
defunct
Цитата(ukpyr @ Dec 22 2010, 09:18) *
вместо long теперь достаточно unsigned int.

Зато сдвиг стал много тяжеловеснее.
Ускорять всяко лучше на асме, где можно сделать умножение U8xU16=U16, или U16xU16 = MSU16(U24) - (опустить расчет младшего байта).
Только думаю смысла нет все это делать, ибо CodeVision и афтар в C плавает - тормозить будет не это так что-то другое.
Serj78
Я думаю Жеке не надо напоминать, что наш любимый CV преобразование типов не делает sm.gif
B появление в правой части выражения char первым, хоть в левой и long или int стоит , будет вызывать переполнение и неадекватный результат. sad.gif
обычно это делается так, хотя и выглядит криво
не a=b*c;
а так: а=b;
b*=с;

Интересно, что работает быстрее, чем "принудительное" приведение к int
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.