Como posso comparar corretamente valores duplos para igualdade em um teste de unidade?

20

Projetei recentemente um módulo de série temporal em que minha série temporal é essencialmente a SortedDictionnary<DateTime, double>.

Agora eu gostaria de criar testes de unidade para garantir que este módulo esteja sempre funcionando e produzindo o resultado esperado.

Uma operação comum é calcular o desempenho entre os pontos da série temporal.

Então, o que faço é criar uma série temporal com, digamos, {1.0, 2.0, 4.0} (em algumas datas), e espero que o resultado seja {100%, 100%}.

O problema é que, se eu criar manualmente uma série temporal com os valores {1.0, 1.0} e verificar a igualdade (comparando cada ponto), o teste não passará, pois sempre haverá imprecisões ao trabalhar com representações binárias de valores reais. números.

Por isso, decidi criar a seguinte função:

private static bool isCloseEnough(double expected, double actual, double tolerance=0.002)
{
    return squaredDifference(expected, actual) < Math.Pow(tolerance,2);
}

Existe outra maneira comum de lidar com esse caso?

SRKX
fonte

Respostas:

10

Posso pensar em duas outras maneiras de lidar com esse problema:

Você pode usar Is.InRange:

Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));

Você pode usar Math.Round:

Assert.That(Math.Round(result, sigDigits), Is.EqualTo(expected));

Penso que os dois lados são mais expressivos do que uma função dedicada, porque o leitor pode ver exatamente o que está acontecendo com seu número antes que ele seja comparado ao valor esperado.

dasblinkenlight
fonte
2
Apenas observe que esta resposta é específica da NUnit e mostra o modelo de asserção "Baseado em construtores". O modelo de asserção clássico seria semelhante a: Assert.AreEqual (esperado, real, tolerância);
7772 RichardM
1
@ Richardhard: Publique isso como resposta e eu o selecionarei para aceitá-lo.
001 SRKX em 07/02
A resposta de @dasblinkenlight está correta, apenas adicionando alguns detalhes (já que pode não estar claro - o modelo clássico de asserção também é NUnit). Outras estruturas de teste (não o MSTest) provavelmente têm seu próprio modelo de declaração para lidar com valores de ponto flutuante.
7262 RichardM
1
Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));falhará se tolerance/abs(expected) < 1E-16.
2241212 quant_dev
@quant_dev Você está absolutamente certo. Como o OP fala sobre o cálculo de retornos como porcentagens, presumi que abs(expected)seria um dígito para dois dígitos. Também assumi a tolerância nas proximidades de 1E-9. Sob essas premissas, essa abordagem reconhecidamente simplista pode atendê-lo razoavelmente bem (eu uso Is.InRangenos meus testes).
precisa saber é o seguinte
6

Como RichardM sugeriu, se você não usa o NUnit, a melhor maneira parece estar usando Assert.AreEqual (double, double, double) , onde o último é a precisão.

SRKX
fonte
3

Depende do que você faz com os números. Se você estiver testando um método que deveria, por exemplo, selecionar um valor apropriado de um conjunto de entradas com base em alguns critérios, deverá testar a igualdade estrita. Se você estiver fazendo cálculos de ponto flutuante, normalmente precisará testar com uma tolerância diferente de zero. O tamanho da tolerância depende dos cálculos, mas com precisão dupla, um bom ponto de partida é escolher a tolerância relativa 1E-14 para cálculos simples e 1E-8 (tolerância) para cálculos mais complicados. YMMV, é claro, e você precisará adicionar uma pequena tolerância absoluta se o resultado esperado for 0.

quant_dev
fonte