Java arredondando para um int usando Math.ceil


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

Por que ainda retorna 4? 157/32 = 4.90625, Preciso arredondar, olhei em volta e parece ser o método certo.

Tentei totalcomo doubletipo, mas consegui 4.0.

O que estou fazendo de errado?

Respostas:


187

Você está fazendo o 157/32que é dividir dois inteiros entre si, o que sempre resulta em um inteiro arredondado para baixo. Portanto, o (int) Math.ceil(...)não está fazendo nada. Existem três soluções possíveis para alcançar o que deseja. Eu recomendo usar a opção 1 ou a opção 2 . Por favor, não use a opção 0 .

## Opção 0

Converta ae bpara um duplo, e você pode usar a divisão e Math.ceilcomo você queria que funcionasse. No entanto, eu desencorajo fortemente o uso dessa abordagem, porque a divisão dupla pode ser imprecisa. Para ler mais sobre a imprecisão das duplas, consulte esta questão .

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

##Opção 1

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

Você faz a / bcom always floor se ae bforem ambos inteiros. Então você tem uma instrução if inline que verifica se você deve ou não teto em vez de chão. Portanto, +1 ou +0, se houver um resto com a divisão, você precisará de +1. a % b == 0verifica o restante.

##Opção 2

Esta opção é muito curta, mas talvez para alguns menos intuitiva. Acho que essa abordagem menos intuitiva seria mais rápida do que a abordagem de divisão dupla e comparação:
Observe que isso não funciona para b < 0.

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

Para reduzir a chance de estouro, você pode usar o seguinte. No entanto, observe que não funciona para a = 0e b < 1.

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

## Explicação por trás da "abordagem menos intuitiva"

Uma vez que dividir dois inteiros em Java (e na maioria das outras linguagens de programação) sempre irá limitar o resultado. Assim:

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

Mas não queremos floor(a/b), mas ceil(a/b)e usando as definições e gráficos da Wikipedia :insira a descrição da imagem aqui

Com estes lotes da função chão e tecto é possível ver a relação.

Função de chão Função de teto

Você pode ver isso floor(x) <= ceil(x). Nós precisamos floor(x + s) = ceil(x). Portanto, precisamos encontrar s. Se pegarmos 1/2 <= s < 1, vai dar certo (experimente alguns números e você verá que sim, eu mesmo acho difícil provar isso). E 1/2 <= (b-1) / b < 1então

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

Esta não é uma prova real, mas espero que você esteja satisfeito com ela. Se alguém puder explicar melhor eu também agradeceria. Talvez pergunte no MathOverflow .


1
Seria um grande favor se você pudesse explicar a intuição por trás da abordagem menos intuitiva? Sei que isso está correto, quero saber como você chegou a isso e como posso mostrar matematicamente que está correto. Tentei resolver matematicamente, não estava convencido.
Saad Rehman Shah

