Como arredondar o resultado da divisão inteira?


335

Estou pensando em particular em como exibir controles de paginação, ao usar uma linguagem como C # ou Java.

Se eu tiver x itens que quero exibir em pedaços de y por página, quantas páginas serão necessárias?


11
Estou esquecendo de algo? y / x + 1 funciona muito bem (desde que você saiba que o operador / sempre arredonda para baixo).
Rikkit 7/08

51
@rikkit - se y e x são iguais, y / x + 1 é muito alto.
196 Ian Ian

11
Para quem acabou de encontrar isso, esta resposta a uma pergunta idiota evita a conversão desnecessária para dobrar e evita preocupações de transbordamento, além de fornecer uma explicação clara.
ZX9

2
@IanNelson de maneira mais geral, se xfor divisível por y, y/x + 1seria muito alto.
Ohad Schneider

11
@ ZX9 Não, não evita preocupações com transbordamento. É exatamente a mesma solução que Ian Nelson postou aqui.
user247702

Respostas:


478

Encontrou uma solução elegante:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

Fonte: Conversão de números, Roland Backhouse, 2001


12
-1 por causa do erro de estouro apontado por Brandon Durette
finnw

30
Sr. Óbvio diz: Lembre-se de certificar-se de que recordsPerPage não é zero
Adam Gent

7
Bom trabalho, não acredito que o C # não tenha teto inteiro.
gosukiwi

2
Sim, aqui estou eu, em meados de 2017, tropeçando nessa ótima resposta depois de tentar várias abordagens muito mais complexas.
Mifo 29/07

11
Para linguagens com um operador adequado da divisão euclidiana, como Python, uma abordagem ainda mais simples seria pageCount = -((-records) // recordsPerPage).
Supercat

194

Converter em ponto flutuante e vice-versa parece uma enorme 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 estouro que Brandon DuRette apontou e, como o usa apenas uma vez, você não precisa armazenar o recordsPerPage especialmente se for de uma função cara para buscar o valor de um arquivo de configuração ou alguma coisa.

Ou seja, isso pode ser ineficiente, se config.fetch_value usou uma pesquisa no 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 é muita digitação:

int recordsPerPage = config.fetch_value('records per page')
int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

Isso é tudo em uma linha e somente busca os dados uma vez:

int pageCount = (records - 1) / config.fetch_value('records per page') + 1;

5
+1, a questão de zero registros ainda retornando 1 pageCount é realmente útil, pois eu ainda quero uma página, mostrando o espaço reservado / linha falsa de "nenhum registro corresponde aos seus critérios", ajuda a evitar problemas de "contagem de 0 páginas" controle de paginação que você usa.
Timothy Walters

27
Esteja ciente de que as duas soluções não retornam a mesma pageCount para zero registros. Esta versão simplificada retornará 1 pageCount para zero registros, enquanto a versão Roland Backhouse retorna 0 pageCount. Tudo bem se você deseja isso, mas as duas equações não são equivalentes quando executadas pela divisão inteira no estilo C # / Java.
6606 Ian Nelson #

10
edição minúscula para maior clareza para as pessoas que escaneiam e faltam bodmas ao mudar para a simplificação da solução Nelson (como fiz na primeira vez!), a simplificação com colchetes é ... int pageCount = ((records - 1) / recordsPerPage) + 1;
Dave heywood

11
Você deve adicionar parênteses à versão simplificada para que ela não dependa de uma ordem específica de operações. ou seja, ((records - 1) / recordsPerPage) + 1. #
Martin

11
@Ian, esta resposta nem sempre retorna 1. Ele pode retornar 0 se o seu recordsPerPage é "1" e há 0 registos: -1 / 1 + 1 = 0. Embora essa não seja uma ocorrência super comum, é importante ter em mente se você está permitindo que os usuários ajustem o tamanho da página. Portanto, não permita que os usuários tenham um tamanho de página igual a 1, verifique o tamanho da página ou ambos (provavelmente preferível para evitar comportamentos inesperados).
Michael

81

Para C #, a solução é converter os valores em um dobro (como Math.Ceiling recebe um dobro):

int nPages = (int)Math.Ceiling((double)nItems / (double)nItemsPerPage);

Em java, você deve fazer o mesmo com Math.ceil ().


4
por que essa resposta está tão distante quando o op pede explicitamente c #!
Felickz 01/10/12

2
Você também precisa converter a saída para, intporque Math.Ceilingretorna a doubleou decimal, dependendo dos tipos de entrada.
DanM7

14
porque é extremamente ineficiente
Zar Shardan

6
Pode ser ineficiente, mas é extremamente fácil de entender. Dado que o cálculo de uma contagem de páginas geralmente é feito uma vez por solicitação, qualquer perda de desempenho não seria mensurável.
Jared Kells

11
É pouco mais legível do que isso "(dividendo + (divisor - 1)) / divisor;" também é lento e requer a biblioteca de matemática.
rolos

68

Isso deve dar o que você deseja. Você definitivamente desejará x itens divididos por y itens por página, o problema é quando números desiguais aparecem, 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);

5
x / y + !! (x% y) evita a ramificação para idiomas do tipo C. As probabilidades são boas, no entanto, seu compilador está fazendo isso de qualquer maneira.
Rhys Ulerich

2
+1 por não estourar como as respostas acima ... embora converter ints para dobras apenas para Math.ceiling e depois voltar novamente é uma má ideia no código sensível ao desempenho.
Cogwheel

3
@RhysUlerich que não funciona em c # (não pode converter diretamente um int em um bool). A solução de rjmunro é a única maneira de evitar ramificações, eu acho.
Smead

18

