Java redondeando a un int usando Math.ceil


101
int total = (int) Math.ceil(157/32);

¿Por qué todavía devuelve 4? 157/32 = 4.90625, Necesito redondear, he mirado alrededor y este parece ser el método correcto.

Intenté totalcomo doubletipo, pero obtuve 4.0.

¿Qué estoy haciendo mal?

Respuestas:


187

Lo que está haciendo 157/32es dividir dos enteros entre sí, lo que siempre resulta en un entero redondeado hacia abajo. Por lo tanto, (int) Math.ceil(...)no está haciendo nada. Hay tres posibles soluciones para lograr lo que desea. Yo recomiendo usar ya sea la opción 1 o la opción 2 . Por favor, NO utilice la opción 0 .

## Opción 0

Convierta ay ben un doble, y puede usar la división y Math.ceilcomo desea que funcione. Sin embargo, desaconsejo encarecidamente el uso de este enfoque, porque la doble división puede ser imprecisa. Para leer más sobre la imprecisión de los dobles, consulte esta pregunta .

int n = (int) Math.ceil((double) a / b));

##Opción 1

int n = a / b + ((a % b == 0) ? 0 : 1); 

Lo haces a / bcon siempre suelo si ay bambos son enteros. Luego tiene una instrucción if en línea que verifica si debe o no ceil en lugar de floor. Entonces +1 o +0, si hay un resto con la división, necesita +1. a % b == 0cheques para el resto.

##Opcion 2

Esta opción es muy corta, pero quizás para algunos menos intuitivos. Creo que este enfoque menos intuitivo sería más rápido que el enfoque de doble división y comparación:
tenga en cuenta que esto no funciona b < 0.

int n = (a + b - 1) / b;

Para reducir la posibilidad de desbordamiento, puede utilizar lo siguiente. Sin embargo, tenga en cuenta que no funciona para a = 0y b < 1.

int n = (a - 1) / b + 1;

## Explicación detrás del "enfoque menos intuitivo"

Dado que dividir dos enteros en Java (y la mayoría de los otros lenguajes de programación) siempre basará el resultado. Entonces:

int a, b;
int result = a/b (is the same as floor(a/b) )

Pero no queremos floor(a/b), pero ceil(a/b), y usando las definiciones y gráficos de Wikipedia :ingrese la descripción de la imagen aquí

Con estas gráficas de la función suelo y techo se puede ver la relación.

Función de suelo Función de techo

Puedes ver eso floor(x) <= ceil(x). Necesitamos floor(x + s) = ceil(x). Entonces tenemos que encontrar s. Si lo tomamos 1/2 <= s < 1, será correcto (pruebe algunos números y verá que sí, me resulta difícil probarlo). Y 1/2 <= (b-1) / b < 1asi

ceil(a/b) = floor(a/b + s)
          = floor(a/b + (b-1)/b)
          = floor( (a+b-1)/b) )

Esta no es una prueba real, pero espero que esté satisfecho con ella. Si alguien puede explicarlo mejor, también lo agradecería. Quizás preguntarlo en MathOverflow .


1
¿Sería un gran favor si pudiera explicar la intuición detrás del enfoque menos intuitivo? Sé que esto es correcto, quiero saber cómo llegaste a eso y cómo puedo demostrar matemáticamente que es correcto. Intenté resolverlo matemáticamente, no estaba convencido.
Saad Rehman Shah

