Какой тип данных Arduino допускает десятичные дроби?

Наверное, это звучит глупо, но я не получил на этот счет четкого ответа. Какой тип данных допускает разумное количество десятичных знаков?

, 👍-2

Обсуждение

Что значит «разумный»?, @Ignacio Vazquez-Abrams

По крайней мере 2 или 3, только наименьшее количество выше этого, чтобы я мог сэкономить вычислительную мощность на своей плате для безделушек., @pdustin101

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


3 ответа


0

По крайней мере 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


2

Если вам нужно представить числа, которые не являются целыми, самый простой Решение состоит в использовании переменных с плавающей точкой, также известных как «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

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

Я бы посоветовал вам сначала попробовать использовать поплавки, так как это самый простой способ. решение. Если это приводит к тому, что код становится слишком большим или слишком медленным, затем попробуйте переписать с использованием арифметики с фиксированной точкой (т.е. только целые числа).

,

0

Другой вариант, о котором Эдгар не упоминает (в основном потому, что это более эзотерическая и сложная в использовании система), — это значения фиксированной точки. Эти значения, также известные как значения 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