A solução matemática inteira fornecida por Ian é boa, mas sofre de um erro de estouro inteiro. Supondo que as variáveis ​​sejam todas int, a solução pode ser reescrita para usar a longmatemática e evitar o erro:

int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;

Se recordsfor um long, o bug permanece. A solução do módulo não possui o erro.


4
Eu não acho que você realmente vá acertar esse bug no cenário apresentado. 2 ^ 31 registros é bastante para ser necessário folhear.
Rjmunro 05/02/09


@finnw: AFAICS, não há um exemplo do mundo real nessa página, apenas um relatório de alguém encontrando o bug em um cenário teórico.
Rjmunro

5
Sim, eu estava sendo pedante ao apontar o bug. Muitos bugs podem existir perpetuamente sem nunca causar problemas. Um bug da mesma forma existia na implementação do binarySearch pelo JDK por nove anos, antes que alguém o denunciasse ( googleresearch.blogspot.com/2006/06/… ). Acho que a pergunta é, independentemente de quão improvável você encontre esse bug, por que não corrigi-lo com antecedência?
Brandon Durette

4
Além disso, deve-se notar que não é apenas o número de elementos paginados que importa, mas também o tamanho da página. Portanto, se você estiver construindo uma biblioteca e alguém optar por não paginar passando 2 ^ 31-1 (Integer.MAX_VALUE) como o tamanho da página, o bug será acionado.
Brandon DuRette

7

Uma variante da resposta de Nick Berardi que evita um ramo:

int q = records / recordsPerPage, r = records % recordsPerPage;
int pageCount = q - (-r >> (Integer.SIZE - 1));

Nota: (-r >> (Integer.SIZE - 1))consiste no bit de sinal rrepetido 32 vezes (graças à extensão do sinal do >>operador.) Avalia como 0 se rfor zero ou negativo, -1 se rfor positivo. Portanto, subtraí-lo qtem o efeito de adicionar 1 se records % recordsPerPage > 0.


4

Para registros == 0, a solução de rjmunro fornece 1. A solução correta é 0. Dito isto, se você souber que registros> 0 (e tenho certeza de que todos assumimos recordsPerPage> 0), a solução rjmunro fornece resultados corretos e não possui nenhum dos problemas de estouro.

int pageCount = 0;
if (records > 0)
{
    pageCount = (((records - 1) / recordsPerPage) + 1);
}
// no else required

Todas as soluções matemáticas inteiras serão mais eficientes do que qualquer uma das soluções de ponto flutuante.


É improvável que esse método seja um gargalo de desempenho. E se for, você também deve considerar o custo da filial.
finnw

4

Na necessidade 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 (excesso, DivideByZeroetc), fique à vontade para adicionar se quiser. A propósito, para aqueles preocupados com a sobrecarga de chamada de método, funções simples como essa podem ser incorporadas pelo compilador de qualquer maneira, então não acho que seja esse o motivo. Felicidades.

PS: você pode achar útil também estar ciente disso (fica o restante):

    int remainder; 
    int result = Math.DivRem(dividend, divisor, out remainder);

11
Isto está incorreto. Por exemplo: DivideUp(4, -2)retorna 0 (deve ser -2). É correto apenas para números inteiros não negativos que não são claros na resposta ou na interface da função.
Thash

6
Thash, por que você não faz algo útil como adicionar a pequena verificação extra, se o número for negativo, em vez de votar minha resposta para baixo e fazer incorretamente a declaração geral: "Isso está incorreto", quando na verdade é apenas uma vantagem caso. Eu já deixei claro que você deve fazer outras verificações primeiro: "Nenhuma verificação aqui (estouro, DivideByZero, etc), fique à vontade para adicionar se quiser ."
Nicholas Petersen

11
A pergunta mencionada "Estou pensando em particular em como exibir controles de paginação " para que números negativos estivessem fora dos limites de qualquer maneira. Novamente, basta fazer algo útil e sugerir uma verificação adicional, se você quiser, é um homem de esforço em equipe.
Nicholas Petersen

Eu não quis ser rude e me desculpe se você entender dessa maneira. A questão era "Como arredondar o resultado da divisão inteira". O autor mencionou a paginação, mas outras pessoas podem ter necessidades diferentes. Eu acho que seria melhor se sua função refletisse de alguma forma que ela não funciona para números inteiros negativos, uma vez que não está claro na interface (por exemplo, um nome ou tipo de argumento diferente). Para trabalhar com números inteiros negativos, você pode, por exemplo, pegar um valor absoluto do dividendo e do divisor e multiplicar o resultado pelo seu sinal.
Thash

2

Outra alternativa é usar a função mod () (ou '%'). Se houver um restante diferente de zero, aumente o resultado inteiro da divisão.


1

Eu faço o seguinte, lida com qualquer excesso:

var totalPages = totalResults.IsDivisble(recordsperpage) ? totalResults/(recordsperpage) : totalResults/(recordsperpage) + 1;

E use esta extensão para se houver 0 resultado:

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;

0

Alternativa para remover ramificação no teste para zero:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage * (records != 0);

Não tenho certeza se isso funcionará em C #, deve funcionar em C / C ++.


-1

Um método genérico, cujo resultado você pode repetir 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;
}

A goiaba tem um método semelhante ( Lists.partition(List, int)) e, ironicamente, o size()método resultante List(a partir de r09) sofre do bug de estouro mencionado na resposta de Brandon DuRette .
finnw

-2

Eu tinha uma necessidade semelhante em que precisava converter Minutos em horas e minutos. O que eu 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);

-2

O seguinte deve fazer o arredondamento melhor do que as soluções acima, mas à custa do desempenho (devido ao cálculo do 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;
}

-4

Você deseja fazer a divisão de ponto flutuante e, em seguida, usar a função de teto, para arredondar o valor para o próximo número inteiro.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.