Страница 1 из 1
AVR: float
Добавлено: Чт июл 24, 2014 12:39:43
k000858
Всем привет.
Есть переменная (float), которая вычисляется из строковой переменной "2.19".
вычисляется посимвольно: (первый символ - '0') + (float)(третий символ - '0')/10 + (четвертый символ - '0')/100
В результате получается 2.1899998
Компилятор AVR GNU Toolchain (Atmel Studio 6.2)
Подскажите, как корректнее вычислить эту самую переменную, что бы в результате получить именно 2.19
Re: AVR: float
Добавлено: Чт июл 24, 2014 12:44:04
dmmedia
Никак, это принцип работы чисел с плавающей запятой.
Используйте округление.
добавил:
как воркэраунд, если нужно работать всегда с 2-мя цифрами после запятой, то работать с целыми числами, а при выводе делить на 100 и форматировать вывод.
Re: AVR: float
Добавлено: Чт июл 24, 2014 12:58:30
k000858
dmmedia писал(а):Никак, это принцип работы чисел с плавающей запятой.
Используйте округление.
добавил:
как воркэраунд, если нужно работать всегда с 2-мя цифрами после запятой, то работать с целыми числами, а при выводе делить на 100 и форматировать вывод.
спасибо за инфу и хорошую идею. пожалуй, так и поступлю
Re: AVR: float
Добавлено: Чт июл 24, 2014 14:32:29
YS
В контроллерах, не имеющих FPU, вещественные типы без крайней нужды лучше вообще не использовать. Это сильно раздувает код и замедляет его выполнение.
Лучше всего использовать целочисленные переменные - например, если нужны сотые, просто умножить используемые величины на сто и вести расчеты с учетом такой договоренности. Если без вещественных чисел ну вообще никак (например, "честное" преобразование HSV->RGB), стоит посмотреть в сторону фиксированной точки. И только в самом крайнем случае использовать float.
Следует отметить, что безусловная необходимость в вещественных типах может возникнуть в основном только при решении СЛАУ и подобных задачах с суровым математическим уклоном. В подавляющем большинстве остальных случаев хватает самой простой методики с умножением - например, если это вольтметр, представлять напряжение не в Вольтах, а в милливольтах, и т.п.
Кроме того, вещественные числа имеют много особенностей вроде невозможности точного представления дробей со знаменателем, не приводимым к степени двойки (на что вы и натолкнулись), потери точности при вычислениях и прочего.
Re: AVR: float
Добавлено: Пт июл 25, 2014 05:53:11
k000858
YS писал(а):
Если без вещественных чисел ну вообще никак, стоит посмотреть в сторону фиксированной точки.
это как?
что за тип данных?
Re: AVR: float
Добавлено: Пт июл 25, 2014 09:12:58
YS
Фиксированная точка - это когда мы договариваемся, что внутри в общем-то целочисленной переменной n бит будет дробной частью. В общем случае, умножение на степень десяти - тоже фиксированная точка, но обычно под этим понятием имеют в виду именно дроби с основанием, равным степени двойки - это позволяет использовать быстрые битовые сдвиги.
Основная идея - мы представляем число как целое + числитель / знаменатель, равный степени двойки, причем храним знаменатель в той же переменной. Например, 1.1 - это примерно 1 + 26/256.
Итак, например, мы хотим иметь точность два десятичных знака после запятой. Выберем знаменатель для нашего представления равным 256. 1/256 ~ 0.004, так что будет небольшой запас. Таким образом, под дробную часть понадобится 8 бит. Для собственно переменной, с учетом сдвигов, которые нам понадобятся, имеет смысл выбрать четырехбайтовый целый тип - uint32_t (да, лучше использовать типы из
stdint.h).
Последний байт числа будем считать "дробным", первый для простоты оставим для обеспечения безопасных сдвигов. Таким образом, под собственно число останется 16 бит.
1.1, с учетом вышесказанного, запишется как 0x0000011A. Складываются/вычитаются такие числа как обычно, умножение и деление производятся чуточку хитрее, про это написано по ссылке выше.
Можете посмотреть, как я писал
преобразование HSV в RGB на фиксированной точке.
Кстати, почитайте еще про
особенности плавающей точки.