Согласованное поведение кода с плавающей точкой с GCC - PullRequest
3 голосов
/ 26 февраля 2010

Я занимаюсь численными вычислениями, и у меня часто возникали проблемы с вычислениями с плавающей запятой при использовании GCC. Для моей нынешней цели меня не очень заботит реальная точность результатов, но я хочу эту фирму:

Неважно, ГДЕ тот же код в моей программе, когда он запускается на тех же самых входах, я хочу, чтобы он давал те же выходные данные.

Как я могу заставить GCC сделать это? В частности, каково поведение --fast-math и различных оптимизаций -O?

Я слышал, что GCC может пытаться быть умным, иногда загружать поплавки в регистры и иногда читать их непосредственно из памяти, и что это может изменить точность поплавков, что приведет к другому выводу. Как я могу избежать этого?

Опять хочу:

  • мои вычисления будут быстрыми
  • мои вычисления должны быть надежными (т.е. один и тот же ввод -> тот же результат)
  • Меня не волнует точность этого конкретного кода, поэтому я могу быть в порядке с пониженной точностью, если это принесет надежность

Может кто-нибудь сказать мне, как решить эту проблему?

Ответы [ 2 ]

1 голос
/ 26 февраля 2010

Если ваши цели включают процессоры x86, использование переключателя, заставляющего gcc использовать инструкции SSE2 (вместо исторических, основанных на стеке), сделает их более похожими на другие.

Если ваши цели включают процессоры PowerPC, использование переключателя, который заставляет gcc не использовать инструкцию fmadd (для замены умножения с последующим добавлением в исходном коде), сделает их более похожими на др.

Не используйте --fast-math: это позволяет компилятору использовать некоторые комбинации клавиш, что приведет к различиям между архитектурами. Gcc более совместим со стандартами и поэтому предсказуем, без этой опции.

Включение ваших собственных математических функций (exp, sin, ...) в ваше приложение вместо того, чтобы полагаться на функции из системной библиотеки, может помочь только с предсказуемостью.

И, наконец, даже когда компилятор строго соблюдает стандарт (здесь я имею в виду C99), могут быть некоторые различия, потому что C99 позволяет вычислять промежуточные результаты с более высокой точностью, чем того требует тип выражения. Если вы действительно хотите, чтобы программа всегда давала одинаковые результаты, напишите трехадресный код . Или используйте только максимальную точность, доступную для всех вычислений, которая будет double, если вы можете избежать исторических инструкций x86. В любом случае не используйте поплавки с более низкой точностью в попытке улучшить предсказуемость: эффект будет противоположным, как указано выше в стандарте.

0 голосов
/ 26 февраля 2010

Я думаю, что GCC довольно хорошо задокументирован, поэтому я не собираюсь раскрывать свое собственное невежество, пытаясь ответить на части вашего вопроса о его возможностях и их последствиях. Тем не менее, я бы сделал общее заявление о том, что, когда речь идет о точности и производительности, это приносит большие дивиденды при чтении руководства. Умные люди, работающие над GCC, прилагают много усилий к своей документации, читая, что это полезно (хорошо, это может быть немного скучно, но, черт возьми, это руководство по компилятору, а не сборщик).

Если для вас важно, чтобы вы получали числовые результаты, идентичные последним битам, вам придется задуматься не только о GCC и о том, как вы можете контролировать его поведение. Вам нужно будет заблокировать библиотеки, которые он вызывает, оборудование, на котором он работает, и, возможно, ряд других факторов, о которых я еще не думал. В худшем (?) Случае вы можете даже захотеть, и я видел, как это сделано, написать свои собственные реализации математики f-p, чтобы гарантировать битовую идентичность на разных платформах. Это сложно и, следовательно, дорого, и оставляет вас, возможно, менее уверенным в правильности вашего собственного кода, чем в коде, используемом GCC.

Тем не менее, вы пишете

Меня не волнует точность этого конкретного кода, поэтому я могу быть в порядке с пониженной точностью, если это принесет надежность

, который задает вам вопрос - почему бы вам просто не использовать точность в 5 десятичных цифр в качестве стандарта (уменьшенной) точности? Это то, что очень многие из нас в области вычислительной техники делают все время; мы игнорируем более тонкие аспекты численного анализа, поскольку их трудно и дорого обходить во время вычислений. Я думаю о таких вещах, как интервальная арифметика и математика высокой точности. (Конечно, если 5 не подходит вам, выберите другое однозначное число.)

Но хорошая новость заключается в том, что это вполне оправданно: мы имеем дело с научными данными, которые по своей природе сопровождаются ошибками (конечно, мы обычно не знаем, что это за ошибки, но это другое дело), ​​поэтому можно игнорировать последние несколько цифр в десятичном представлении, скажем, 64-битного числа fp. Идите прямо вперед и игнорируйте еще несколько из них. Более того, не имеет значения, сколько битов у ваших чисел f-p, вы всегда будете терять точность при выполнении численных расчетов на компьютерах; добавление большего количества бит просто возвращает ошибки как к младшим битам, так и к концу длительных вычислений.

Вам следует остерегаться случая, когда у вас такой плохой алгоритм или плохая реализация алгоритма, что он быстро теряет много точности. Обычно это проявляется при любом разумном размере числа f-p. Ваш набор тестов должен был разоблачить это, если это реальная проблема для вас.

В заключение: вам нужно каким-то образом бороться с потерей точности, и не обязательно неправильно чистить более мелкие детали под ковром.

...