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

Арифметика с насыщением

Сложите в 16-битной переменной 30 000 и 10 000 — получится не 40 000, а −25 536: число перешло через границу и сменило знак. В звуке это громкий щелчок, в регуляторе — рывок в обратную сторону. Арифметика с насыщением вместо этого «упирается» в предел: результат будет 32 767.

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

Арифметика с насыщением (saturating arithmetic) — способ вычислений, при котором результат, вышедший за границы диапазона, заменяется ближайшей границей, а не переносится по модулю. В обычной целочисленной арифметике дополнительного кода переполнение «заворачивает» число на противоположный край: максимум плюс единица становится минимумом, и знак меняется. При насыщении результат остаётся у предела. Архитектура Armv7-M определяет это так: для знакового N-битного результата всё, что больше 2^(N−1) − 1, становится 2^(N−1) − 1, а всё, что меньше −2^(N−1), становится −2^(N−1); для беззнакового диапазон — от 0 до 2^N − 1 (Armv7-M ARM, разд. A2.2).

Пример

Операция над 16-битными знаковымиПеренос по модулюС насыщением
30 000 + 10 000−25 53632 767
−30 000 − 10 00025 536−32 768
1 000 + 2 0003 0003 000

Наш расчёт по правилам дополнительного кода и определению насыщения. Пока результат в диапазоне, обе арифметики совпадают. Разница только при выходе за предел: насыщение даёт ошибку по величине, но сохраняет знак, а перенос по модулю переворачивает знак. Поэтому насыщение используют в обработке звука, цифровых фильтрах и регуляторах, где смена знака хуже, чем ограничение, — особенно вместе с числами в формате Q15.

Команды Cortex-M

КомандаЧто делаетГде есть
SSAT, USATнасыщают значение, при необходимости сдвинутое: SSAT — до знакового числа из 1–32 бит, USAT — до беззнакового из 0–31 битArmv7-M: Cortex-M3 и выше
SSAT16, USAT16то же для двух полусловDSP-расширение
QADD, QSUBсложение и вычитание 32-битных чисел с насыщениемDSP-расширение
QDADD, QDSUBудвоение с насыщением, затем сложение или вычитаниеDSP-расширение
QADD16, UQADD8 и др.параллельные сложения полуслов и байтов с насыщениемDSP-расширение

По табл. A4-9 — A4-14 Armv7-M ARM. Команда SSAT с параметром 16 превращает 32-битную сумму в правильно ограниченное 16-битное значение — так сохраняют результат накопления в int16 без проверки условий. Флаг Q в регистре APSR при насыщении ставят QADD, QSUB, QDADD, QDSUB, SSAT, SSAT16, USAT и USAT16 (а также умножения с накоплением SMLAD, SMLABB и родственные — при переполнении); параллельные QADD16, QADD8, UQADD16, UQADD8 и другие команды этой группы насыщают результат, но флаг Q не трогают (A7.7.103, A7.7.205, A7.7.206). Команды ставят Q, но не сбрасывают его, поэтому флаг показывает, было ли насыщение хоть раз за блок вычислений; сбрасывают флаг записью в APSR командой MSR. Остальные команды DSP-расширения — в термине «DSP-инструкции».

На C

Стандартный C насыщения не умеет: выход знакового результата за диапазон типа — неопределённое поведение, а беззнаковые числа переносятся по модулю (C11, 6.5 и 6.2.5). На Cortex-M насыщение вызывают через функции CMSIS: __SSAT(x, 16) и __USAT(x, 8) есть для всех ядер — на Armv7-M и выше это одна команда, на остальных ядрах cmsis_gcc.h подставляет реализацию на C. Функции __QADD() и подобные определены только для ядер с DSP-расширением. Как устроены вычисления с фиксированной точкой и где в них нужно насыщение, разобрано в статье «Фиксированная точка».

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

Чем насыщение отличается от переполнения?

