Работа с ошибками с плавающей запятой в .NET

Я работаю над научным проектом по вычислениям и визуализации в C # /. NET, и мы используемdoubles для представления всех физических величин. Так как числа с плавающей точкой всегда включают в себя немного округления, у нас есть простые методы для сравнения на равенство, такие как:

static double EPSILON = 1e-6;

bool ApproxEquals(double d1, double d2) {
    return Math.Abs(d1 - d2) < EPSILON;
}

Довольно стандартный.

Однако нам постоянно приходится корректироватьEPSILON поскольку мы сталкиваемся с ситуациями, в которых ошибка «равных» величин больше, чем мы ожидали. Например, если вы умножаете 5 большихdoubles вместе, а затем разделить 5 раз, вы теряете много точности. Дошло до того, что мы не можем сделать EPSILON слишком большим, иначе это даст нам ложные срабатывания, но мы все равно получим ложные отрицания.

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

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

Кроме того, переход наDecimal имеет другие осложнения, такие как общее отсутствие поддержки вMath библиотека, так что мы должны написать нашу собственную функцию квадратного корня, например.

Любой совет?

РЕДАКТИРОВАТЬ: кстати, тот факт, что я использую постоянный эпсилон (вместо относительного сравнения), не является вопросом моего вопроса. Я просто привожу это в качестве примера, на самом деле это не фрагмент моего кода. Изменение относительного сравнения не будет иметь значения для вопроса, потому что проблема возникает из-за потери точности, когда числа становятся очень большими, а затем снова маленькими. Например, у меня могло бы быть значение 1000, и затем я делаю серию вычислений, которые должны привести к тому же самому числу, но из-за потери точности у меня фактически есть 1001. Если я тогда иду, чтобы сравнить те числа, это не Не имеет большого значения, использую ли я относительное или абсолютное сравнение (при условии, что я определил сравнения таким образом, чтобы они имели значение для проблемы и масштаба).

В любом случае, как предположил Митч Уит, изменение порядка алгоритмов помогло с проблемами.

Ответы на вопрос(3)

Ваш ответ на вопрос