Ответ 1
Важным аспектом инструкции с объединенным умножением-добавлением является (практически) бесконечная точность промежуточного результата. Это помогает с производительностью, но не столько потому, что две операции кодируются в одной команде - это помогает в производительности, потому что практически бесконечная точность промежуточного результата иногда важна, а очень дорого восстанавливается с помощью обычных умножение и добавление, когда этот уровень точности действительно то, что программист после.
Пример: сравнение a * b
с 1.0
Предположим, что для алгоритма важно определить, где произведение двух чисел двойной точности a
и b
относится к ненулевой константе (мы будем использовать 1.0
). Числа a
и b
имеют полные значения двоичных цифр. Если вы вычисляете a*b
как double
, результат может быть 1.0
, но это не говорит вам, был ли фактический математический продукт немного ниже 1.0 и округлен до точно 1,0 или чуть выше 1,0 и округлен. Без FMA ваши варианты:
-
вычислить
a*b
как число с четырьмя точностью. Quad-точность не реализована в аппаратных средствах, но есть библиотеки эмуляции программного обеспечения. В quad-precision математический результат продукта точно представлен, и вы можете сравнить его с 1.0. -
Вычислить
a*b
в двойной точности в режиме округления вверх и в режиме округления вниз. Если оба результата равны 1.0, значит,a*b
- ровно 1,0. Если RU (a * b) больше 1,0, это означает, что математический продукт выше 1.0, а если RD (a * b) меньше 1.0, это означает, что математический продукт ниже 1.0. Для большинства процессоров этот подход означает изменение режима округления три раза, и каждое изменение дорого (оно включает в себя очистку конвейера CPU).
С инструкцией FMA можно вычислить fma(a, b, -1.0)
и сравнить результат с 0.0. Поскольку числа с плавающей запятой плотнее вокруг нуля, а так как промежуточный продукт не округлен в вычислении, мы можем быть уверены, что fma(a, b, -1.0) > 0
означает, что математический продукт a
и b
больше 1 и т.д..
Пример: умножение Veltkamp/Dekker
double-double - эффективное представление чисел в виде суммы двух чисел с плавающей запятой с двойной точностью. Это почти так же точно, как и четырехточечная точность, но использует преимущества существующего оборудования с двойной точностью.
Рассмотрим следующую функцию Mul12(a, b)
, которая принимает два числа двойной точности a
и b
и вычисляет их произведение как двойное двойное число. Алгоритм, из-за Veltkamp и Dekker, вычисляет эту функцию только с добавлением и умножением двойной точности (ссылка). Он принимает 6 умножений (один является частью каждого Split()
плюс четыре в основной части алгоритма) и множество дополнений.
Если имеется инструкция FMA, Mul12
может быть реализована как две операции, одно умножение и одно FMA.
high = a * b; /* double-precision approximation of the real product */
low = fma(a, b, -high); /* remainder of the real product */
/* now the real product of a and b is available as the sum of high and low */
Дополнительные примеры
Примеры, где FMA используется для его точности, а не только как инструкция, которая выполняет умножение и добавление, являются вычислением квадратного корня и деления. Эти операции должны быть правильно округлены (до ближайшего числа с плавающей запятой математического результата) в соответствии со стандартом IEEE 754. Эти две операции могут быть эффективно реализованы при наличии аппаратной команды FMA. Этот аспект обычно скрывается цепочкой компиляции, но набор команд IA-64 (Itanium) не имеет инструкции для деления. Вместо этого правильно округленное деление можно получить с помощью последовательности инструкций (обычно генерируемых компилятором) с участием FMA.