Существуют ли компиляторы, которые оптимизируют операции с плавающей запятой для точности (в отличие от скорости)?

Мы знаем, что компиляторы все лучше и лучше оптимизируют наш код и заставляют его работать быстрее, но мой вопрос: существуют ли компиляторы, которые могут оптимизировать операции с плавающей запятой для обеспечения большей точности?

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

Итак, операция с плавающей запятой, например

y = x*(a + b); // faster but less accurate

Следует изменить на

y = x*a + x*b; // slower but more accurate

Существуют ли какие-либо компиляторы, которые оптимизируют для повышения точности с плавающей запятой за счет скорости, как я показал выше? Или главная забота о скорости компиляторов без учета точности операций с плавающей запятой?

Спасибо

Обновление: выбранный ответ показал очень хороший пример, когда этот тип оптимизации не будет работать, поэтому компилятор не сможет заранее узнать, как точнее оценить y. Спасибо за встречный пример.


person hhafez    schedule 14.01.2010    source источник
comment
Как компилятор узнает, нужна ли вам точность или скорость, кроме как выбора между формами y = x*(a + b) и y = x*a + x*b?   -  person Robert Harvey    schedule 14.01.2010
comment
Не существует универсальных правил относительно того, какой порядок операций будет наиболее точным. Это зависит от ожидаемых значений x, a и b. Это также может зависеть от вашего варианта использования. Иногда вам может понадобиться захотеть арифметику с плавающей запятой, а не максимально возможное приближение к арифметике вещественных чисел. И тогда переупорядочение компиляторов даст вам ответы, которые будут дальше от желаемых значений - это внесет неточности.   -  person jalf    schedule 14.01.2010
comment
@Robert Harvey, просто компилятор предоставил бы разные флаги оптимизации :)   -  person hhafez    schedule 14.01.2010
comment
@jalf, конечно, нет универсальных правил, которые применялись бы даже к обычной оптимизации, поэтому компиляторы поставляются с таким количеством опций, чтобы вы могли точно настроить, какие оптимизации вы хотите включить или выключить, я могу представить себе компилятор, который будет иметь эти типы эвристики FP в качестве флагов оптимизации. А тот, кому действительно не нужно приближение к реальным цифрам, может потом просто не включать оптимизацию   -  person hhafez    schedule 14.01.2010


Ответы (3)


Ваша предпосылка ошибочна. x*(a + b), (в общем) не менее точен, чем x*a + x*b. Фактически, он часто будет более точным, потому что он выполняет только две операции с плавающей запятой (и, следовательно, допускает только две ошибки округления), тогда как последний выполняет три операции.

Если вы знаете что-то об ожидаемом распределении значений для x, a и b априори, вы можете принять обоснованное решение, но компиляторы почти никогда не имеют доступа к такого рода информации.

Кроме того, что, если человек, пишущий программу, на самом деле имеет в виду x*(a+b) и конкретно хочет округлить именно то, что вызвано этой конкретной последовательностью операций? Такого рода вещи на самом деле довольно распространены в высококачественных численных алгоритмах.

Лучше делать то, что написал программист, а не то, что, как вы думаете, он мог иметь в виду.

Изменить – пример, иллюстрирующий случай, когда предложенное вами преобразование приводит к катастрофической потере точности: предположим,

x = 3.1415926535897931
a = 1.0e15
b = -(1.0e15 - 1.0)

Затем, оценивая в double, мы получаем:

x*(a + b) = 3.1415926535897931

но

x*a + x*b = 3.0
person Stephen Canon    schedule 14.01.2010
comment
Мое предположение верно: в первом примере есть два сложения и одно умножение, а во втором — два сложения и два умножения. Умножения в операциях FP не вносят никакой точности. Это факт :) Однако добавление FP с очень разными величинами вносит неточности, поэтому выполняйте умножение до того, как эти неточности будут введены, чтобы увеличить их величину, а не делать это заранее. - person hhafez; 14.01.2010
comment
Кроме того, ваше замечание о том, что лучше делать то, что пишет программист, а не то, что, по вашему мнению, он, возможно, намеревался, иногда верно, но не всегда, в этом вся предпосылка оптимизации. Компилятор делает что-то эквивалентное, но быстрее, однако эквивалент не является на самом деле 100% эквивалентным, поскольку мы знаем, что иначе он не был бы быстрее, поэтому делать то, что всегда говорит программист, означает отключить всю оптимизацию. Включение оптимизации допустимо во многих случаях, но, как правило, с этим можно поспорить. - person hhafez; 14.01.2010
comment
Когда мы говорим об оптимизации, мы обычно определяем эквивалент как среднее, дающее точно такой же результат. Это преобразование не делает - следовательно, оно не эквивалентно. - person Anon.; 14.01.2010
comment
Утверждение, что умножение с плавающей запятой никогда не приводит к округлению, просто вопиюще ложно. - person Stephen Canon; 14.01.2010
comment
+1. Посылка ошибочна, если вы не знаете ожидаемых величин переменных. Больше операций означает больше усечения или округления (даже с умножением). Я не уверен, почему hhafez утверждает, что в каждом примере есть два дополнения. - person Adrian McCarthy; 14.01.2010
comment
точка принята, но я могу привести встречный пример, я думаю, что вы продемонстрировали, что не существует золотых правил - person hhafez; 14.01.2010
comment
Да, бывают случаи, когда (например) x*a + x*b выдает правильно округленный результат, а x*(a + b) отличается на ulp в последнюю очередь. Вот почему я использовал фразу часто быть более точным, не всегда быть более точным. - person Stephen Canon; 14.01.2010
comment
x*a + x*b можно сделать с 2-мя округлениями с помощью FMA, но, возможно, это все же медленнее и не более правильно, чем x*(a+b) - person phuclv; 17.04.2015
comment
@LưuVĩnhPhúc: FMA действительно дает вам два округления, но фактически делает преобразование более опасным в некотором смысле: если a = -b, то x*(a+b) и x*a + x*b точно равны нулю, а fma(x, a, x*b) часто нет. - person Stephen Canon; 18.04.2015

Компиляторы обычно «оптимизируют» точность, а не скорость, точность определяется как точная реализация стандарта IEEE 754. В то время как операции с целыми числами могут быть переупорядочены любым способом, не вызывающим переполнения, операции FP должны выполняться точно так, как указывает программист. Это может принести в жертву числовую точность (обычные компиляторы C не приспособлены для оптимизации для этого), но точно реализует то, что просил программист.

Программист, который уверен, что он не оптимизировал точность вручную, может включить функции компилятора, такие как -funsafe-math-optimizations и -ffinite-math-only GCC, чтобы, возможно, получить дополнительную скорость. Но обычно большого выигрыша нет.

person Potatoswatter    schedule 14.01.2010

Нет, нет. Стивен Кэнон приводит несколько веских причин, почему это было бы глупой идеей, и он прав; поэтому вы не найдете компилятор, который это делает.

Если вы, как программист, имеете некоторое представление о диапазонах чисел, которыми вы манипулируете, вы можете использовать круглые скобки, временные переменные и подобные конструкции, чтобы убедительно намекнуть компилятору о том, как вы хотите, чтобы все было сделано.

person Carl Smotricz    schedule 14.01.2010