AVR: float

Обсуждаем контроллеры компании Atmel.
Ответить
k000858
Открыл глаза
Сообщения: 44
Зарегистрирован: Сб июн 14, 2008 11:51:50

AVR: float

Сообщение k000858 »

Всем привет.
Есть переменная (float), которая вычисляется из строковой переменной "2.19".
вычисляется посимвольно: (первый символ - '0') + (float)(третий символ - '0')/10 + (четвертый символ - '0')/100

В результате получается 2.1899998

Компилятор AVR GNU Toolchain (Atmel Studio 6.2)

Подскажите, как корректнее вычислить эту самую переменную, что бы в результате получить именно 2.19
Реклама
dmmedia
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт сен 06, 2011 18:00:36
Откуда: Tallinn, Estonia
Контактная информация:

Re: AVR: float

Сообщение dmmedia »

Никак, это принцип работы чисел с плавающей запятой.
Используйте округление.

добавил:
как воркэраунд, если нужно работать всегда с 2-мя цифрами после запятой, то работать с целыми числами, а при выводе делить на 100 и форматировать вывод.
Реклама
k000858
Открыл глаза
Сообщения: 44
Зарегистрирован: Сб июн 14, 2008 11:51:50

Re: AVR: float

Сообщение k000858 »

dmmedia писал(а):Никак, это принцип работы чисел с плавающей запятой.
Используйте округление.

добавил:
как воркэраунд, если нужно работать всегда с 2-мя цифрами после запятой, то работать с целыми числами, а при выводе делить на 100 и форматировать вывод.
спасибо за инфу и хорошую идею. пожалуй, так и поступлю
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

Re: AVR: float

Сообщение YS »

В контроллерах, не имеющих FPU, вещественные типы без крайней нужды лучше вообще не использовать. Это сильно раздувает код и замедляет его выполнение.

Лучше всего использовать целочисленные переменные - например, если нужны сотые, просто умножить используемые величины на сто и вести расчеты с учетом такой договоренности. Если без вещественных чисел ну вообще никак (например, "честное" преобразование HSV->RGB), стоит посмотреть в сторону фиксированной точки. И только в самом крайнем случае использовать float.

Следует отметить, что безусловная необходимость в вещественных типах может возникнуть в основном только при решении СЛАУ и подобных задачах с суровым математическим уклоном. В подавляющем большинстве остальных случаев хватает самой простой методики с умножением - например, если это вольтметр, представлять напряжение не в Вольтах, а в милливольтах, и т.п.

Кроме того, вещественные числа имеют много особенностей вроде невозможности точного представления дробей со знаменателем, не приводимым к степени двойки (на что вы и натолкнулись), потери точности при вычислениях и прочего.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Реклама
Эиком - электронные компоненты и радиодетали
k000858
Открыл глаза
Сообщения: 44
Зарегистрирован: Сб июн 14, 2008 11:51:50

Re: AVR: float

Сообщение k000858 »

YS писал(а): Если без вещественных чисел ну вообще никак, стоит посмотреть в сторону фиксированной точки.
это как?
что за тип данных?
Реклама
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

Re: AVR: float

Сообщение YS »

Фиксированная точка - это когда мы договариваемся, что внутри в общем-то целочисленной переменной n бит будет дробной частью. В общем случае, умножение на степень десяти - тоже фиксированная точка, но обычно под этим понятием имеют в виду именно дроби с основанием, равным степени двойки - это позволяет использовать быстрые битовые сдвиги.

Основная идея - мы представляем число как целое + числитель / знаменатель, равный степени двойки, причем храним знаменатель в той же переменной. Например, 1.1 - это примерно 1 + 26/256.

Итак, например, мы хотим иметь точность два десятичных знака после запятой. Выберем знаменатель для нашего представления равным 256. 1/256 ~ 0.004, так что будет небольшой запас. Таким образом, под дробную часть понадобится 8 бит. Для собственно переменной, с учетом сдвигов, которые нам понадобятся, имеет смысл выбрать четырехбайтовый целый тип - uint32_t (да, лучше использовать типы из stdint.h).

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

1.1, с учетом вышесказанного, запишется как 0x0000011A. Складываются/вычитаются такие числа как обычно, умножение и деление производятся чуточку хитрее, про это написано по ссылке выше.

Можете посмотреть, как я писал преобразование HSV в RGB на фиксированной точке.

Кстати, почитайте еще про особенности плавающей точки.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Реклама
Ответить

Вернуться в «AVR»