Компьютерная точность: когда мне следует об этом беспокоиться?

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

std::vector<double> first (50000, 0.0);
std::vector<double> second (first);

Возможно ли, что second[619] = 0.00000000000000000000000000001234 (я имею в виду очень маленькое значение). Или SUM = second[0]+second[1]+...+second[49999] => 1e-31? Или SUM = second[0]-second[1]-...-second[49999] => -7.987654321e-12?

Мои вопросы:

  1. Могут ли быть какие-то небольшие нарушения в работе с номерами типа double?
  2. Что может вызывать такие мелкие помехи? то есть ошибки округления становятся большими? Не могли бы вы перечислить их? Как соблюдать меры предосторожности?
  3. Если при определенных операциях могут возникнуть небольшие помехи, означает ли это, что после этих операций использование if (SUM == 0) опасно? Вместо этого всегда следует использовать if (SUM < SMALL), где SMALL определяется как очень маленькое значение, например 1E-30?
  4. Наконец, могут ли небольшие возмущения привести к отрицательному значению? Потому что, если это возможно, мне лучше использовать if (abs(SUM) < SMALL).

Есть опыт?


person Daniel    schedule 02.05.2013    source источник


Ответы (2)


Это хороший справочный документ по точности с плавающей запятой: Должен знать об арифметике с плавающей запятой

Одна из наиболее важных частей — катастрофическая отмена.

Катастрофическая отмена происходит, когда операнды подвержены ошибкам округления. Например, в квадратичной формуле встречается выражение b2 - 4ac. Величины b2 и 4ac подвержены ошибкам округления, поскольку они являются результатом умножения с плавающей запятой. Предположим, что они округлены до ближайшего числа с плавающей запятой и поэтому имеют точность в пределах 0,5 ulp. Когда они вычитаются, отмена может привести к исчезновению многих точных цифр, оставив после себя в основном цифры, загрязненные ошибкой округления. Следовательно, разница может иметь погрешность в несколько ulps. Например, рассмотрим b = 3,34, a = 1,22 и c = 2,28. Точное значение b2 - 4ac равно 0,0292. Но b2 округляется до 11,2, а 4ac округляется до 11,1, следовательно, окончательный ответ равен 0,1, что является ошибкой на 70 ulps, хотя 11,2 - 11,1 в точности равно 0,16. Вычитание не внесло никакой ошибки, а скорее выявило ошибку, внесенную в более ранние умножения.

Доброкачественное аннулирование происходит при вычитании точно известных величин. Если x и y не имеют ошибки округления, то по теореме 2, если вычитание производится со сторожевым разрядом, разность x-y имеет очень маленькую относительную ошибку (менее 2).

Формула, демонстрирующая катастрофическую отмену, иногда может быть изменена, чтобы устранить проблему. Снова рассмотрим квадратичную формулу

person Jean-Bernard Pellerin    schedule 02.05.2013

В вашем конкретном примере 0 имеет точное представление как двойное, и добавление ровно 0 к двойному не меняет его значения.

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

Чтобы составить лучшее мнение о «помехах», мне нужно знать, какие виды вычислений выполняет ваш код.

person NovaDenizen    schedule 02.05.2013