Divisão interna: Por que o resultado de 1/3 == 0?


107

Eu estava escrevendo este código:

public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

O resultado é 0. Por que isso acontece e como resolvo esse problema?

Respostas:


147

Os dois operandos (1 e 3) são inteiros, portanto a aritmética de inteiros (divisão aqui) é usada. Declarar a variável de resultado como double apenas faz com que uma conversão implícita ocorra após a divisão .

A divisão inteira, é claro, retorna o verdadeiro resultado da divisão arredondado para zero. O resultado de 0.333...é, portanto, arredondado para 0 aqui. (Observe que o processador não faz nenhum arredondamento, mas você ainda pode pensar nisso dessa forma.)

Além disso, observe que se ambos os operandos (números) são fornecidos como flutuantes; 3.0 e 1.0, ou mesmo apenas o primeiro , então a aritmética de ponto flutuante é usada, dando a você 0.333....


33
+1 e sempre arredonda para baixo. int i = .99999999define int como 0. Mais especificamente, ele pega a parte inteira e descarta o resto.
Byron Whitlock

37
Ele arredonda "para zero", que é "para baixo" para valores maiores que zero. (+0,9 é arredondado para 0, -0,9 também é arredondado para 0.)
Adrian Smith

@Byron: Sim, exatamente. Não acredito que o processador realmente faça qualquer arredondamento, já que a divisão é implementada de forma muito diferente, mas é útil pensar dessa forma.
Noldorin

15
Não, sem arredondamento: truncamento
Basil Bourque

3
@BasilBourque Na terminologia java, o arredondamentoDOWN é para zero. O arredondamento FLOORé em direção ao infinito negativo.
Andreas

23

1/3 usa divisão inteira porque ambos os lados são inteiros.

Você precisa que pelo menos um deles seja floatou double.

Se você estiver inserindo os valores no código-fonte como sua pergunta, você pode fazer 1.0/3; o 1.0é um duplo.

Se você obtiver os valores de outro lugar, poderá usar (double)para transformar o intem a double.

int x = ...;
int y = ...;
double value = ((double) x) / y;

10

Lance-o explicitamente como um double

double g = 1.0/3.0

Isso acontece porque o Java usa a operação de divisão de inteiro para 1e 3desde que você os inseriu como constantes inteiras.


2

você deveria usar

double g=1.0/3;

ou

double g=1/3.0;

A divisão inteira retorna um inteiro.


2

Porque você está fazendo uma divisão inteira.

Como @Noldorin diz, se ambos os operadores forem inteiros, então a divisão de inteiros é usada.

O resultado 0,33333333 não pode ser representado como um número inteiro, portanto, apenas a parte inteira (0) é atribuída ao resultado.

Se qualquer um dos operadores for um double/ float, a aritmética de ponto flutuante ocorrerá. Mas você terá o mesmo problema se fizer isso:

int n = 1.0 / 3.0;

1

Porque trata 1 e 3 como inteiros, portanto, arredondando o resultado para 0, de modo que seja um inteiro.

Para obter o resultado que você está procurando, diga explicitamente a java que os números são duplos, como:

double g = 1.0/3.0;

1

Faça o 1 um float e a divisão de float será usada

public static void main(String d[]){
    double g=1f/3;
    System.out.printf("%.2f",g);
}

1

A conversão em JAVA é bastante simples, mas precisa de algum entendimento. Conforme explicado no JLS para operações inteiras :

Se um operador inteiro diferente de um operador de deslocamento tiver pelo menos um operando do tipo longo, a operação será realizada usando a precisão de 64 bits e o resultado do operador numérico será do tipo longo. Se o outro operando não for longo, ele é primeiro ampliado (§5.1.5) para digitar longo por promoção numérica (§5.6).

E um exemplo é sempre a melhor forma de traduzir o JLS;)

int + long -> long
int(1) + long(2) + int(3) -> long(1+2) + long(3)

Caso contrário, a operação é realizada com a precisão de 32 bits e o resultado do operador numérico é do tipo int. Se nenhum dos operandos for um int, ele é primeiro ampliado para o tipo int por promoção numérica.

short + int -> int + int -> int

Um pequeno exemplo usando Eclipse para mostrar que mesmo a adição de dois shorts não será tão fácil:

short s = 1;
s = s + s; <- Compiling error

//possible loss of precision
//  required: short
//  found:    int

Isso exigirá uma fundição com uma possível perda de precisão.

O mesmo é verdadeiro para os operadores de ponto flutuante

Se pelo menos um dos operandos para um operador numérico for do tipo double, a operação será realizada usando aritmética de ponto flutuante de 64 bits e o resultado do operador numérico será um valor do tipo double. Se o outro operando não for um duplo, ele é primeiro ampliado (§5.1.5) para digitar duplo por promoção numérica (§5.6).

Portanto, a promoção é feita no float para o dobro.

E a mistura de valores inteiros e flutuantes resulta em valores flutuantes, como disse

Se pelo menos um dos operandos para um operador binário for do tipo de ponto flutuante, a operação será de ponto flutuante, mesmo se o outro for integral.

Isso é verdade para operadores binários, mas não para "Operadores de atribuição" como +=

Um exemplo simples de trabalho é o suficiente para provar isso

int i = 1;
i += 1.5f;

A razão é que há um elenco implícito feito aqui, ele será executado como

i = (int) i + 1.5f
i = (int) 2.5f
i = 2

1

1 e 3 são contantes inteiros e então Java faz uma divisão inteira cujo resultado é 0. Se você quiser escrever constantes duplas, você terá que escrever 1.0e 3.0.


1

A solução mais fácil é apenas fazer isso

double g = ((double) 1 / 3);

O que isso faz, uma vez que você não inseriu 1.0 / 3.0, é permitir que você o converta manualmente para o tipo de dados double, já que Java assumiu que era uma divisão inteira, e faria isso mesmo que significasse estreitar a conversão. Isso é chamado de operador de elenco.


1

Eu fiz isso.

double g = 1.0/3.0;
System.out.printf("%gf", g);

Use .0 ao fazer cálculos duplos ou então o Java assumirá que você está usando números inteiros. Se um cálculo usar qualquer quantidade de valores duplos, a saída será um valor duplo. Se forem todos inteiros, a saída será um inteiro.


0

(1/3) significa divisão inteira, por isso você não pode obter o valor decimal desta divisão. Para resolver este problema, use:

public static void main(String[] args) {
        double g = 1.0 / 3;
        System.out.printf("%.2f", g);
    }

0
public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

Como 1 e 3 são inteiros, o resultado não é arredondado, mas truncado. Assim, você ignora as frações e pega apenas todos.

Para evitar isso, tenha pelo menos um de seus números 1 ou 3 como formato decimal 1.0 e / ou 3.0.


0

Experimente isto:

public static void main(String[] args) {
    double a = 1.0;
    double b = 3.0;
    double g = a / b;
    System.out.printf(""+ g);
}

Não poste respostas apenas com código, inclua uma explicação sobre o que seu código faz e como (e por que) ele resolve o problema da questão. Considere também que esta é uma pergunta de 8 anos que tem respostas votadas positivamente e se sua resposta adiciona algum valor sobre as respostas existentes.
Mark Rotteveel de

-1

Faça "duplo g = 1,0 / 3,0;" em vez de.


Apenas curiosidade ... o que há de errado com essa resposta? Usei essa abordagem quando encontrei o problema pela primeira vez. Uma explicação do que está errado com esta solução seria apreciada. Desde já, obrigado.
Sr. Port St Joe

@ Mr.PortStJoe Não fornece detalhes para a pessoa que faz a pergunta. Observando a resposta com melhor classificação, podemos ver que também explicaram POR QUE isso está acontecendo. Embora a resposta possa ser tecnicamente correta, sua utilidade pode ser vista como limitada.
Andrew T Finnell de

-1

Muitos outros não conseguiram apontar o verdadeiro problema:

Uma operação apenas em inteiros converte o resultado da operação em um inteiro.

Isso necessariamente significa que os resultados de ponto flutuante, que podem ser exibidos como um inteiro, serão truncados (corte da parte decimal).

O que é casting (typecasting / conversão de tipo), você pergunta?

Isso varia de acordo com a implementação da linguagem, mas a Wikipedia tem uma visão bastante abrangente e também fala sobre coerção , que é uma informação essencial para responder à sua pergunta.

http://en.wikipedia.org/wiki/Type_conversion


Essa resposta é falha. Não há conversão de tipo ou conversão de tipo envolvida em uma divisão inteira como 1/2, não na linguagem de destino (java). Você simplesmente invoca uma divisão inteira, que resultará em um resultado inteiro. A Fundição de Tipo é apenas por causa da conversão ascendente de intpara a doubledurante a atribuição.
YoYo,

@YoYo Você está dizendo que 0,5 é um número inteiro? Acho que não, mano.
sova,

1
este mano não disse nada sobre 0.5. Simplesmente, em Java, 1/2é uma divisão inteira, que resulta em um inteiro zero. Você pode atribuir um zero a um duplo, ainda será um zero, embora seja um 0.0duplo.
YoYo,
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.