Арифметика с насыщением
Сложите в 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 536 | 32 767 |
| −30 000 − 10 000 | 25 536 | −32 768 |
| 1 000 + 2 000 | 3 000 | 3 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.
Источники
- 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
- 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
- Arm. Cortex-M3 Devices Generic User Guide (ARM DUI 0552A) — разд. 3.7 Saturating instructions: у Cortex-M3 команды насыщения — SSAT и USAT; бит 27 регистра состояния — флаг насыщения Q
- Arm. Cortex-M0 Devices Generic User Guide (ARM DUI 0497A) — гл. 3 — набор команд Cortex-M0 (Armv6-M): команд SSAT и USAT в нём нет
- ISO/IEC 9899:201x (C11), Committee Draft N1570, 12 April 2011 — 6.2.5 п. 9: вычисление с беззнаковыми операндами не переполняется, результат берётся по модулю числа на единицу больше максимального значения типа; 6.5 п. 5: если результат выражения вне диапазона представимых значений типа, поведение не определено
Нашли ошибку?
Смотрите также
- Формат QQm.n: дробные числа в целых, Q15 и Q31
- Дополнительный кодTwo's complement: запись отрицательных целых
- Флаг переполнения (V)Выход знакового результата за диапазон
- DSP-инструкции Armv7E-MSIMD, двойное умножение с накоплением и насыщение в ядрах Cortex-M
- Фиксированная точкаФормат Q, умножение со сдвигом, округление, насыщение, q15 и _Fract