Поскольку double и float представляют один и тот же тип данных (обычно), что предпочтительнее?

Похоже, что на платах на базе ATMega float и double эквивалентны 32-битным (4-байтным) типам данных.

Однако на Arduino Due double являются 64-битными (8-байтовыми), а float — 32-битными (4-байтовыми), как и в ATMega.

Я также видел один источник на avr-gcc, в котором double указан как нестандартный.

Итак, при написании кода для плат на базе ATMega float предпочтительнее использовать double, поскольку это сделало бы код эквивалентным для плат Due? Или по какой-то причине двойные предпочтительнее?

, 👍2

Обсуждение

Я бы использовал float, чтобы любой, кто читает мой код, кто может не знать о нестандартном двойнике AVR, знал, какую точность ожидать. Но это скорее личное предпочтение, чем жесткое правило (поэтому я не считаю его «настоящим» ответом)., @Edgar Bonet

Я думаю, что это довольно серьезное рассуждение; можно не ожидать, что «двойник» действительно будет поплавком, если он приходит с другой платформы., @Ehryk


2 ответа


2

См. avr-gcc

Отклонения от стандарта

двойной

double имеет ширину всего 32 бита и реализован так же, как float


при написании кода для плат на базе ATMega...

Поэтому они одинаковы.


(отредактировано для добавления)

При написании кода предпочтительнее ли использовать одно, а не другое?

Я не очень понимаю ваш вопрос. Вы спрашивали о написании кода для плат на базе ATMega. Вы действительно имеете в виду, если вы пишете код для Due? Быстрый ответ: на Due двойной занимает вдвое больше оперативной памяти, однако он более чем в два раза точнее — около 7,2 десятичных разряда для числа с плавающей запятой по сравнению с 15,9 для двойного числа. (Я не уверен насчет скорости, вероятно, удвоение было бы медленнее, если только оно не реализовано в оборудовании с плавающей запятой, однако я не вижу никаких ссылок на это в техническом описании AT91SAM3X8E).

См.: https://en.wikipedia.org/wiki/Floating_point

Если вам нужна большая точность (однако более медленная и занимающая больше оперативной памяти), вы можете использовать double, что не будет иметь никакого значения для плат на базе ATmega, но будет иметь значение для Due.


Как сказал Эдгар Боне:

Я бы использовал число с плавающей запятой, чтобы любой, кто читает мой код и может не знать о нестандартном двойнике AVR, знал, какой точности ожидать.

В этом есть смысл.


Предупреждаю вас, что "предпочтительнее" субъективно. Это близко к вопросу "что лучше"? На что ответ: "лучше всего для чего?"

,

На платах ATMega да, они одинаковые. Если вы затем перенесете код на Arduino Due, они больше не будут одинаковыми при использовании double, но будут одинаковыми при использовании float. При написании кода предпочтительнее ли использовать один над другим? Почему или почему нет?, @Ehryk

См. измененный ответ., @Nick Gammon

Ну, строго говоря, сейчас я ориентируюсь только на архитектуры ATMega, я _думал_, что пишу код, который будет одинаковым для всех Arduino. Я не спрашивал, какой из них _лучший_, хотя я думаю, что отвечаю на свой вопрос тем, что float более известен, лучше описывает тип данных для разных архитектур и совместим с большим количеством Arduino., @Ehryk

Было бы больше смысла, если бы я добавил: «При написании кода Arduino _для неизвестной платы_ предпочтительнее ли использовать один над другим?», @Ehryk

Я полагаю. \*пожал плечами\* - при работе с платами, которые могут иметь ограниченные объемы ОЗУ и флэш-памяти, не всегда возможно кодировать в общем виде., @Nick Gammon

В начале программирования для Arduino я думал, что они, по крайней мере, пытаются сделать код как можно более аппаратно-независимым. В этом духе я думаю, что с этого момента я буду использовать поплавки., @Ehryk

Этот ответ больше не актуален с avr-gcc, который поддерживает 64-битное двойное (и длинное двойное) начиная с v10. См., например, вики-страницу avr-gcc, на которую вы ссылаетесь., @emacs drives me nuts

