Comparando problemas em DUnit CheckEquals com Valores de Campo de Moeda
Pergunta
Eu estou comparando alguns valores de moeda em DUnit, mas não está funcionando em tudo na minha máquina (trabalho em outros, mas não no meu).
Um exemplo:
CheckEquals(16.65, SomeCurrencyFieldValue);
Levanta:
expected: <16,65> but was: <16,65>
se fazer a seguinte comparação, em seguida, funciona:
var
Temp: Currency;
begin
Temp := 16.65;
CheckEquals(Temp, SomeCurrencyFieldValue);
A questão é:Por que a comparação não funciona quando eu passar o valor direto para o CheckEquals método?
Solução
O problema tem a ver com a forma como Currency
os valores são convertidos para Extended
valores em tempo de execução versus como ponto flutuante literais convertido para Extended
valores em tempo de compilação.Se a conversão não é o mesmo em ambos os casos, os valores passados para CheckEquals
não pode comparar igual.
Seria a pena conferir o depurador na CPU janela se qualquer um dos valores passa por um intermediário Double
valor em seu caminho para Extended
em preparação para a chamada de função.Um extra de conversão afetaria o valor exato do resultado.
Outra coisa a considerar é que 16.65 não é exatamente representáveis como um Extended
valor, mas é representáveis exatamente como um Currency
o valor.Apesar de Currency
é classificado como um tipo de ponto flutuante, ele é realmente um ponto fixo em escala inteiro de 64 bits.Essa é provavelmente a razão para o pedido de adicional CheckEquals
sobrecarga em DUnit que leva em conta.
Outras dicas
Vejo que existem apenas checkequals () para valores estendidos nas fontes Dunit Delphi 2007. Mas você pode usar este:
procedure CheckEquals(expected, actual: extended; delta: extended;
msg: string = ''); overload; virtual;
e dê um delta adequado para valores de moeda.
Eu encontrei o mesmo problema. Parece que algumas DLLs modificam uma palavra de controle FPU (processador). Isso explica por que o erro nem sempre ocorre. Pode aparecer repentinamente quando alguns novos testes, que usam outras unidades além do conjunto de testes anteriores, são adicionados. Ou se uma atualização de software instalar DLLs ruins. Eu escrevi sobre no meu blog:
Também descobri que o Delphi contém uma função SafeloAdlibrary, que restaura a palavra de controle.
Isso também explica por que a pergunta original menciona que o problema depende da máquina.
Aqui está uma solução sugerida:
procedure CheckEquals(expected, actual: double; Precision:integer; msg:string ='');overload;virtual;
...
procedure TAbstractTest.CheckEquals(expected, actual: double;
Precision: integer; msg: string);
var
I: Integer;
begin
FCheckCalled := true;
for I := 0 to Precision do begin
Expected := Expected * 10;
Actual := Actual * 10;
end;
if Round(Expected) <> Round(Actual) then
FailNotEquals( IntToStr(Round(Expected)), IntToStr(Round(Actual)), msg, CallerAddr);
end;
Sei que sua implementação ruim, mas apenas uma idéia, de que gosto mais do que "Delta" e muito mais fácil de usar.
Na versão atual da dunit, você pode usar
procedure CheckEquals(expected, actual: extended; delta: extended; msg: string = ''); overload; virtual;
CheckEquals(0.011,0.01,0.009,'will pass');