Какой тип данных Arduino допускает десятичные дроби?
Наверное, это звучит глупо, но я не получил на этот счет четкого ответа. Какой тип данных допускает разумное количество десятичных знаков?
@pdustin101, 👍-2
Обсуждение3 ответа
По крайней мере 2 или 3 [цифры], просто введите наименьшее число сверх этого, чтобы я мог экономия вычислительной мощности на моей плате Trinket
Процессоры (компьютеры) обычно не хранят значения в двоично-десятичном коде (BCD) (как в системе счисления с основанием 10), поскольку такое хранение занимает ценное пространство. Вместо этого процессоры обычно используют двоичные числа (как в системе счисления с основанием 2).
Большинство процессоров работают с двоичными значениями, кратными 8 битам. Поэтому значения обычно представлены 8 битами, 16 битами, 32 битами и т. д. 8-битное двоичное число может иметь 2^8 возможных значений или 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 или 256. То есть 8-битное двоичное число может иметь значения от 0 до 255 включительно.
Если ваше требование «разумного количества десятичных знаков» попадает в этот диапазон, то тип, который вы ищете, — «uint8_t» или «char». Оба обычно имеют 8 бит в языке C / C++. Если вам нужно больше, то следующее двоичное число имеет 16 бит или 2^16 или значения от 0 до 65535. Тогда тип, который вы ищете, — «uint16_t» или «unsigned int».
Хотя факты, которые вы излагаете, в основном верны, это не решает *заданный вопрос* никоим образом на практике. В результате ваши окончательные рекомендации неверны. Вопросы касаются нецелых чисел, поэтому целочисленный тип не может *сам по себе* быть решением, хотя, как уже упоминалось, он может быть таковым при использовании *масштабного коэффициента*., @Chris Stratton
Если вам нужно представить числа, которые не являются целыми, самый простой
Решение состоит в использовании переменных с плавающей точкой, также известных как «floats».
float объявляется с ключевым словом float
. Числовая константа — это
автоматически число с плавающей точкой, если оно имеет десятичную точку:
42
— целое число (типint
)42.0
— это число с плавающей точкой
Если объединить int и float в арифметической операции, то int будет неявно повышен до float, и результатом является float.
Число с плавающей точкой дает вам точность в 24 значимых бита. «Значимые» означает, что мы подсчитываем как биты до, так и после двоичного числа точка. Тогда, небольшое число будет иметь много дробных бит, тогда как большое число будет иметь только несколько, и любое число больше, чем 223 = 8388608 не будет иметь никаких дробных битов вообще.
Эта точность почти всегда лучше, чем 7 значащих знаков после запятой и хуже 8 значащих знаков после запятой. Например, следующие два числа являются последовательными: любое вычисление, которое дает результат в между ними будет округлено до любого из них (обычно ближайшего):
binary (24 bits) decimal
101010.000000000000000000 42.0
101010.000000000000000001 42.000003814697265625
Проблема с поплавками в том, что они дорогие. В отличие от настольных компьютер, Trinket не имеет аппаратной поддержки для поплавков (это ATtiny: у него даже нет аппаратного множителя!). Тогда каждый Вычисления производятся программным обеспечением. Даже самые простые вещи, такие как добавление два числа, могут занять довольно много циклов ЦП и много флэш-памяти. Вот почему на очень маленьких устройствах люди иногда предпочитают фиксированную точку арифметика. В двух словах, фиксированная точка — это выбор подходящего единиц измерения для достижения требуемой точности с использованием только целые числа. Например, АЦП-преобразователь в Trinket дает вам показания напряжения в единицах
5 V ÷ 1024 = 4.8828125 mV
Эта единица измерения может показаться непрактичной, но она обеспечивает разумную точность. используя только целые числа. Если вы можете избежать преобразования в вольты, вы будете возможность писать код, содержащий только целые числа, который меньше и быстрее.
Я бы посоветовал вам сначала попробовать использовать поплавки, так как это самый простой способ. решение. Если это приводит к тому, что код становится слишком большим или слишком медленным, затем попробуйте переписать с использованием арифметики с фиксированной точкой (т.е. только целые числа).
Другой вариант, о котором Эдгар не упоминает (в основном потому, что это более эзотерическая и сложная в использовании система), — это значения фиксированной точки. Эти значения, также известные как значения Q, часто используются в цифровой обработке сигналов (DSP), поскольку они дают предсказуемое разрешение и высокую скорость обработки.
Значение AQ — это просто целое число, биты которого разделены на две части — целую часть и числитель.
Например, вы можете выбрать реализацию формата Q4.11. Это дает 4 бита целочисленного значения (может хранить 0-15) и 11 бит числителя (то есть десятичное разрешение 1/(2^11)
или 0,000488281). 16-й бит (4+11 = 15, в 16-битном целом числе нужно учитывать еще один бит) — это неявный знаковый бит. В некоторых реализациях он есть, в некоторых — нет — решать только вам. Если вам нужны только положительные значения, вы можете включить 16-й бит в свое значение, чтобы увеличить разрешение или размер целого числа и т. д.
Разумеется, вы не ограничены 16 битами — вы можете использовать 8 бит для меньших чисел и меньшей точности или 32 бита для больших чисел и большей точности.
Недостатком использования значений Q по сравнению с использованием чисел с плавающей точкой является то, что числа с плавающей точкой могут произвольно менять десятичную точность на целочисленный размер. С фиксированной точкой вы ограничиваете себя диапазоном целых чисел, которые можете представить. Но эта жертва может стоить того, если у вас изначально ограниченный диапазон (скажем, 0-5 В) и вам нужна дополнительная скорость.
Для работы с фиксированной точкой сначала необходимо преобразовать число с плавающей точкой в целевое значение Q и обратно. Это довольно просто — просто умножьте (или разделите для преобразования из Q в число с плавающей точкой) значение на значение Q, равное 1. То есть, для Q4.11 вы бы использовали значение 12-го бита (первый из целочисленных битов) или 1<<11 (начните отсчет с 0, поэтому 12-й бит равен 11).
После того, как у вас есть значения Q, сложение и вычитание выполняются так же, как обычно, с использованием операторов +
и -
. Умножение и деление, хотя и немного сложнее, но результирующий код значительно эффективнее. Все это можно сделать, используя только сдвиги влево и вправо. Например, некоторые функции, которые я использую в своей небольшой библиотеке для значений Q15 (точнее, Q0.15), которые хранят значения между ±1 с разрешением 15 бит, выглядят так:
typedef int16_t Q15;
static inline int16_t __satQ15(int32_t x)
{
if (x > 0x7FFFL) return 0x7FFF;
else if (x < -0x8000L) return -0x8000;
else return (int16_t)x;
}
static inline Q15 Q15_mul_Q15(Q15 a, Q15 b) {
int16_t result;
int32_t temp;
temp = (int32_t)a * (int32_t)b; // тип результата - тип операнда
// Округление; средние значения округляются в большую сторону
temp += (1 << 14);;
// Исправляем, разделив на основание и насыщая результат
result = __satQ15(temp >> 15);
return result;
}
static inline Q15 Q15_div_Q15(Q15 a, Q15 b)
{
int16_t result;
int32_t temp;
// предварительно умножаем на основание (масштабируем до Q16, чтобы результат был в формате Q8)
temp = (int32_t)a << 15;
// Округление: средние значения округляются в большую сторону (в меньшую сторону для отрицательных значений).
if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0))
temp += b / 2;
else
temp -= b / 2;
result = (int16_t)(temp / b);
return result;
}
Более подробную информацию о числах Q можно узнать здесь:
- https://en.wikipedia.org/wiki/Q_(number_format)
Обратите внимание, что Q0.15 изначально поддерживается gcc как _Fract
. Хотя я еще не экспериментировал с этим типом., @Edgar Bonet
@EdgarBonet Только если версия GCC, которую вы используете, имеет эту опцию, скомпилированную в - не все имеют. Я не знаю навскидку, есть ли она в компиляции Arduino avr-gcc или нет., @Majenko
- Как получить тип данных переменной?
- Преобразование в Unix Timestamp и обратно
- ардуино - миллисекунды ()
- Невозможно создать массив типа const char*
- Ошибка Cast from 'char*' to 'uint8_t {aka unsigned char}' loses precision [-fpermissive]
- Получение шестнадцатеричных данных с терминала
- Поскольку double и float представляют один и тот же тип данных (обычно), что предпочтительнее?
- Inttypes против определенных интегральных типов Arduino
Что значит «разумный»?, @Ignacio Vazquez-Abrams
По крайней мере 2 или 3, только наименьшее количество выше этого, чтобы я мог сэкономить вычислительную мощность на своей плате для безделушек., @pdustin101
Вы можете использовать число с плавающей запятой, но вы также можете рассмотреть возможность умножения ваших значений на коэффициент масштабирования, чтобы использовать целочисленную арифметику, которая будет намного более эффективной, если вам не нужен более широкий диапазон., @Chris Stratton