@emacsdrivesmenuts Ну да, но с использованием параметра конфигурации, а не по умолчанию. `В avr-gcc v10 и более поздних версиях формат double и long double определяется параметрами конфигурации --with-double= и --with-long-double= соответственно. занимает больше времени, поэтому вашему приложению нужно будет выбирать точность, а не скорость., @Nick Gammon

И возьмите больше памяти, которую вы, возможно, не захотите брать на процессор с ограниченным объемом оперативной памяти., @Nick Gammon

@Nick Gammon: Конечно, речь идет о точности, которая не была целью моего комментария. Кроме того, avr-gcc v10+ поддерживает -mdouble=64. Единственная причина, по которой по умолчанию установлено значение 32 бита, заключалась в согласовании со старыми (не соответствующими стандарту) версиями avr-gcc., @emacs drives me nuts


1

Ни один из них не является предпочтительным.

Если в вашем микроконтроллере нет аппаратного модуля с плавающей запятой, использование float или double приводит к огромным накладным расходам для вашей программы.

Поэтому по возможности используйте целочисленную математику.

Вместо того, чтобы работать в вольтах, используйте, например, милливольты.

Однако, если ваш MCU имеет FPU, а некоторые современные более мощные устройства имеют его, вам следует использовать размер float или double, который является родным для FPU. Если это 32-битный FPU, используйте float. Если это 64-битный FPU, вы можете использовать double.

,

есть ли у вас фактические ориентиры "огромных накладных расходов"? менее эффективно, конечно, но у меня никогда не было проблем с использованием плавающей запятой на Arduino., @BrettAM

в; недействительная установка () { а = 1; } void loop() { a = a * 2; } использует 482 байта. Измените int на float, он почти не удваивается до 878 байт. Еще хуже, если вы измените «2» на «2.0», в то время как это «int» — вы получите 1086 байт. Массивное увеличение. И это не говоря уже о том, сколько времени нужно для запуска. Я сделаю грубый тест., @Majenko

Умножение 1 на 2, пока оно не превысит 32768 (1, 2, 4, 8, 16 и т. д.) с помощью int, занимает 12 микросекунд. При использовании float это занимает 148 микросекунд. Это более чем в десять раз дольше для одной простой операции., @Majenko

Интересно, что умножение целого числа на «2.0» занимает еще больше времени — 208 микросекунд. в 17 раз дольше., @Majenko

Фиксированная точка требует больше операций, чем просто целочисленное умножение, и мы должны сравнивать 32-битные значения с обеих сторон. в тесте float (a*b) и long (a*b/1000), как было бы в случае, если бы вы выполняли операции в мВ, с плавающей запятой быстрее. Это не вычисление дополнительного старшего байта, необходимого для реальной фиксированной точки, тогда снова фиксированная точка выиграет от более простого делителя. Сложение, безусловно, останется быстрее в фиксированной точке. Я не думаю, что плавающие и фиксированные значения так же очевидны, как это делает сообщество Arduino SE., @BrettAM

@BrettAM: при работе с фиксированной точкой вы никогда не будете масштабироваться на 1000, всегда на степень двойки. И нет, вы не должны сравнивать 32 бита: вы должны сравнивать 32-битные числа с плавающей запятой с фиксированной запятой наименьшего размера, достаточно точного для задания, которое часто будет 16 бит, а иногда даже 8. Я сделал 16-битная реализация функций cos() и sin() с фиксированной точкой, которая в 15,7 раз быстрее, чем стандартные функции <math.h>., @Edgar Bonet

@EdgarBonet Я упомянул, что делитель плохой, он взят из ответа, который это предложил. Умножение 6,10 с фиксированной точкой дает результат 12,20 (32 бита); поэтому, если их числа не соответствуют 8, умножение должно выполняться в 32 бита. Накладные расходы на написание пользовательской фиксированной точки cos() (или даже на ее загрузку) намного больше, чем наличие нескольких медленных триггерных функций для большинства приложений здесь, на arduino SE., @BrettAM

@BrettAM: ответ не предлагал деления, он предлагал использовать милливольты, что не одно и то же. Обычно вы должны либо analogRead(pin) * 39U >> 3, либо analogRead(pin) * 1250UL >> 8, в зависимости от требуемой точности. И вам не нужно умножать на 32 бита: вы можете сделать умножение 16×16→16 в ассемблере или использовать типы данных _Fract или _Accum. Но тогда я согласен с тем, что во многих проектах достаточно циклов процессора, чтобы сделать фиксированную точку неактуальной., @Edgar Bonet