Перейти к содержанию

Формат Q и фиксированная точка

Формат Q — способ хранить дробные числа в обычных целых переменных: целое число считается делённым на степень двойки. Так считают на микроконтроллерах без блока плавающей точки — быстро и с предсказуемой точностью.

Обновлено Редакция GAW

Формат Q (Qm.n) — способ записи дробных чисел с фиксированной точкой (fixed-point): знаковое целое из 1 + m + n бит интерпретируется как целое, делённое на 2n. Здесь n — число бит дробной части, m — целой. Сложение и вычитание выполняются обычными целочисленными командами, а положение запятой программист держит в уме.

Распространённые форматы

ФорматТипДиапазонШаг
Q7 (1.7)int8_t−1 … 0,99222−7 ≈ 0,0078
Q15 (1.15)int16_t−1 … 0,999972−15 ≈ 3,05 · 10−5
Q31 (1.31)int32_t−1 … 1 − 2−312−31 ≈ 4,66 · 10−10
Q16.16int32_t−32 768 … 32 767,999982−16 ≈ 1,53 · 10−5

Перевод: целое = round(значение · 2n), значение = целое / 2n. Например, 0,3 в Q15 — round(0,3 · 32 768) = 9830, а обратно 9830 / 32 768 = 0,29999: погрешность меньше половины шага. Перевод в обе стороны с hex и диапазоном формата считает калькулятор формата Q. Обозначения 1.7, 1.15 и 1.31 использует библиотека CMSIS-DSP для типов q7_t, q15_t и q31_t; стандарт Embedded C (TR 18037) вводит для них типы _Fract и _Accum, которые поддерживает avr-gcc.

Арифметика

Сложение двух чисел одного формата — обычное сложение целых, но результат может выйти за диапазон, и тогда нужно насыщение. Умножение выполняют в удвоенной разрядности и сдвигают результат вправо на n бит:

c=a⋅b2n,в C для Q15: (a⋅b)≫15c = \frac{a \cdot b}{2^{n}}, \qquad \text{в C для Q15: } (a \cdot b) \gg 15

Сдвиги, маски и продвижение типов, на которых строится такая арифметика, разобраны в статье о битовых операциях, а умножение с округлением, деление, насыщение и примеры — в статье «Фиксированная точка». Типы целых и их размеры на AVR и ARM — в статье «C для микроконтроллеров». Код АЦП — по сути то же число с фиксированной точкой: доля опорного напряжения с шагом в один младший разряд, 2−N.

Частые вопросы

Что такое формат Q15?

16-битное знаковое целое, в котором 15 младших бит — дробная часть. Значение равно целому, делённому на 2^15 = 32 768, поэтому диапазон — от −1 до 1 − 2^−15 ≈ 0,99997, а шаг — около 3,05 · 10^−5. В CMSIS-DSP это тип q15_t, в GCC — _Fract.

Как перемножить два числа Q15?

Перемножить как 32-битные целые и сдвинуть произведение вправо на 15 бит: (int16_t)(((int32_t)a * b) >> 15). Для округления к ближайшему перед сдвигом прибавляют 1 << 14. Произведение −1 на −1 даёт +1, которое в Q15 не помещается, — этот случай обрабатывают насыщением.

Когда фиксированная точка лучше float?

На ядрах без блока плавающей точки — AVR, Cortex-M0, M0+, M3, — где float считается программно и медленно, и там, где нужна одинаковая абсолютная точность во всём диапазоне: в цифровых фильтрах, регуляторах, обработке сигналов АЦП. Плата за это — нужно самому следить за диапазоном и переполнением.

Источники

  1. ISO/IEC TR 18037 (Embedded C), WG14 N1169, 2006 — 4.1 — типы _Fract и _Accum, диапазон fract от −1 до 1, форматы s.7, s.15; 4.1.3 — переполнение и насыщение
  2. Arm. CMSIS-DSP: arm_math_types.h (github.com/ARM-software/CMSIS-DSP) — q7_t — int8_t в формате 1.7, q15_t — int16_t в формате 1.15, q31_t — int32_t в формате 1.31
  3. GCC Wiki: avr-gcc — Fixed-Point Support; GCC Manual: Fixed-Point Types — avr-gcc с версии 4.8: short _Fract s.7, _Fract s.15, long _Fract s.31, _Accum s16.15

Нашли ошибку?