Я работаю над научным проектом по вычислениям и визуализации в C # /. NET, и мы используем double
s для представления всех физических величин. Так как числа с плавающей точкой всегда включают в себя немного округления, у нас есть простые методы для сравнения на равенство, такие как:
static double EPSILON = 1e-6;
bool ApproxEquals(double d1, double d2) {
return Math.Abs(d1 - d2) < EPSILON;
}
Довольно стандартно.
Однако нам постоянно приходится корректировать величину EPSILON
, когда мы сталкиваемся с ситуациями, в которых погрешность «равных» величин больше, чем мы ожидали. Например, если вы умножите 5 больших double
с вместе, а затем разделите 5 раз, вы потеряете много точности. Дошло до того, что мы не можем сделать EPSILON слишком большим, иначе это даст нам ложные срабатывания, но мы все равно получим ложные отрицания.
В целом наш подход заключался в поиске более числово-стабильных алгоритмов для работы, но программа очень вычислительная, и мы смогли сделать лишь так много.
Есть ли у кого-нибудь хорошие стратегии для решения этой проблемы? Я немного изучил тип Decimal
, но обеспокоен производительностью и не знаю достаточно о нем, чтобы знать, решит ли он проблему или только затемнит ее. Я был бы готов принять умеренное снижение производительности (скажем, в 2 раза), перейдя к Decimal
, если это решит эти проблемы, но производительность, безусловно, является проблемой, и поскольку код в основном ограничен арифметикой с плавающей точкой, я не Не думаю, что это необоснованная проблема. Я видел людей, цитирующих разницу в 100 раз, что определенно было бы неприемлемо.
Кроме того, переключение на Decimal
имеет другие сложности, такие как общее отсутствие поддержки в библиотеке Math
, поэтому мы должны написать нашу собственную функцию квадратного корня, например.
Любой совет?
РЕДАКТИРОВАТЬ: кстати, тот факт, что я использую постоянный эпсилон (вместо относительного сравнения) не является вопросом моего вопроса. Я просто привожу это в качестве примера, на самом деле это не фрагмент моего кода. Изменение относительного сравнения не будет иметь значения для вопроса, потому что проблема возникает из-за потери точности, когда числа становятся очень большими, а затем снова маленькими. Например, у меня могло бы быть значение 1000, и затем я делаю серию вычислений, которые должны привести к тому же самому числу, но из-за потери точности у меня фактически есть 1001. Если я тогда иду, чтобы сравнить те числа, это не Не имеет большого значения, использую ли я относительное или абсолютное сравнение (при условии, что я определил сравнения таким образом, чтобы они имели значение для проблемы и масштаба).
В любом случае, как предложил Митч Уит, изменение порядка алгоритмов помогло с проблемами.