Есть ли сценарий, где можно использовать функцию FMA в libc?
Я наткнулся на эту страницу и обнаружил, что есть нечетная плавающая функция умножения - fma
а также fmaf
, Это говорит о том, что результат примерно такой:
(x * y) + z #fma(x,y,z)
И значение имеет бесконечную точность и округляется один раз до формата результата.
Однако, AFAICT, я никогда раньше не видел такой троичной операции. Так что мне интересно, что за использование Cumstom для этой функции.
2 ответа
Важным аспектом инструкции fused-multiply-add является (практически) бесконечная точность промежуточного результата. Это помогает с производительностью, но не так сильно, потому что две операции закодированы в одной инструкции. Это помогает с производительностью, потому что практически бесконечная точность промежуточного результата иногда важна, и очень дорого восстанавливать при обычном умножении и сложении, когда этот уровень Точность - это то, что нужно программисту.
Пример: сравнение a * b
в 1.0
Предположим, что для алгоритма важно определить, где произведение двух чисел двойной точности a
а также b
по отношению к ненулевой константе (мы будем использовать 1.0
). Число a
а также b
оба имеют полное значение двоичных цифр. Если вы вычисляете a*b
как double
результат может быть 1.0
, но это не говорит вам, был ли фактический математический продукт немного ниже 1,0 и округлен до точно 1,0, или немного выше 1,0 и округлен вниз. Без FMA, ваши варианты:
вычисление
a*b
как число с четверной точностью. Четверная точность не реализована в оборудовании, но есть библиотеки программной эмуляции. В четверной точности математический результат произведения является точно представимым, и вы можете сравнить его с 1,0.вычисление
a*b
с двойной точностью в режиме округления вверх и в режиме округления вниз. Если оба результата равны 1,0, это означаетa*b
точно 1,0. Если RU(a * b) больше 1,0, это означает, что математическое произведение выше 1,0, а если RD(a * b) ниже 1,0, это означает, что математическое произведение меньше 1,0. На большинстве процессоров этот подход означает изменение режима округления три раза, и каждое изменение является дорогостоящим (оно включает очистку конвейера ЦП).
С помощью инструкции FMA можно вычислить fma(a, b, -1.0)
и сравните результат с 0,0. Так как числа с плавающей точкой плотнее около нуля, а промежуточный продукт не округляется в вычислениях, мы можем быть уверены, что fma(a, b, -1.0) > 0
означает математическое произведение a
а также b
больше 1 и так далее.
Пример: умножение Вельткампа / Деккера
Формат double-double является эффективным представлением чисел как суммы двух чисел с плавающей запятой двойной точности. Он почти такой же точный, как и четырехточный, но использует преимущества существующего оборудования двойной точности.
Рассмотрим следующую функцию: Mul12(a, b)
, что занимает два числа с двойной точностью a
а также b
и вычисляет их произведение как двойное число Алгоритм, благодаря Вельткампу и Деккеру, вычисляет эту функцию только с добавлением и умножением с двойной точностью ( ссылка). Требуется 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.
Обычно используется как оптимизация. Большинство единиц с плавающей точкой имеют fma
инструкция, поэтому вычисление может быть выполнено в одной инструкции вместо двух или более. Поэтому для кода с плавающей запятой, важного для производительности, это полезная функция.