Alternativas para a função Round, inclusive arredondamento de valores não inteiros e de centenas, milhares, milhões, etc. Como poderia arredondar um número "normalmente"? Poderia arredondar as casas de milhares? Poderia arredondar o terceiro dígito depois da vírgula decimal?

Arredondamento de inteiro

A função Round que vem com Delphi executa o que é chamado "arredondamento de banqueiro", significando que um número com uma parte fracionária de valor 0,5, às vezes é arredondada “para cima” e às vezes “para abaixo”, sempre para o valor par mais próximo.

Isso quer dizer que, por exemplo, Round(3,5) resultará em um valor 4, enquanto que Round(2,5) resultará em um valor 2. A seguir, apresentamos um conjunto de funções para arredondamento de valores, inclusive a função RoundN, a qual arredonda um valor "normalmente" (i.e. RoundN(3,5) resulta em um valor 4 e RoundN(2,5) resulta em um valor 3).

 

function Sgn(X: Extended): Integer;

{ Retorna -1, 0 or 1 de acordo com o sinal do argumento }

begin
  if X < 0 then

    Result := -1

  else

    if X = 0 then

      Result := 0

    else
      Result := 1;

end;

function RoundUp(X: Extended): Extended;

{ Retorna o primeiro inteiro maior que ou igual a um

  dado número em valor absoluto (o sinal e preservado).

  RoundUp(3,3) = 4    RoundUp(-3,3) = -4 }

begin
  Result := Int(X) + Sgn(Frac(X));

end;

function RoundDn(X: Extended): Extended;

{ Retorna o primeiro inteiro menor que ou

  igual a um dado número em  valor absoluto (o sinal é preservado).

  RoundDn(3,7) = 3    RoundDn(-3,7) = -3

begin
  Result := Int(X);

end;

function RoundN(X: Extended): Extended;

{ Arredonda um número "normalmente": caso a parte de fração
  seja >= 0,5 o número será arredondado “para cima” (ver RoundUp)
  caso contrário, se a parte de fração for < 0,5, o
  número será arredondado “para baixo” (ver RoundDn).
  
RoundN(3,5) = 4     RoundN(-3,5) = -4

  RoundN(3,1) = 3     RoundN(-3,1) = -3 }

begin
  (*
  if Abs(Frac(X)) >= 0.5 then

    Result := RoundUp(X)

  else
    Result := RoundDn(X);

  *)
    Result := Int(X) + Int(Frac(X) * 2);

end;

function Fix(X: Extended): Extended;

{ Retorna o primeiro inteiro menor que ou

  igual a um dado número.

  Int(3,7) = 3          Int(-3,7) = -3

  Fix(3,7) = 3          Fix(-3,1) = -4 }

begin
  if (X >= 0) or (Frac(X) = 0) then

    Result := Int(X)

  else
    Result := Int(X) - 1;

end;

function RoundDnX(X: Extended): Extended;

{ Retorna o primeiro inteiro menor que ou

  igual a um dado número.

  RoundDnX(3,7) = 3     RoundDnX(-3,7) = -3

  RoundDnX(3,7) = 3     RoundDnX(-3,1) = -4 }

begin
  Result := Fix(X);

end;

function RoundUpX(X: Extended): Extended;

{ Retorna o primeiro inteiro maior que ou

  igual a um dado número.

  RoundUpX(3,1) = 4     RoundUpX(-3,7) = -3 }

begin
  Result := Fix(X) + Abs(Sgn(Frac(X)))

end;

function RoundX(X: Extended): Extended;

{ Arredonda o número "normalmente", porém levando em conta o sinal:
  se a parte de fração for >= 0,5, o número

  será arredondado “para cima” (ver RoundUpX)

  caso contrario, se a parte de fração for < 0,5,  

  o número será arredondado “para baixo” (ver RoundDnX).

  RoundX(3,5) = 4     RoundX(-3,5) = -3 }

begin
  (*
  if Abs(Frac(X)) >= 0,5 then

    Result := RoundUpX(X)

  else
    Result := RoundDnX(X);

  *)
    Result := Fix(X + 0.5);

end;

Arredondando para um dígito decimal

As funções acima apresentadas, sempre arredondam “para acima” até o último dígito inteiro, porém, às vezes precisamos, por exemplo, arredondar para a segunda casa decimal ou para a casa dos milhares, milhões ou bilhões. Podemos sobrecarregar a função RoundN para funcionar com essa versão, adicionando um parâmetro extra, para indicar o dígito a ser arredondado:

 

function RoundNExtend(x: Extended; d: Integer): Extended;

{ RoundN(123,456, 0) = 123,00

  RoundN(123,456, 2) = 123,46

  RoundN(123456, -3) = 123000 }

const
  t: array [0..12] of int64 = (1, 10, 100, 1000, 10000, 100000,
    1000000, 10000000, 100000000, 1000000000, 10000000000,
    100000000000, 1000000000000);

begin
  if Abs(d) > 12 then

    raise ERangeError.Create('RoundN: Value must be in -12..12');

  if d = 0 then

    Result := Int(x) + Int(Frac(x) * 2)

  else

    if d > 0 then

    begin

      x := x * t[d];

      Result := (Int(x) + Int(Frac(x) * 2)) / t[d];

    end

    else

    begin  // d < 0

      x := x / t[-d];

      Result := (Int(x) + Int(Frac(x) * 2)) * t[-d];

    end;
end;