Ronda con división entera

¿Existe una forma simple y pythonica de redondear al número entero más cercano sin utilizar un punto flotante? Me gustaría hacer lo siguiente pero con aritmética de enteros:

skip = int(round(1.0 * total / surplus)) 

==============

@John: El punto flotante no se puede reproducir en las plataformas. Si desea que su código pase las pruebas a través de diferentes plataformas, debe evitar el punto flotante (o agregar algunas cosas espelonadas a sus pruebas y esperar que funcione). Lo anterior puede ser lo suficientemente simple como para ser el mismo en la mayoría de las plataformas, pero prefiero no tomar esa determinación ya que es más fácil evitar el punto flotante por completo. ¿Cómo es eso “no en el espíritu de Python“?

Usted puede hacer esto de manera bastante simple:

(n + d // 2) // d , donde n es el dividendo y d es el divisor.

Alternativas como (((n << 1) // d) + 1) >> 1 o su equivalente (((n * 2) // d) + 1) // 2 pueden ser POTENTES en los CPythons recientes, donde un int Se implementa como el antiguo long .

El método simple realiza 3 accesos variables, 1 carga constante y 3 operaciones enteras. Los métodos complicados hacen 2 accesos variables, 3 cargas constantes y 4 operaciones enteras. Es probable que las operaciones de enteros tomen tiempo, lo que depende del tamaño de los números involucrados. Los accesos variables de las funciones locales no implican “búsquedas”.

Si realmente estás desesperado por la velocidad, haz puntos de referencia. De lo contrario, KISS.

 skip = (((total << 1) // surplus) + 1) >> 1 

Cambiar las cosas dejadas por un bit efectivamente se multiplica por dos, cambiando las cosas correctamente por un bit se divide por dos redondeos hacia abajo. Agregar uno en el medio hace que el “redondeo hacia abajo” se redondee hacia arriba si el resultado hubiera estado por encima de una parte decimal de .5.

Es básicamente lo mismo que si escribieras …

 skip = int((1.0*total/surplus) + 0.5) 

excepto con todo multiplicado por 2, y luego dividido por 2, que es algo que puede hacer con la aritmética de enteros (ya que los cambios de bits no requieren punto flotante).

Inspirado por la respuesta de zhmyh , que es

 q, r = divmod(total, surplus) skip = q + int(bool(r)) # rounds to next greater integer (always ceiling) 

, Se me ocurrió la siguiente solución:

 q, r = divmod(total, surplus) skip = q + int(2 * r >= surplus) # rounds to nearest integer (floor or ceiling) 

Dado que el OP solicitó el redondeo al número entero más cercano , la solución de zhmhs es, de hecho, ligeramente incorrecta, porque siempre se redondea al siguiente número entero mayor , mientras que mi solución funciona según lo requerido.

(Si cree que mi respuesta debería haber sido una edición o un comentario sobre la respuesta de zhmh, permítame señalar que mi edición sugerida fue rechazada, porque debería haber sido un comentario, pero aún no tengo suficiente reputación para comentando!)

En caso de que te preguntes cómo se define divmod : Según su documentación.

Para los enteros, el resultado es el mismo que (a // b, a % b) .

Por lo tanto, nos atenemos a la aritmética de enteros, como lo exige el OP.

Otra forma divertida:

 q, r = divmod(total, surplus) skip = q + int(bool(r)) 

Simplemente cuida la regla de redondeo antes de dividir. Para la media vuelta más simple:

 if total % surplus < surplus / 2: return total / surplus else: return (total / surplus) + 1 

Modifique un poco si necesita hacer una ronda de igualación adecuada.

Esto debería funcionar también:

 def rint(n): return (int(n+.5) if n > 0 else int(n-.5))