quinta-feira, 8 de abril de 2010

Problemas Fracionários Delphi

Solucionando problemas fracionários

Observando uma longa discussão sobre este tema, resolvi apresentar minha experiência nesta área.
Eu tive muitos problemas parecidos com este há muito tempo, especialmente quando comecei a implantar sistemas para uso com ECF (emissor de cupom fiscal). Acontecia do programa apresentar um total de venda diferente do valor impresso pelo ECF. Então fui estudando a questão com carinho e cheguei a algumas conclusões que resolveram meus problemas:
1. O modo de arredondamento do FB é 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 FB. Para resolver as diferenças entre Delphi e FB 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;
2. 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.
3. 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.
4. Use no FB campos do tipo NUMERIC(x,y) para armazenar valores financeiros e quantidades. 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)
5. Crie campos calculados no banco (COMPUTED BY) já com os devidos ajustes de arredondamento. No FB existe alguns problemas de arredondamento também
que 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 FB 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 resolvi completamente os problemas que eu tinha com relação aos arredondamentos, tanto no Delphi quanto no IB/FB.

Por:
Daniel P. Guimarães
Tecnobyte Informática

Nenhum comentário: