2008-08-20 32 views
262

Estoy pensando en particular en cómo mostrar los controles de paginación cuando utilizo un lenguaje como C# o Java.¿Cómo redondear el resultado de la división de enteros?

Si tengo x artículos que quiero mostrar en trozos de y por página, el número de páginas se necesitarán?

+0

¿Emite más? y/x + 1 funciona muy bien (siempre que sepa que el operador/siempre se redondea hacia abajo). – rikkit

+39

@rikkit - si yy son iguales, y/x + 1 es uno demasiado alto. –

+0

Para cualquiera que lo encuentre ahora, [esta respuesta a una pregunta de dupe] (http://stackoverflow.com/a/4846569/4163002) evita la conversión innecesaria a double * y * evita preocupaciones de desbordamiento además de proporcionar una explicación clara. – ZX9

Respuesta

392

encontrado una solución elegante:

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

Fuente: Number Conversion, Roland Backhouse, 2001

+67

@RishiD - llamamos a eso "memorizar" :-) –

+10

-1 debido al error de desbordamiento señalado por [Brandon DuRette] (http://stackoverflow.com/questions/17944/how-to-round-up-the -result-of-integer-division/96921 # 96921) – finnw

+21

Mr Obvious dice: Recuerde asegurarse de que recordsPerPage no sea cero –

-5

Querrá hacer la división de coma flotante, y luego usar la función de techo, para redondear el valor al siguiente entero.

2

Otra alternativa es usar la función mod() (o '%'). Si hay un resto distinto de cero, entonces incremente el resultado entero de la división.

53

Esto debería darle lo que desea. Definitivamente querrá x elementos divididos por elementos y por página, el problema es cuando aparecen números desiguales, por lo que si hay una página parcial también queremos agregar una 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); 
+4

x/y + !! (x% y) evita la rama para los idiomas similares a C. Las probabilidades son buenas, sin embargo, su compilador está haciendo eso de todos modos. –

+1

+1 para no desbordar como las respuestas anteriores ... aunque convertir ints a dobles solo para Math.ceiling y luego de nuevo es una mala idea en el código sensible al rendimiento. – Cogwheel

+2

@RhysUlerich que no funciona en C# (no puede convertir directamente un int a bool). La solución de rjmunro es la única manera de evitar la bifurcación, creo. – smead

50

para C# es la solución a emitir los valores a un doble (como Math.Ceiling lleva una doble):

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

en Java que debe hacer lo mismo con Math.ceil().

+2

¡por qué esta respuesta está tan abajo cuando el operador solicita C# explícitamente! – felickz

+2

También necesita convertir el resultado a 'int' porque' Math.Ceiling' devuelve 'double' o' decimal', dependiendo de los tipos de entrada. – DanM7

+11

porque es extremadamente ineficiente –

15

La solución matemáticas entero que Ian proporcionado es agradable, pero sufre de un error de desbordamiento de enteros. Suponiendo que las variables son todas int, la solución podría ser reescrito para usar long matemáticas y evitar el error:

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

Si records es una long, el error se mantiene. La solución del módulo no tiene el error.

+3

No creo que vaya a golpear este error en el escenario presentado. 2^31 registros es bastante tener que pasar por la página. – rjmunro

+4

