[firebase-br] Diferença fracionária - Soluções

Tecnobyte Informática temp2 em tecnobyte.com.br
Ter Maio 1 09:59:13 -03 2007


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.

Atenciosamente.

Daniel P. Guimarães
Tecnobyte Informática
www.tecnobyte.com.br





Mais detalhes sobre a lista de discussão lista