Como arredondar o resultado da divisão inteira?
Pergunta
Estou pensando principalmente em como exibir controles de paginação ao usar uma linguagem como C# ou Java.
Se eu tiver x itens que desejo exibir em pedaços de sim por página, quantas páginas serão necessárias?
Solução
Encontrou uma solução elegante:
int pageCount = (records + recordsPerPage - 1) / recordsPerPage;
Outras dicas
Converter para ponto flutuante e vice-versa parece uma grande perda de tempo no nível da CPU.
A solução de Ian Nelson:
int pageCount = (records + recordsPerPage - 1) / recordsPerPage;
Pode ser simplificado para:
int pageCount = (records - 1) / recordsPerPage + 1;
AFAICS, isso não tem o bug de overflow apontado por Brandon DuRette, e como ele é usado apenas uma vez, você não precisa armazenar o recordsPerPage especialmente se ele vier de uma função cara para buscar o valor de um arquivo de configuração ou algo.
Ou sejaisso pode ser ineficiente, se config.fetch_value usasse uma pesquisa de banco de dados ou algo assim:
int pageCount = (records + config.fetch_value('records per page') - 1) / config.fetch_value('records per page');
Isso cria uma variável que você realmente não precisa, que provavelmente tem implicações (menores) de memória e é digitada demais:
int recordsPerPage = config.fetch_value('records per page')
int pageCount = (records + recordsPerPage - 1) / recordsPerPage;
Tudo isso é uma linha e só busca os dados uma vez:
int pageCount = (records - 1) / config.fetch_value('records per page') + 1;
Para C# a solução é converter os valores em double (já que Math.Ceiling leva double):
int nPages = (int)Math.Ceiling((double)nItems / (double)nItemsPerPage);
Em java você deve fazer o mesmo com Math.ceil().
Isso deve lhe dar o que você deseja.Definitivamente, você desejará x itens divididos por y itens por página. O problema é quando surgem números ímpares; portanto, se houver uma página parcial, também queremos adicionar uma página.
int x = number_of_items;
int y = items_per_page;
// with out library
int pages = x/y + (x % y > 0 ? 1 : 0)
// with library
int pages = (int)Math.Ceiling((double)x / (double)y);
A solução matemática de números inteiros fornecida por Ian é boa, mas sofre de um bug de estouro de números inteiros.Supondo que todas as variáveis sejam int
, a solução poderia ser reescrita para usar long
matemática e evite o bug:
int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;
Se records
é um long
, o bug permanece.A solução de módulo não apresenta o bug.
Uma variante de A resposta de Nick Berardi que evita um branch:
int q = records / recordsPerPage, r = records % recordsPerPage;
int pageCount = q - (-r >> (Integer.SIZE - 1));
Observação: (-r >> (Integer.SIZE - 1))
consiste no bit de sinal de r
, repetido 32 vezes (graças à extensão do sinal do >>
operador.) Isso é avaliado como 0 se r
é zero ou negativo, -1 se r
é positivo.Então, subtraindo isso de q
tem o efeito de adicionar 1 se records % recordsPerPage > 0
.
Para registros == 0, a solução de rjmunro dá 1.A solução correta é 0.Dito isto, se você sabe que records > 0 (e tenho certeza de que todos assumimos recordsPerPage > 0), então a solução rjmunro fornece resultados corretos e não apresenta nenhum problema de estouro.
int pageCount = 0;
if (records > 0)
{
pageCount = (((records - 1) / recordsPerPage) + 1);
}
// no else required
Todos as soluções matemáticas inteiras serão mais eficientes do que qualquer das soluções de ponto flutuante.
Precisando de um método de extensão:
public static int DivideUp(this int dividend, int divisor)
{
return (dividend + (divisor - 1)) / divisor;
}
Nenhuma verificação aqui (estouro, DivideByZero
, etc), fique à vontade para adicionar, se desejar.A propósito, para aqueles preocupados com a sobrecarga de invocação de métodos, funções simples como essa podem ser incorporadas pelo compilador de qualquer maneira, então não acho que seja esse o motivo.Saúde.
P.S.você também pode achar útil estar ciente disso (o restante fica):
int remainder;
int result = Math.DivRem(dividend, divisor, out remainder);
Outra alternativa é usar a função mod() (ou '%').Se houver um resto diferente de zero, aumente o resultado inteiro da divisão.
Eu faço o seguinte, lida com qualquer estouro:
var totalPages = totalResults.IsDivisble(recordsperpage) ? totalResults/(recordsperpage) : totalResults/(recordsperpage) + 1;
E use esta extensão se houver 0 resultados:
public static bool IsDivisble(this int x, int n)
{
return (x%n) == 0;
}
Além disso, para o número da página atual (não foi solicitado, mas pode ser útil):
var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1;
Alternativa para remover ramificações no teste de zero:
int pageCount = (records + recordsPerPage - 1) / recordsPerPage * (records != 0);
Não tenho certeza se isso funcionará em C#, deve funcionar em C/C++.
para C# use a função Math.Ceiling:
var pageCount= Math.Ceiling((double)myList.Count() / recordsPerPage);
e Java usam a função Math.Ceil:
int n = (int) Math.ceil((double)myList.size() / recordsPerPage));
Um método genérico, cujo resultado você pode iterar, pode ser interessante:
public static Object[][] chunk(Object[] src, int chunkSize) {
int overflow = src.length%chunkSize;
int numChunks = (src.length/chunkSize) + (overflow>0?1:0);
Object[][] dest = new Object[numChunks][];
for (int i=0; i<numChunks; i++) {
dest[i] = new Object[ (i<numChunks-1 || overflow==0) ? chunkSize : overflow ];
System.arraycopy(src, i*chunkSize, dest[i], 0, dest[i].length);
}
return dest;
}
Eu tive uma necessidade semelhante de converter minutos em horas e minutos.O que usei foi:
int hrs = 0; int mins = 0;
float tm = totalmins;
if ( tm > 60 ) ( hrs = (int) (tm / 60);
mins = (int) (tm - (hrs * 60));
System.out.println("Total time in Hours & Minutes = " + hrs + ":" + mins);
O seguinte deve fazer o arredondamento melhor do que as soluções acima, mas às custas do desempenho (devido ao cálculo de ponto flutuante de 0,5*rctDenominator):
uint64_t integerDivide( const uint64_t& rctNumerator, const uint64_t& rctDenominator )
{
// Ensure .5 upwards is rounded up (otherwise integer division just truncates - ie gives no remainder)
return (rctDenominator == 0) ? 0 : (rctNumerator + (int)(0.5*rctDenominator)) / rctDenominator;
}
Você desejará fazer a divisão de ponto flutuante e, em seguida, usar a função teto para arredondar o valor para o próximo número inteiro.