@rjmunro, [ejemplo del mundo real aquí] (http://code.google.com/p/guava-libraries/issues/detail?id = 616) – finnw

+0

@finnw: AFAICS, no hay un ejemplo del mundo real en esa página, solo un informe de alguien que encuentra el error en un escenario teórico. – rjmunro

157

La conversión a punto flotante y viceversa parece una enorme pérdida de tiempo en el nivel de la CPU.

solución de Ian Nelson:

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

se puede simplificar a:

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

AFAICS, esto no tiene el error de desbordamiento que Brandon DURETTE señaló, y debido a que sólo se utiliza una vez , no es necesario almacenar RecordPerPage especialmente si proviene de una función costosa para recuperar el valor de un archivo de configuración o algo así.

I.e. esto podría ser ineficiente, si config.fetch_value utiliza una búsqueda de base de datos o algo:

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

Esto crea una variable que realmente no necesita, lo que probablemente tiene implicaciones (menores) de memoria y es simplemente demasiado escribiendo:

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

Este es toda una línea, y sólo recoge los datos una vez:

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

+1, el problema de cero registros todavía devuelve 1 pageCount es realmente útil, ya que aún quisiera 1 página, mostrando el marcador de posición/fila falsa de "ningún registro coincide con sus criterios", ayuda a evitar cualquier problema de "0 páginas contadas" sea ​​cual sea el control de paginación que uses –

+19

Tenga en cuenta que las dos soluciones no devuelven el mismo pageCount para cero registros. Esta versión simplificada devolverá 1 pageCount para cero registros, mientras que la versión de Roland Backhouse devuelve 0 pageCount. Está bien si eso es lo que deseas, pero las dos ecuaciones no son equivalentes cuando se realiza por C#/división entera de estilo de Java. –

+7

edición pequeña para mayor claridad para las personas que lo escanean y bodmas que faltan cuando cambian a la simplificación de la solución Nelson (como hice la primera vez!), La simplificación con paréntesis es ... int pageCount = ((records - 1)/recordsPerPage) + 1; –

4

Para los registros == 0, la solución de rjmunro da 1. La solución correcta es 0. dicho esto, si usted sabe que los registros> 0 (y estoy Seguro que tenemos al l asumió recordsPerPage> 0), luego la solución rjmunro da resultados correctos y no tiene ninguno de los problemas de desbordamiento.

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

Todos soluciones matemáticas del número entero van a ser más eficiente que cualquier de las soluciones de punto flotante.

+0

Es poco probable que este método sea un cuello de botella de rendimiento. Y si lo es, también debe considerar el costo de la sucursal. – finnw

0

alternativa para eliminar la ramificación en las pruebas de cero:

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

No estoy seguro si esto va a funcionar en C#, debe hacerlo en C/C++.

-1

Un método genérico, cuyo resultado se puede iterar sobre pueda ser de su interés:

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; 
} 
+0

[Guava] (http://code.google.com/p/guava-libraries/) tiene un método similar (['Lists.partition (List, int)'] (http: //guava-libraries.googlecode. com/svn/tags/release09/javadoc/com/google/common/collect/Lists.html # partition% 28java.util.List,% 20int% 29)) e irónicamente el método 'size()' de la 'Lista resultante '(a partir de' r09') sufre del error de desbordamiento mencionado en [la respuesta de Brandon DuRette] (http://stackoverflow.com/questions/17944/how-to-round-up-the-result-of-integer-division/96921 # 96921). – finnw

6

Una variante de Nick Berardi's answer que evite una rama:

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

Nota: (-r >> (Integer.SIZE - 1)) consiste en el bit de signo de r, repetido 32 veces (gracias a la extensión de signo del operador >>.) Esto se evalúa a 0 si r es cero o negativo, -1 si r es positivo mi. Así que restarlo de q tiene el efecto de agregar 1 si records % recordsPerPage > 0.

-2

Tenía una necesidad similar donde necesitaba convertir Minutos a horas & minutos. Lo que utilicé fue:

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

Lo siguiente debe hacer redondeo mejor que las soluciones anteriores, pero a expensas de rendimiento (debido al cálculo de coma flotante 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; 
} 
1

hago lo siguiente, se ocupa de cualquier desbordamientos:

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

y utilizar esta extensión de si hay 0 resultados:

public static bool IsDivisble(this int x, int n) 
{ 
      return (x%n) == 0; 
} 

Además, para la corriente número de página (no se le preguntó, pero podría serle útil):

var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1; 
3

En la necesidad de un método de extensión:

public static int DivideUp(this int dividend, int divisor) 
    { 
     return (dividend + (divisor - 1))/divisor; 
    } 

Sin verificación de aquí (desbordamiento, DivideByZero, etc), no dude en añadir si lo desea. Por cierto, para aquellos preocupados por la sobrecarga de invocación de métodos, funciones sencillas como esta podrían ser delineadas por el compilador de todos modos, así que no creo que sea por eso. Aclamaciones.

P.S. es posible que también le resulte útil tener esto en cuenta (obtiene el resto):

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

Esto es incorrecto. Por ejemplo: 'DivideUp (4, -2)' devuelve 0 (debe ser -2). ** Solo es correcto para enteros no negativos ** que no está claro en la respuesta o en la interfaz de la función. – Thash

+2

Thash, ¿por qué no haces algo útil como agregar el pequeño cheque extra luego si el número es negativo, en lugar de votar mi respuesta, e incorrectamente hacer la declaración general: "Esto es incorrecto", cuando en realidad es solo un caso extremo. Ya dejé claro que primero debe hacer otros controles: "No hay cheques aquí (desbordamiento, DivideByZero, etc.), ** siéntase libre de agregarlos si lo desea **." –

+0

La pregunta mencionó "Estoy pensando en particular en cómo mostrar ** controles de paginación **", por lo que los números negativos se habrían salido de todos modos. De nuevo, solo haga algo útil y sugiera un control adicional si lo desea, es un esfuerzo de equipo. –

Cuestiones relacionadas