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;