Lidando com erros de ponto flutuante no .NET

Estou trabalhando em um projeto de computação e visualização científica em C # /. NET e usamosdoubles para representar todas as quantidades físicas. Como os números de ponto flutuante sempre incluem um pouco de arredondamento, temos métodos simples para fazer comparações de igualdade, como:

static double EPSILON = 1e-6;

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

Bastante padrão.

No entanto, estamos constantemente precisando ajustar a magnitude deEPSILON quando encontramos situações em que o erro de quantidades "iguais" é maior do que havíamos previsto. Por exemplo, se você multiplicar 5 grandesdoubles juntos e, em seguida, divida 5 vezes, você perde muita precisão. Chegou ao ponto em que não podemos tornar o EPSILON muito maior, senão isso nos dará falsos positivos, mas ainda temos falsos negativos.

Em geral, nossa abordagem tem sido procurar algoritmos numericamente estáveis para trabalhar, mas o programa é muito computacional e há muito o que conseguimos fazer.

Alguém tem boas estratégias para lidar com esse problema? Eu olhei para oDecimal digite um pouco, mas estou preocupado com o desempenho e realmente não sei o suficiente para saber se isso resolveria o problema ou apenas o obscureceria. Eu estaria disposto a aceitar um impacto moderado no desempenho (digamos, 2x) indo paraDecimal se isso resolveria esses problemas, mas o desempenho é definitivamente uma preocupação e, como o código é limitado principalmente pela aritmética de ponto flutuante, não acho que seja uma preocupação irracional. Já vi pessoas citando uma diferença de 100x, o que definitivamente seria inaceitável.

Além disso, mudar paraDecimal tem outras complicações, como falta geral de apoio noMath biblioteca, então teríamos que escrever nossa própria função de raiz quadrada, por exemplo.

Algum conselho?

EDITAR: a propósito, o fato de eu estar usando um epsilon constante (em vez de uma comparação relativa) não é o ponto da minha pergunta. Apenas coloquei isso como exemplo, na verdade não é um trecho do meu código. Mudar para uma comparação relativa não faria diferença para a pergunta, porque o problema surge da perda de precisão quando os números ficam muito grandes e depois pequenos novamente. Por exemplo, eu posso ter um valor 1000 e, em seguida, faço uma série de cálculos que devem resultar exatamente no mesmo número, mas, devido à perda de precisão, na verdade tenho 1001. Se for comparar esses números, ele não importa muito se eu usar uma comparação relativa ou absoluta (desde que eu tenha definido as comparações de uma maneira que seja significativa para o problema e a escala).

De qualquer forma, como Mitch Wheat sugeriu, a reordenação dos algoritmos ajudou nos problemas.

questionAnswers(3)

yourAnswerToTheQuestion