Solucionando problemas de arredondamentos no Delphi

O modo de arredondamento do Firebird é igual ao modo das calculadoras financeiras. Já no Delphi o arredondamento leva em consideração se a parte inteira é par ou ímpar quando a parte decimal termina em 5. Os ECFs também calculam como no Firebird. Para resolver as diferenças entre Delphi e Firebird escrevi a função abaixo (a conversão para string resolve alguns problemas de arredondamento do Delphi):


function ExRound(Value: Extended; Decimals: Integer): Extended;
var
  Factor, Fraction: Extended;
begin
  Factor := IntPower(10, Decimals);
  Value := StrToFloat(FloatToStr(Value * Factor));
  Result := Int(Value);
  Fraction := Frac(Value);
  if Fraction >= 0.5 then
    Result := Result + 1
  else if Fraction <= -0.5 then
    Result := Result - 1;
  Result := Result / Factor;
end;

Algumas vezes o Delphi gera uns problemas de arredondamento quando você faz vários cálculos (especialmente multiplicação e divisão) sem aplicar o arredondamento em cada etapa do cálculo. Isto ocorre devido ao modo que o Delphi armazena um valor de ponto flutuante numa variável. Procure fazer o arredondamento a cada etapa do cálculo, exceto se este arredondamento passo-a-passo for prejudicar o resultado do cálculo.

Sempre que possível use o tipo Currency para variáveis que receberão números reais com até 4 casas decimais. Lembre-se também de usar AsCurrency ao acessar valores de campos de DataSets.

Use no Firebird campos do tipo NUMERIC(x,y) para armazenar valores financeiros e quantidades, mas tome o cuidado de criar o banco de dados com o dialeto 3, pois no dialeto 1 o tipo NUMERIC poderá ser convertido para DOUBLE PRECISION internamente.

Exemplos:
Quantidade NUMERIC(9,3)
Quantidade NUMERIC(18,3)
Preco      NUMERIC(9,2)
Preco      NUMERIC(18,2)
Preco      NUMERIC(18,4)
Desconto   NUMERIC(4,2)

Crie campos calculados no banco (COMPUTED BY) já com os devidos ajustes de arredondamento. No Firebird existem alguns problemas de arredondamento também, mas geralmente se resolve com CASTs. Veja alguns exemplos:

Itens de venda:
ValorDescto   NUMERIC(9,2) COMPUTED(CAST(Qtd * PrecoVenda * Descto / 100 AS NUMERIC(9,2))),
Total         NUMERIC(9,2) COMPUTED(CAST(Qtd * PrecoVenda - ValorDescto AS NUMERIC(9,2))),
ValorComissao NUMERIC(9,2) COMPUTED(CAST(Total * Comissao / 100 AS NUMERIC(9,2)))
Contas a receber/recebidas:
Atraso INTEGER COMPUTED(
  CASE
    WHEN Recda = 'N' AND Vencto < CURRENT_DATE THEN
     CURRENT_DATE - Vencto
    WHEN Recda = 'S' AND Vencto < DataRecto THEN
     DataRecto - Vencto
   ELSE
    0
  END), 
ValorJuro  NUMERIC(9,2) COMPUTED(CAST(Valor * Juro * Atraso / 100 / 30 AS NUMERIC(9,2))), 
Total      NUMERIC(9,2) COMPUTED(CAST(Valor + ValorJuro AS NUMERIC(9,2))), 
TotalRecdo NUMERIC(9,2) COMPUTED(CAST(CapitalRecdo + JuroRecdo AS NUMERIC(9,2)))

Enfim, use CASTs no Firebird sempre que fizer cálculos envolvendo multiplicação e divisão para que o resultado tenha de fato as casas decimais desejadas.

Com estas técnicas acima resolve completamente os problemas em relação aos arredondamentos, tanto no Delphi quanto no Interbase ou Firebird.