Можно ли использовать вычисления с плавающей запятой в любой надежной функции, в частности, в контейнерах и алгоритмах? - PullRequest
0 голосов
/ 29 мая 2019

В C и C ++ вычисления с плавающей запятой по умолчанию не являются детерминированными, поскольку пользователь не выбирает даже истинный тип данных, как для любого промежуточного вычисления подвыражения FP, компилятор может выбрать представление значения с более высокой точностью(то есть как еще один real тип данных) .

[Известно, что некоторые компиляторы (GCC) делали это для любой автоматической переменной, а не только для (анонимного) промежуточного результатаподвыражение.]

Компилятор может сделать это за несколько вычислений, в некоторых функциях;он может делать это в некоторых случаях, а не в других - для одного и того же подвыражения.

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

[Обратите внимание, что это похоже на определение строковых литералов: любые два вычисления одного и того же строкового литерала в исходном коде могут ссылаться на одинаковые или разныесимвольные массивы.]

Это означает, что даже для чисто аппликативных функций фундаментальное равенство f(x) == f(x) гарантируется, только если используются операции с плавающей запятой (и строковые литералы) , а не (или чтоадрес строкового литерала используется только для доступа к его элементам).

Таким образом, операции с плавающей запятой имеют недетерминированную семантику с произвольным выбором, сделанным компилятором для каждой операции FP (котораякажется намного более извращенцамиТо, что очень небольшая проблема - позволить компилятору выбрать, какое подвыражение A или B будет вычисляться первым в A+B).

Кажется, что функция, которая выполняет любые вычисления с промежуточными значениями с плавающей запятой, не может использоваться ни в одном STLконтейнер или алгоритм, который ожидает функтор , удовлетворяющий аксиомам , такой, что

  • сортирует контейнеры: set, map, multiset, multimap
  • хэшированные контейнеры
  • алгоритмы сортировки: sort, stable_sort
  • алгоритм, работающий в отсортированных диапазонах: lower_bound, set_union, set_intersection ...

, поскольку все двоичные предикаты и хеш-функции должны быть детерминированными, прежде чем аксиомы могут быть даже задуманы , что они должны быть чисто прикладной математической функцией с определенным значением всех возможных входных данных, что никогда не являетсяслучай с недетерминированными промежуточными значениями C ++ с недетерминированными значениями?

Другими словами, являются ли операции с плавающей запятой по умолчанию почти непригодными для использования только на основе стандарта иМожно ли использовать в реальных реализациях, которые имеют некоторые (смутные) неявные гарантии детерминизма?

1 Ответ

1 голос
/ 29 мая 2019

Компилятору разрешается использовать операции и временные значения с более высокой точностью при оценке арифметических выражений с плавающей точкой. Поэтому, если вы сделаете a1 = b+c; a2 = b+c;, тогда да, возможно, что a1 и a2 не будут равны друг другу (из-за двойного округления).

Это действительно не имеет значения для упомянутых вами алгоритмов и контейнеров; те не делают никакой арифметики на их значениях. «Аксиомы», на которые они опираются, - это не более упорядоченные отношения.

Худшее, что вы можете сказать, это то, что если вы ранее сохранили b+c, скажем, в set, выполнение mySet.find(b+c) может завершиться неудачей. Так что да, не делай этого. Но это то, что вы обычно вообще не делаете, ошибка округления, вызванная арифметикой с плавающей точкой, уже делает редким ожидание точного равенства от производных величин.

Единственный случай, когда вам действительно придется беспокоиться о расширенной точности, - это когда вы вычисляете теоретические границы ошибок FP для последовательности операций. Когда вы это сделаете, вы будете знать, что искать. До тех пор эта проблема не является проблемой.

...