Числа №14. Фиксированная запятая
Мы много говорили о числах с плавающей запятой. Есть и другой формат – дробные числа с фиксированной запятой. Давайте кратко их рассмотрим.
Об этих числах я упоминал в заметке о первой Playstation. Это эмуляция дробных чисел для архитектур без математического сопроцессора. Главное их преимущество в том, что все операции с ними выполняются как целочисленные.
У фиксированных чисел, как ясно из определения, дробный разделитель не плавает туда-сюда. Например, мы решили, что после запятой хранится два разряда, и так будет всегда. Не нужно нормализовать числа и приводить их к общим экспонентам.
Число с фиксированной запятой хранится как целое, умноженное на некий коэффициент. Для простоты рассмотрим десятичные числа. Будем хранить цены в рублях с двумя десятичными знаками (копейками):
Кофе 80.99
Булочка 115.50
Коэффициент равен 10^-2 (или 0.01), а их целые представления равны:
Кофе 8099
Булочка 11550
Складываются и вычитаются такие числа в одну машинную команду. Найдем сумму покупки:
1
8099
11550
-----
19649
Приведем к дробному виду, чтобы были видны копейки:
19649 * 10^-2 = 196.49
Это было сложение, а теперь рассмотрим умножение. Умножать булочку на кофе немного странно, но давайте не думать об этом.
Числа 80.99 и 115.50 можно записать таким образом:
80.99 = 8099 * 10^-2
115.50 = 11550 * 10^-2
Их произведение:
8099 * 10^-2 * 11550 * 10^-2
Две степени десяти схлопываются в одну: -2 + -2 = -4
8099 * 11550 * 10^-4
Умножение на степень десяти равносильно тому, чтобы сдвинуть разряд влево или
вправо. Произведение 8099 * 11550 дает 93543450, и нужно сдвинуть
разделитель влево на 4 позиции. Результат:
9354.3450
Однако мы храним только две цифры после запятой. Поэтому хвост 3450
округляется до двух знаков. Цифра 5 округляется вверх, и на конце оказывается
35:
9354.35
Целое (внутреннее) представление становится таким образом 935435.
Вот как устроены числа с фиксированной запятой.
Выше мы рассматрели их в десятичном виде, потому что так удобней. В двоичном
виде происходит то же самое. Договариваются, что первые 16 бит хранят целую
часть, а остальные 16 – дробную. Сокращенно записывают так: Q16.16. Могут быть
и другие варианты: больше бит на целую часть (Q24.8) или наоборот – на дробную
(Q8.24). Вместо десятичного сдвига происходит битовый силами процессора.
С такими числами работали игровые консоли вроде Playstation, Sega Saturn и GameBoy. Очевидный их минус в том, что, во-первых, диапазон значений гораздо ниже, чем у float. Во-вторых, нужно следить за переполнением целой части. Чтобы не допустить переполнения, числа приводят к аналогам, где у целой части больше бит. Например, конвертируют Q16.16 в Q24.8, выполняют расчеты, а затем возвращаются к Q16.16.
Раньше числа с фиксированной запятой использовались часто. Например, в Doom для расчета углов; в LaTeX для растеризации шрифтов; в ранних играх Blizzard (Starcraft).
В следующей заметке я хочу рассказать о Старкрафте: какую роль в нем играли числа с фиксированной запятой и почему разработчики не использовали целые.
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter