Executando verificação de segurança...
8

Arredondamento para baixo ou para cima, arredondamento matemático e truncamento

Vez ou outra precisa-se obter valores inteiros a partir de números reais provenientes de outras operações. Cálculo de datas, por exemplo, caso a linguagem não disponibilize funções dedicadas, o programador pode criar códigos específicos que implementam os respectivos algoritmos. Transformações geométricas envolvendo imagens e sistemas de coordenadas, conversões de formato etc. são apenas alguns dos diversos outros exemplos.

Em se tratanto desses operadores de arredondamento/truncamento, determinadas linguagens de programação já dispõem de funções para realizar esta extração a partir do argumento fornecido. Deve-se atentar ao funcionamento de cada um dos diferentes operadores disponibilizados pela linguagem. Na tabela seguinte estão alguns exemplos obtidos de [1]. São referentes à linguagem LISP. Surpreenda-se!

Argumento floor ceiling truncate round
>>===================================<<
   2.9      2       3       2       3
   2.5      2       3       2       2
   2.1      2       3       2       2
   0.9      0       1       0       1
   0.1      0       1       0       0

  -0.1     -1       0       0       0
  -0.9     -1       0       0      -1
  -2.1     -3      -2      -2      -2
  -2.5     -3      -2      -2      -2
  -2.9     -3      -2      -2      -3
>>===================================<<
Fonte: Adaptado de Manzano (2019, pg. 57)

Na tabela anterior nota-se claramente que o funcionamento da função (para a linguagem LISP) muda quando o argumento é positivo ou negativo, se está mais próximo da borda do inteiro ou acima/abaixo de n,5. Entender esses detalhes bem como tê-los em mente quando empregando tais operadores em seus códigos, evita-se algumas surpresas.

Cada linguagem de programação tem suas especificidades e as dúvidas podem ser resolvidas recorrendo ao manual oficial da linguagem ou implementando um pequeno código para avaliação.

Por incrível que pareça, no Brasil há uma norma que trata a respeito das Regras de arredondamento na numeração decimal: A NBR5891 de 12/2014.

Referências

[1] MANZANO, José Augusto Navarro Garcia. Linguagem LISP: primeiros passos com Common LISP (CL). São Paulo: Propes Vivens, 2019. 232 p. (978-85-923720-6-4). Disponível em: https://github.com/J-AugustoManzano/livro_LISP/blob/main/Livro/Livro_LISP_Free.pdf. Acesso em: 03 out. 2024.


Referência gerada com auxílio da plataforma MORE.

Carregando publicação patrocinada...
6

Complementando...

Com relação ao round, existem várias formas de arredondar em caso de "empate" (ou seja, quando a parte decimal é .5).

Entre as opções existentes, podemos listar as mais comuns:

  • arredondar para o maior (ou seja, 2.5 vira 3 e -2.5 vira -2), também chamado de "em direção ao infinito positivo" (towards infinity)
  • arredondar para o menor (ou seja, 2.5 vira 2 e -2.5 vira -3), também chamado de "em direção ao infinito positivo negativo" (towards negative infinity)
  • arredondar para o valor mais próximo de zero (2.5 vira 2 e -2.5 vira -2), também chamado de "towards zero"
  • arredondar para o valor mais distante de zero (2.5 vira 3 e -2.5 vira -3), também chamado de "away from zero"
  • arredondar para o número par mais próximo (2.5 vira 2 e 1.5 também vira 2) - essa é usada como o default em Python e C#
    • como curiosidade, esta regra também é conhecida como bankers rounding ou Banker's algorithm, tem mais detalhes sobre ela aqui.
  • arredondar para o número ímpar mais próximo (2.5 vira 3 e 1.5 vira 1)

A tabela abaixo tem um resumo disso:

ValorTowards +∞Towards -∞ParÍmparTowards zeroAway from zero
1.5212112
2.5322323
-1.5-1-2-2-1-1-2
-2.5-2-3-2-3-2-3

Muitas linguagens, inclusive, possuem formas de escolher qual opção queremos ao arredondar. Claro que nem todas as linguagens possuem todas as opções, e outras podem ter mais formas ainda. Python, por exemplo, possui a opção ROUND_05UP: se o último dígito depois de arredondar em direção ao zero for 0 ou 5, usa o critério away from zero , senão usa towards zero ("Round away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise round towards zero").


Quanto ao fato de floor e ceiling darem resultados "inesperados" com números negativos, na verdade faz sentido se levarmos em conta a definição. floor arredonda "para baixo", ou seja, o resultado é um número inteiro menor ou igual ao valor original. Por isso que floor(-2.1) é -3 e não -2. De forma similar, ceiling(-2.9) é um número inteiro que é maior ou igual ao valor original, por isso é -2 e não -3.

Estas formas de arredondamento também costumam ser descritas como "towards negative infinity" (floor) e "towards positivy infinity" (ceil), ou seja, o número é arredondado em determinada direção na Reta Numérica:

                     floor(-1.2)   ceil(-1.2)    floor(1.2)  ceil(1.2)
negative infinity <---- -2 --------- -1 ----- 0 ---- 1 -------- 2 ----> positive infinity
                              -1.2                      1.2

No caso, floor arredonda em direção ao infinito negativo: a partir do número, começa-se a "caminhar" na direção do infinito negativo, e o resultado é o primeiro número inteiro que for encontrado neste caminho. ceil faz o mesmo em direção ao infinito positivo.

Vale notar que no caso de floor e ceil, o critério é usado sempre para qualquer número, ao contrário do round, que usa somente em caso de empate (pode usar algum dos já citados acima como o default, ou qualquer outro indicado via algum parâmetro). Ou seja, floor(2.9) e floor(2.5) resultam em 2, ceil(2.9) e ceil(2.5) resultam em 3, e round(2.9) resulta em 3. Mas round(2.5) pode ser 2 ou 3 dependendo do critério de desempate usado.

Valorfloorceilround
2.1232
2.5232 ou 3 (depende do critério de desempate)
2.9233

Por fim, já escrevi um post bem detalhado sobre isso (do qual retirei/adaptei os trechos acima), serve como um complemento :-)

3

kht, antes de publicar aqui no Tabnews este post, fiz uma busca para ver se já não havia algo repetido aqui. Pena não ter encontrado sua postagem que ficou muito rica em detalhes que eu não conhecia a respeito destas potencialidades da linguagem a que se referiu. Inclusive, sua postagem original lá no Stackoverflow está bem caprichada e vale a pena conferir quem chegou a ler até aqui. Valeu por sua complementação, digna de troca de lugar com a que fiz :)


20241003T2311Z - Pareceu-me que sua habilidade com essas funções se dá pela sua experiência com manipulação de datas em diferentes linguagens de programação. Realmente, uma das funções que usei uma vez confia na operação com inteiros da máquina, como é o caso do cálculo de data no calendário gregoriano para dias juliano e vice-versa.

20241004T2151Z - Fiz um complemento usando algumas funções da linguagem C de que me lembrei. Um artíficio que emprega o recurso de casting de tipo (para float e para double) também é apresentado na coluna cast.

 --------------- float ------------- | -------------- double --------------
 value  cast trunc round floor  ceil |  value  cast trunc round floor  ceil
  3.00  3.00  3.00  3.00  3.00  3.00 |   3.00  3.00  3.00  3.00  3.00  3.00 *
  2.90  2.00  2.00  3.00  2.00  3.00 |   2.90  2.00  2.00  3.00  2.00  3.00
  2.60  2.00  2.00  3.00  2.00  3.00 |   2.60  2.00  2.00  3.00  2.00  3.00
  2.50  2.00  2.00  3.00  2.00  3.00 |   2.50  2.00  2.00  2.00  2.00  3.00
  2.40  2.00  2.00  2.00  2.00  3.00 |   2.40  2.00  2.00  2.00  2.00  3.00
  2.10  2.00  2.00  2.00  2.00  3.00 |   2.10  2.00  2.00  2.00  2.00  3.00
  
  2.00  2.00  2.00  2.00  2.00  3.00 |   2.00  1.00  1.00  2.00  1.00  2.00
  1.90  1.00  1.00  2.00  1.00  2.00 |   1.90  1.00  1.00  2.00  1.00  2.00
  1.60  1.00  1.00  2.00  1.00  2.00 |   1.60  1.00  1.00  2.00  1.00  2.00
  1.50  1.00  1.00  2.00  1.00  2.00 |   1.50  1.00  1.00  1.00  1.00  2.00
  1.20  1.00  1.00  1.00  1.00  2.00 |   1.20  1.00  1.00  1.00  1.00  2.00
  1.10  1.00  1.00  1.00  1.00  2.00 |   1.10  1.00  1.00  1.00  1.00  2.00
  
  1.00  1.00  1.00  1.00  1.00  2.00 |   1.00  0.00  0.00  1.00  0.00  1.00
  0.90  0.00  0.00  1.00  0.00  1.00 |   0.90  0.00  0.00  1.00  0.00  1.00
  0.60  0.00  0.00  1.00  0.00  1.00 |   0.60  0.00  0.00  1.00  0.00  1.00
  0.50  0.00  0.00  1.00  0.00  1.00 |   0.50  0.00  0.00  0.00  0.00  1.00
  0.40  0.00  0.00  0.00  0.00  1.00 |   0.40  0.00  0.00  0.00  0.00  1.00
  0.10  0.00  0.00  0.00  0.00  1.00 |   0.10  0.00  0.00  0.00  0.00  1.00
  
 --------------- float ------------- | -------------- double --------------
 value  cast trunc round floor  ceil |  value  cast trunc round floor  ceil
  0.00  0.00  0.00  0.00  0.00  1.00 |  -0.00 -0.00  0.00 -0.00 -1.00 -0.00 *
 -0.10 -0.00  0.00 -0.00 -1.00 -0.00 |  -0.10 -0.00  0.00 -0.00 -1.00 -0.00
 -0.40 -0.00  0.00 -0.00 -1.00 -0.00 |  -0.40 -0.00  0.00 -0.00 -1.00 -0.00
 -0.50 -0.00  0.00 -0.00 -1.00 -0.00 |  -0.50 -0.00  0.00 -1.00 -1.00 -0.00
 -0.60 -0.00  0.00 -1.00 -1.00 -0.00 |  -0.60 -0.00  0.00 -1.00 -1.00 -0.00
 -0.90 -0.00  0.00 -1.00 -1.00 -0.00 |  -0.90 -0.00  0.00 -1.00 -1.00 -0.00
 
 -1.00 -0.00  0.00 -1.00 -1.00 -0.00 |  -1.00 -1.00 -1.00 -1.00 -2.00 -1.00
 -1.10 -1.00 -1.00 -1.00 -2.00 -1.00 |  -1.10 -1.00 -1.00 -1.00 -2.00 -1.00
 -1.40 -1.00 -1.00 -1.00 -2.00 -1.00 |  -1.40 -1.00 -1.00 -1.00 -2.00 -1.00
 -1.50 -1.00 -1.00 -1.00 -2.00 -1.00 |  -1.50 -1.00 -1.00 -2.00 -2.00 -1.00
 -1.60 -1.00 -1.00 -2.00 -2.00 -1.00 |  -1.60 -1.00 -1.00 -2.00 -2.00 -1.00
 -1.90 -1.00 -1.00 -2.00 -2.00 -1.00 |  -1.90 -1.00 -1.00 -2.00 -2.00 -1.00
 
 -2.00 -1.00 -1.00 -2.00 -2.00 -1.00 |  -2.00 -2.00 -2.00 -2.00 -3.00 -2.00
 -2.10 -2.00 -2.00 -2.00 -3.00 -2.00 |  -2.10 -2.00 -2.00 -2.00 -3.00 -2.00
 -2.40 -2.00 -2.00 -2.00 -3.00 -2.00 |  -2.40 -2.00 -2.00 -2.00 -3.00 -2.00
 -2.50 -2.00 -2.00 -2.00 -3.00 -2.00 |  -2.50 -2.00 -2.00 -3.00 -3.00 -2.00
 -2.60 -2.00 -2.00 -3.00 -3.00 -2.00 |  -2.60 -2.00 -2.00 -3.00 -3.00 -2.00
 -2.90 -2.00 -2.00 -3.00 -3.00 -2.00 |  -2.90 -2.00 -2.00 -3.00 -3.00 -2.00
 
 -3.00 -2.00 -2.00 -3.00 -3.00 -2.00 |  -3.00 -3.00 -3.00 -3.00 -4.00 -3.00
 -3.10 -3.00 -3.00 -3.00 -4.00 -3.00 |  -3.10 -3.00 -3.00 -3.00 -4.00 -3.00

A tabela acima tem algumas particularidades indicadas devido o incremento utilizado ser 1/10, um valor não representável exatamente em IEEE754. A tabela completa pode ser calculada, extendida, modificada com o código C abaixo.

/* source code starts here... */
/* Compile with  gcc -Wall -Wextra round2.c -lm                               */
#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[]) {

  float  f_value, f_truncated, f_rounded, f_floored, f_ceiled, f_casted;
  double d_value, d_truncated, d_rounded, d_floored, d_ceiled, d_casted;
  f_value = 3.0;
  d_value = 3.0;
  // print a header
  printf(" --------------- float ------------- | ");
  printf("-------------- double --------------\n" );
  printf(" value  cast trunc round floor  ceil | ");
  printf(" value  cast trunc round floor  ceil\n" );

  for(int i=0; i<58; i++) {
    f_truncated = trunc(f_value);
    f_rounded   = round(f_value);
    f_floored   = floor(f_value);
    f_ceiled    = ceil (f_value);
    f_casted    = (float)((int)(f_value));

    d_truncated = trunc(d_value);
    d_rounded   = round(d_value);
    d_floored   = floor(d_value);
    d_ceiled    = ceil (d_value);
    d_casted    = (double)((int)(d_value));

    printf("%6.2f%6.2f%6.2f%6.2f%6.2f%6.2f | %6.2f%6.2f%6.2f%6.2f%6.2f%6.2f\n",
                f_value, f_truncated, f_casted, f_rounded, f_floored, f_ceiled,
               d_value, d_truncated, d_casted, d_rounded, d_floored, d_ceiled);
    f_value += -0.1;
    d_value += -0.1;
  }

  return 0;
}
/* source code ends here. */

Fazendo uma pequena modificação no código e adotando um incremento (1/8) "representável" em base 2, a tabela fica livre de casos anormais.

 ------------------ float ---------------- | ----------------- double -----------------
  value   cast  trunc  round  floor   ceil |   value   cast  trunc  round  floor   ceil
  3.500  3.000  3.000  4.000  3.000  4.000 |   3.500  3.000  3.000  4.000  3.000  4.000
  3.375  3.000  3.000  3.000  3.000  4.000 |   3.375  3.000  3.000  3.000  3.000  4.000
  3.250  3.000  3.000  3.000  3.000  4.000 |   3.250  3.000  3.000  3.000  3.000  4.000
  3.125  3.000  3.000  3.000  3.000  4.000 |   3.125  3.000  3.000  3.000  3.000  4.000

  3.000  3.000  3.000  3.000  3.000  3.000 |   3.000  3.000  3.000  3.000  3.000  3.000
  2.875  2.000  2.000  3.000  2.000  3.000 |   2.875  2.000  2.000  3.000  2.000  3.000
  2.750  2.000  2.000  3.000  2.000  3.000 |   2.750  2.000  2.000  3.000  2.000  3.000
  2.625  2.000  2.000  3.000  2.000  3.000 |   2.625  2.000  2.000  3.000  2.000  3.000
  2.500  2.000  2.000  3.000  2.000  3.000 |   2.500  2.000  2.000  3.000  2.000  3.000
  2.375  2.000  2.000  2.000  2.000  3.000 |   2.375  2.000  2.000  2.000  2.000  3.000
  2.250  2.000  2.000  2.000  2.000  3.000 |   2.250  2.000  2.000  2.000  2.000  3.000
  2.125  2.000  2.000  2.000  2.000  3.000 |   2.125  2.000  2.000  2.000  2.000  3.000
  
  2.000  2.000  2.000  2.000  2.000  2.000 |   2.000  2.000  2.000  2.000  2.000  2.000
  1.875  1.000  1.000  2.000  1.000  2.000 |   1.875  1.000  1.000  2.000  1.000  2.000
  1.750  1.000  1.000  2.000  1.000  2.000 |   1.750  1.000  1.000  2.000  1.000  2.000
  1.625  1.000  1.000  2.000  1.000  2.000 |   1.625  1.000  1.000  2.000  1.000  2.000
  1.500  1.000  1.000  2.000  1.000  2.000 |   1.500  1.000  1.000  2.000  1.000  2.000
  1.375  1.000  1.000  1.000  1.000  2.000 |   1.375  1.000  1.000  1.000  1.000  2.000
  1.250  1.000  1.000  1.000  1.000  2.000 |   1.250  1.000  1.000  1.000  1.000  2.000
  1.125  1.000  1.000  1.000  1.000  2.000 |   1.125  1.000  1.000  1.000  1.000  2.000
  
  1.000  1.000  1.000  1.000  1.000  1.000 |   1.000  1.000  1.000  1.000  1.000  1.000
  0.875  0.000  0.000  1.000  0.000  1.000 |   0.875  0.000  0.000  1.000  0.000  1.000
  0.750  0.000  0.000  1.000  0.000  1.000 |   0.750  0.000  0.000  1.000  0.000  1.000
  0.625  0.000  0.000  1.000  0.000  1.000 |   0.625  0.000  0.000  1.000  0.000  1.000
  0.500  0.000  0.000  1.000  0.000  1.000 |   0.500  0.000  0.000  1.000  0.000  1.000
  0.375  0.000  0.000  0.000  0.000  1.000 |   0.375  0.000  0.000  0.000  0.000  1.000
  0.250  0.000  0.000  0.000  0.000  1.000 |   0.250  0.000  0.000  0.000  0.000  1.000
  0.125  0.000  0.000  0.000  0.000  1.000 |   0.125  0.000  0.000  0.000  0.000  1.000
  
  0.000  0.000  0.000  0.000  0.000  0.000 |   0.000  0.000  0.000  0.000  0.000  0.000
 -0.125 -0.000  0.000 -0.000 -1.000 -0.000 |  -0.125 -0.000  0.000 -0.000 -1.000 -0.000
 -0.250 -0.000  0.000 -0.000 -1.000 -0.000 |  -0.250 -0.000  0.000 -0.000 -1.000 -0.000
 -0.375 -0.000  0.000 -0.000 -1.000 -0.000 |  -0.375 -0.000  0.000 -0.000 -1.000 -0.000
 -0.500 -0.000  0.000 -1.000 -1.000 -0.000 |  -0.500 -0.000  0.000 -1.000 -1.000 -0.000
 -0.625 -0.000  0.000 -1.000 -1.000 -0.000 |  -0.625 -0.000  0.000 -1.000 -1.000 -0.000
 -0.750 -0.000  0.000 -1.000 -1.000 -0.000 |  -0.750 -0.000  0.000 -1.000 -1.000 -0.000
 -0.875 -0.000  0.000 -1.000 -1.000 -0.000 |  -0.875 -0.000  0.000 -1.000 -1.000 -0.000
 
 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 |  -1.000 -1.000 -1.000 -1.000 -1.000 -1.000
 -1.125 -1.000 -1.000 -1.000 -2.000 -1.000 |  -1.125 -1.000 -1.000 -1.000 -2.000 -1.000
 -1.250 -1.000 -1.000 -1.000 -2.000 -1.000 |  -1.250 -1.000 -1.000 -1.000 -2.000 -1.000
 -1.375 -1.000 -1.000 -1.000 -2.000 -1.000 |  -1.375 -1.000 -1.000 -1.000 -2.000 -1.000
 -1.500 -1.000 -1.000 -2.000 -2.000 -1.000 |  -1.500 -1.000 -1.000 -2.000 -2.000 -1.000
 -1.625 -1.000 -1.000 -2.000 -2.000 -1.000 |  -1.625 -1.000 -1.000 -2.000 -2.000 -1.000
 -1.750 -1.000 -1.000 -2.000 -2.000 -1.000 |  -1.750 -1.000 -1.000 -2.000 -2.000 -1.000
 -1.875 -1.000 -1.000 -2.000 -2.000 -1.000 |  -1.875 -1.000 -1.000 -2.000 -2.000 -1.000
 
 -2.000 -2.000 -2.000 -2.000 -2.000 -2.000 |  -2.000 -2.000 -2.000 -2.000 -2.000 -2.000
 -2.125 -2.000 -2.000 -2.000 -3.000 -2.000 |  -2.125 -2.000 -2.000 -2.000 -3.000 -2.000
 -2.250 -2.000 -2.000 -2.000 -3.000 -2.000 |  -2.250 -2.000 -2.000 -2.000 -3.000 -2.000
 -2.375 -2.000 -2.000 -2.000 -3.000 -2.000 |  -2.375 -2.000 -2.000 -2.000 -3.000 -2.000
 -2.500 -2.000 -2.000 -3.000 -3.000 -2.000 |  -2.500 -2.000 -2.000 -3.000 -3.000 -2.000
 -2.625 -2.000 -2.000 -3.000 -3.000 -2.000 |  -2.625 -2.000 -2.000 -3.000 -3.000 -2.000
 -2.750 -2.000 -2.000 -3.000 -3.000 -2.000 |  -2.750 -2.000 -2.000 -3.000 -3.000 -2.000
 -2.875 -2.000 -2.000 -3.000 -3.000 -2.000 |  -2.875 -2.000 -2.000 -3.000 -3.000 -2.000
 
 -3.000 -3.000 -3.000 -3.000 -3.000 -3.000 |  -3.000 -3.000 -3.000 -3.000 -3.000 -3.000
 -3.125 -3.000 -3.000 -3.000 -4.000 -3.000 |  -3.125 -3.000 -3.000 -3.000 -4.000 -3.000
 -3.250 -3.000 -3.000 -3.000 -4.000 -3.000 |  -3.250 -3.000 -3.000 -3.000 -4.000 -3.000
 -3.375 -3.000 -3.000 -3.000 -4.000 -3.000 |  -3.375 -3.000 -3.000 -3.000 -4.000 -3.000
 -3.500 -3.000 -3.000 -4.000 -4.000 -3.000 |  -3.500 -3.000 -3.000 -4.000 -4.000 -3.000
 -3.625 -3.000 -3.000 -4.000 -4.000 -3.000 |  -3.625 -3.000 -3.000 -4.000 -4.000 -3.000

É importante lembrar-se de que o arredondamento segundo a NBR5891 de 12/2014 não foi considerado nos exemplos.