Есть ли сценарий, в котором можно использовать функцию fma в libc?

Я встречаю эту страницу и обнаруживаю, что существует нечетная функция многократного добавления с плавающей запятой - fma и fmaf. В нем говорится, что результат выглядит примерно так:

 (x * y) + z             #fma(x,y,z)

И значение равно бесконечной точности и округляет один раз до формата результата.

Однако, AFAICT Я никогда раньше не видел такой тройственной операции. Поэтому мне интересно, какое использование cumstom для этой функции.

Ответ 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.

Ответ 2

Он обычно используется в качестве оптимизации. Большинство блоков с плавающей запятой имеют инструкцию fma, поэтому расчет может выполняться в одной команде вместо двух или более. Таким образом, для критически важного кода с плавающей запятой это полезная функция.