Espero que você esteja satisfeito com minha edição, não consigo fazer melhor, eu acho :(
martijnn2008

Suponho que Math.floor e ceil estão corretos apenas para divisão inteira, não para divisão longa, quando os valores são convertidos em duplos. Os exemplos de contadores são 4611686018427386880/4611686018427387137 falha no chão e 4611686018427386881/4611686018427386880 falha no teto
Wouter

2
Um ponto de esclarecimento: os resultados das duas subopções da opção 2 não são idênticos em todos os casos. Um valor zero para a fornecerá 0 no primeiro e 1 no segundo (o que não é a resposta correta para a maioria dos aplicativos).
Sushisource de

1
Tem certeza de que não quis dizer "No entanto, observe que não funciona para a = 0 e b < 1"
dantiston

60

157/32 é int/int, o que resulta em um int.

Tente usar o literal dupla - 157/32d, o que é int/double, o que resulta em um double.


Tem certeza de que int / int sempre resultará em um int ?! você pode, por favor, fornecer a fonte disso ?!


34

157/32é uma divisão inteira porque todos os literais numéricos são inteiros, a menos que seja especificado de outra forma com um sufixo ( dpara double lfor long)

a divisão é arredondada para baixo (para 4) antes de ser convertida para um duplo (4,0), que é então arredondado para cima (para 4,0)

se você usar variáveis, pode evitar que

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


7

Ninguém mencionou o mais intuitivo:

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

Esta solução corrige a imprecisão da divisão dupla .


1
Math.round retorna longo
Zulqurnain Jutt

Obrigado @ZulqurnainJutt, adicionado um elenco
IG Pascual

4

Em Java, adicionar um .0 tornará um duplo ...

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

3

Ao dividir dois inteiros, por exemplo,

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

o resultado é um int, o valor do qual é adividido por b, arredondado para zero. Porque o resultado já está arredondado, ceil()não faz nada. Observe que esse arredondamento não é o mesmo que floor(), que arredonda para o infinito negativo. Então, 3/2igual 1(e floor(1.5)igual 1.0, mas (-3)/2igual -1(mas floor(-1.5)igual -2.0).

Isso é significativo porque se a/bfosse sempre o mesmo que floor(a / (double) b), você poderia apenas implementar ceil()de a/bcomo -( (-a) / b).

A sugestão de obter ceil(a/b)de

int n = (a + b - 1) / b;, que é equivalente a a / b + (b - 1) / b, ou(a - 1) / b + 1

funciona porque ceil(a/b)é sempre um maior que floor(a/b), exceto quando a/bé um número inteiro. Portanto, você deseja aumentar (ou ultrapassar) o próximo número inteiro, a menos que a/bseja um número inteiro. Adicionar 1 - 1 / bfará isso. Para números inteiros, não vai empurrá-los para o próximo número inteiro. Para todo o resto, sim.

Caramba. Esperançosamente, isso faz sentido. Tenho certeza de que há uma maneira matematicamente mais elegante de explicar isso.


2

Além disso, para converter um número inteiro em real, você pode adicionar um ponto:

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

E o resultado de (157/32.) Também será real. ;)



1

Verifique a solução abaixo para sua dúvida:

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

Aqui você deve multiplicar Numerator por 1.0, então ele dará sua resposta.

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

0

Use duplo para lançar como

Math.ceil((double)value) ou como

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

0

Java fornece apenas divisão de piso /por padrão. Mas podemos escrever teto em termos de piso . Vamos ver:

Qualquer número inteiro ypode ser escrito com o formulário y == q*k+r. De acordo com a definição de divisão do piso (aqui floor) que completa r,

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

e da divisão do teto (aqui ceil) que arredonda para cima r₁,

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

onde podemos substituir r+1por r₁:

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


Em seguida, substituímos a primeira equação na terceira para qobter

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

Finalmente, dado qualquer inteiro yonde y = q*k+r+1há algum q, k, r, temos

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

E nós terminamos. Espero que isto ajude.


Tenho certeza que isso está correto, mas como o objetivo disso é esclarecer, não está claro para mim por que ceilé definido como tal na definição intuitiva, em particular quando estamos tomando o teto de um número inteiro, ou seja, r1 = k. Já que os casos extremos são o que há de complicado nisso, acho que precisa ser explicado um pouco mais.
Luigi Plinge

@LuigiPlinge Para mim, a derivação não pode ser mais simples devido à diferença intrínseca entre piso e teto no contexto da operação de divisão. Acho que você não precisa se concentrar no caso extremo - é um fato natural quando você tenta unificar as definições de piso e teto dividindo um número inteiro. Como resultado, a prova consiste em apenas três etapas, e a conclusão pode ser aproximadamente lembrada como "um passo amortizado para trás, depois um passo absoluto para a frente".
ShellayLee

0

Existem dois métodos pelos quais você pode arredondar o seu valor duplo.

  1. Math.ceil
  2. Math.floor

Se você quiser sua resposta 4,90625 como 4, você deve usar Math.floor e se você quiser sua resposta 4,90625 como 5, você pode usar Math.ceil

Você pode consultar o código a seguir para isso.

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

ou mais geral

(a-1)/b +1 

Acho que isso funciona, mas você realmente não explicou por que a versão original não funcionou.
Teepeemm

" No entanto, observe que não funciona para a = 0 e b <1 "
IG Pascual
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.