При обычном сложении в дополнительном коде результат, вышедший за диапазон, берётся по модулю: в int16 сумма 30 000 + 10 000 даёт −25 536 (наш расчёт: 40 000 − 65 536). При насыщении результат прижимается к ближайшей границе: 32 767 для положительного выхода, −32 768 для отрицательного. Ошибка остаётся, но знак и порядок величины верны.

Как сбросить флаг Q?

Команды SSAT, USAT, QADD и другие только устанавливают Q, когда насыщение произошло, и сами его не сбрасывают. Поэтому флаг Q удобен для проверки «было ли насыщение хоть раз» за целый блок вычислений, а сбрасывают его записью в регистр APSR командой MSR APSR_nzcvq (Armv7-M ARM, разд. A2.3.2 и B5.2.3).

Можно ли использовать насыщение на Cortex-M0?

Аппаратных команд SSAT и USAT у Cortex-M0 нет (DUI 0497A), но функции CMSIS __SSAT и __USAT работают и там: для ядер без этих команд cmsis_gcc.h даёт реализацию на C с тем же результатом, только медленнее. Команда QADD и другие команды DSP-расширения доступны лишь ядрам Armv7E-M.

Источники

  1. Arm. Armv7-M Architecture Reference Manual (ARM DDI 0403E.e, ID021621) — разд. A2.2 Pseudocode details of saturation: при выходе результата за знаковый или беззнаковый N-битный диапазон выдаётся наибольшее или наименьшее значение диапазона вместо переноса по модулю 2^N; SignedSatQ: больше 2^(N−1) − 1 → 2^(N−1) − 1, меньше −2^(N−1) → −2^(N−1); UnsignedSatQ: больше 2^N − 1 → 2^N − 1, меньше 0 → 0; разд. A2.3.2: флаг Q (бит 27 APSR) ставится, если SSAT или USAT изменили значение, а в процессорах с DSP-расширением — и при переполнении некоторых умножений; разд. A4.4.4, табл. A4-9: SSAT и USAT в базовой архитектуре Armv7-M; табл. A4-10 и A4-11: SSAT16, USAT16, QADD, QSUB, QDADD, QDSUB — DSP-расширение; разд. A7.7.152: SSAT насыщает значение, при необходимости сдвинутое (LSL 0–31 или ASR 1–31), до разрядности от 1 до 32 бит, при насыщении ставит Q; разд. A7.7.213: USAT насыщает до беззнакового числа разрядностью 0–31 бит, при насыщении ставит Q; разд. A7.7.103, A7.7.205, A7.7.206: QADD16, UQADD16, UQADD8 насыщают результат, но флаг Q не меняют; разд. A7.7.102: QADD — сложение с насыщением до диапазона −2^31…2^31 − 1, при насыщении ставит Q; разд. B5.1 и B5.2.3 MSR: запись APSR_nzcvq меняет флаги N, Z, C, V и Q
  2. Arm. CMSIS-Core cmsis_gcc.h, V5.4.1, 27 May 2021 (Apache-2.0) — __SSAT(val, sat) и __USAT(val, sat), sat — от 1 до 32: для Armv7-M, Armv7E-M и Armv8-M Mainline — команды SSAT и USAT, для остальных ядер — функции на C с тем же результатом; __QADD и другие — только при __ARM_FEATURE_DSP == 1
  3. Arm. Cortex-M3 Devices Generic User Guide (ARM DUI 0552A) — разд. 3.7 Saturating instructions: у Cortex-M3 команды насыщения — SSAT и USAT; бит 27 регистра состояния — флаг насыщения Q
  4. Arm. Cortex-M0 Devices Generic User Guide (ARM DUI 0497A) — гл. 3 — набор команд Cortex-M0 (Armv6-M): команд SSAT и USAT в нём нет
  5. ISO/IEC 9899:201x (C11), Committee Draft N1570, 12 April 2011 — 6.2.5 п. 9: вычисление с беззнаковыми операндами не переполняется, результат берётся по модулю числа на единицу больше максимального значения типа; 6.5 п. 5: если результат выражения вне диапазона представимых значений типа, поведение не определено

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