Espero que esté satisfecho con mi edición, no puedo hacerlo mejor, creo :(
martijnn2008

Supongo que Math.floor y ceil solo son correctos para la división de enteros, no para la división larga cuando los valores se convierten en dobles. Ejemplos de contador son 4611686018427386880/4611686018427387137 falla en el piso y 4611686018427386881/4611686018427386880 falla en el techo
Wouter

2
Una aclaración: los resultados de las dos subopciones de la opción 2 no son idénticos en todos los casos. Un valor de cero para a proporcionará 0 en el primero y 1 en el segundo (que no es la respuesta correcta para la mayoría de las aplicaciones).
Sushisource

1
¿Está seguro de que no quiso decir "Sin embargo, tenga en cuenta que no funciona para a = 0 yb < 1"
dantiston

60

157/32 es int/int, lo que da como resultado un int.

Intente usar el doble literal - 157/32d, que es int/double, que da como resultado un double.


¿Estás seguro de que int / int siempre resultará en un int? ¡¿Podrías dar una fuente a eso ?!


34

157/32es una división de enteros porque todos los literales numéricos son enteros a menos que se especifique lo contrario con un sufijo ( dpara doble lpara largo)

la división se redondea hacia abajo (a 4) antes de convertirse en un doble (4.0) que luego se redondea hacia arriba (a 4.0)

si usa variables, puede evitarlo

double a1=157;
double a2=32;
int total = (int) Math.ceil(a1/a2);

26
int total = (int) Math.ceil((double)157/32);

7

Nadie ha mencionado lo más intuitivo:

int x = (int) Math.round(Math.ceil((double) 157 / 32));

Esta solución corrige la imprecisión de la doble división.


1
Math.round vuelve largo
Zulqurnain Jutt

Gracias @ZulqurnainJutt, agregó un elenco
IG Pascual

4

En Java, agregar un .0 lo convertirá en un doble ...

int total = (int) Math.ceil(157.0 / 32.0);

3

Al dividir dos enteros, por ejemplo,

int c = (int) a / (int) b;

el resultado es un int, cuyo valor se adivide entre b, redondeado hacia cero. Como el resultado ya está redondeado, ceil()no hace nada. Tenga en cuenta que este redondeo no es el mismo que floor(), que redondea hacia el infinito negativo. Entonces, 3/2es igual 1(y floor(1.5)es igual 1.0, pero (-3)/2es igual -1(pero floor(-1.5)es igual -2.0)).

Esto es importante porque si a/bsiempre fueron los mismos que floor(a / (double) b), a continuación, usted podría aplicar ceil()de a/btan -( (-a) / b).

La sugerencia de obtener ceil(a/b)de

int n = (a + b - 1) / b;, que es equivalente a a / b + (b - 1) / b, o(a - 1) / b + 1

funciona porque ceil(a/b)siempre es uno mayor que floor(a/b), excepto cuando a/bes un número entero. Por lo tanto, desea subirlo (o pasar) al siguiente número entero, a menos que a/bsea ​​un número entero. Agregar 1 - 1 / bhará esto. Para números enteros, no los empujará al siguiente número entero. Para todo lo demás, lo hará.

¡Ay! Ojalá tenga sentido. Estoy seguro de que hay una forma matemáticamente más elegante de explicarlo.


2

Además, para convertir un número de entero a número real, puede agregar un punto:

int total = (int) Math.ceil(157/32.);

Y el resultado de (157/32.) También será real. ;)



1

Compruebe la solución a continuación para su pregunta:

int total = (int) Math.ceil(157/32);

Aquí debe multiplicar Numerador por 1.0, luego dará su respuesta.

int total = (int) Math.ceil(157*1.0/32);

0

Usa doble para lanzar como

Math.ceil((double)value) o me gusta

Math.ceil((double)value1/(double)value2);

0

Java proporciona solo división de piso /de forma predeterminada. Pero podemos escribir techo en términos de piso . Veamos:

Cualquier número entero yse puede escribir con el formulario y == q*k+r. De acuerdo con la definición de división de piso (aquí floor) que completa r,

floor(q*k+r, k) == q  , where 0  r  k-1

y de la división del techo (aquí ceil) que se redondea r₁,

ceil(q*k+r₁, k) == q+1  , where 1  r  k

donde podemos sustituir r+1por r₁:

ceil(q*k+r+1, k) == q+1  , where 0  r  k-1


Luego sustituimos la primera ecuación en la tercera para qobtener

ceil(q*k+r+1, k) == floor(q*k+r, k) + 1  , where 0  r  k-1

Por último, dado cualquier número entero y, donde y = q*k+r+1para algunos q, k, r, tenemos

ceil(y, k) == floor(y-1, k) + 1

Y hemos terminado. Espero que esto ayude.


Estoy seguro de que esto es correcto, pero dado que el objetivo de esto es aclarar, no me ceilqueda claro por qué se define como tal a partir de la definición intuitiva, en particular cuando estamos tomando el techo de un número entero, es decir, r1 = k. Dado que los casos extremos son lo complicado de esto, creo que debe explicarse un poco más.
Luigi Plinge

@LuigiPlinge Para mí, la derivación no puede ser más simple debido a la diferencia intrínseca entre piso y techo en el contexto de la operación de división. Creo que no es necesario que se centre en el caso extremo; es un hecho natural cuando intenta unificar las definiciones de suelo y techo mediante la descomposición de un número entero. Como resultado, la prueba consta de solo tres pasos, y la conclusión puede recordarse aproximadamente como "un paso atrás amortizado, luego un paso adelante absoluto".
ShellayLee

0

Hay dos métodos mediante los cuales puede redondear su valor doble.

  1. Math.ceil
  2. Math.floor

Si desea que su respuesta 4.90625 sea 4, entonces debe usar Math.floor y si desea que su respuesta 4.90625 sea 5, entonces puede usar Math.ceil

Puede consultar el siguiente código para eso.

public class TestClass {

    public static void main(String[] args) {
        int floorValue = (int) Math.floor((double)157 / 32);
        int ceilValue = (int) Math.ceil((double)157 / 32);
        System.out.println("Floor: "+floorValue);
        System.out.println("Ceil: "+ceilValue);

    }

}

-3
int total = (157-1)/32 + 1

o más general

(a-1)/b +1 

Creo que esto funciona, pero no has explicado realmente por qué no funcionó la versión original.
Teepeemm

" Sin embargo, tenga en cuenta que no funciona para a = 0 y b <1 "
IG Pascual
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.