Divisão inteira: Como você produz um duplo?


249

Para este bloco de código:

int num = 5;
int denom = 7;
double d = num / denom;

o valor de dé 0.0. Pode ser forçado a trabalhar lançando:

double d = ((double) num) / denom;

Mas existe outra maneira de obter o doubleresultado correto ? Não gosto de lançar primitivos, quem sabe o que pode acontecer.



9
converter um 'int' para um duplo é seguro, você sempre obterá o mesmo valor sem perda de precisão.
Peter Lawrey

1
Gostaria de saber se a seguir estão os passos corretos executados pelo compilador para a divisão: 1) cast num to float 2) cast denom para float também 2) divide num por denomin. Informe-me se estiver incorreto.
mannyee

Respostas:


156
double num = 5;

Isso evita um elenco. Mas você descobrirá que as conversões de elenco são bem definidas. Você não precisa adivinhar, basta verificar o JLS . int dobrar é uma conversão crescente. A partir do §5.1.2 :

As conversões primitivas ampliadas não perdem informações sobre a magnitude geral de um valor numérico.

[...]

A conversão de um valor int ou longo para flutuar ou de um valor longo para duplicar pode resultar em perda de precisão, ou seja, o resultado pode perder alguns dos bits menos significativos do valor. Nesse caso, o valor de ponto flutuante resultante será uma versão arredondada corretamente do valor inteiro, usando o modo IEEE 754 de arredondar para o mais próximo (§4.2.4) .

5 pode ser expresso exatamente como um duplo.


60
+1. Pare de ter medo de lançar. Aprenda como funciona. Está tudo bem definido.
Mark Peters

1
@FabricioPH Isso funciona em todas as situações e de forma idêntica à solução * 1.0.
Vitruvius

3
@Saposhiente Vai além do trabalho ou não. Alterar o tipo de uma variável parece um código sujo para mim. Se você tem algo que DEVE SER um número inteiro e altera a representação de um float apenas para poder executar a operação matemática, pode estar se arriscando. Também em algum contexto, a leitura da variável como um número inteiro facilita a compreensão do código.
Fabricio PH

2
O @FabricioPH, multiplicar por 1.0ainda altera o tipo de resultado, apenas de uma maneira diferente. "Parece código sujo para mim" não explica em que cenário (s) você acredita que multiplicar 1.0é realmente melhor (ou por que isso pode ser).
Matthew Flaschen

4
@FabricioPH Você e o OP parecem estar trabalhando sob a crença falsa (onde você diz "Alterando o tipo da variável") que fazer de (double)numalguma maneira muda num. Não - ele cria um duplo temporário para o contexto da declaração.
Paul Tomblin 27/03

100

O que há de errado com a transmissão de primitivas?

Se você não quer transmitir por algum motivo, pode fazer

double d = num * 1.0 / denom;

63
... que faz uma conversão implícita antes da multiplicação
Alice Purcell

2
Na maioria dos casos, isso é melhor do que alterar o tipo de outra variável.
Fabricio PH

3
@ FabricioPH Nada sugere que isso seja verdade, a menos que você tenha uma fonte para citar.
Vitruvius

1
@Saposhiente respondeu em seu outro comentário similar
Fabricio PH

1
Você também pode usar o postfix "D" (para executar a mesma conversão implícita); - por exemplo:double d = num * 1D / denom;
BrainSlugs83

73

Não gosto de lançar primitivos, quem sabe o que pode acontecer.

Por que você tem um medo irracional de lançar primitivos? Nada de ruim vai acontecer quando você joga um intpara um double. Se você não tiver certeza de como isso funciona, consulte a Especificação de Linguagem Java . Converter um intpara doubleé uma conversão primitiva cada vez maior .

Você pode se livrar do par extra de parênteses lançando o denominador em vez do numerador:

double d = num / (double) denom;

2
você pode muito bem fazer double d = (double) num / denom;... (OK, este depende de precedência)
user85421

27

Se você alterar o tipo de uma das variáveis, lembre-se de esgueirar-se novamente duas vezes se sua fórmula mudar, porque se essa variável deixar de fazer parte do cálculo, o resultado será confuso. Eu tenho o hábito de transmitir dentro do cálculo e adiciono um comentário ao lado.

double d = 5 / (double) 20; //cast to double, to do floating point calculations

Observe que a transmissão do resultado não fará isso

double d = (double)(5 / 20); //produces 0.0

17

Lance um dos números inteiros / os dois inteiros para flutuar para forçar a operação a ser feita com ponto flutuante Math. Caso contrário, o número inteiro Math é sempre preferido. Assim:

1. double d = (double)5 / 20;
2. double v = (double)5 / (double) 20;
3. double v = 5 / (double) 20;

Observe que a transmissão do resultado não será suficiente. Porque a primeira divisão é feita conforme a regra de precedência.

double d = (double)(5 / 20); //produces 0.0

Eu não acho que exista algum problema com o elenco, como tal, você está pensando.


10

Fundição tipo é a única maneira


Produzir uma doubledivisão inteira - não há outro caminho sem a conversão (pode ser que você não faça isso explicitamente, mas isso acontecerá).

Agora, existem várias maneiras pelas quais podemos tentar obter um doublevalor preciso (onde nume denomsão do inttipo e, é claro, com a transmissão ) -

  1. com transmissão explícita:

    • double d = (double) num / denom;
    • double d = ((double) num) / denom;
    • double d = num / (double) denom;
    • double d = (double) num / (double) denom;

mas não double d = (double) (num / denom);

  1. com elenco implícito:

    • double d = num * 1.0 / denom;
    • double d = num / 1d / denom;
    • double d = ( num + 0.0 ) / denom;
    • double d = num; d /= denom;

mas não double d = num / denom * 1.0;
e nãodouble d = 0.0 + ( num / denom );


2

use algo como:

double step = 1d / 5;

(1d é convertido para dobrar)


5
1d não é um elenco, é apenas uma declaração.
Sefier Tang

0

Você pode considerar agrupar as operações. Por exemplo:

class Utils
{
    public static double divide(int num, int denom) {
        return ((double) num) / denom;
    }
}

Isso permite que você verifique (apenas uma vez) se o elenco faz exatamente o que você deseja. Esse método também pode estar sujeito a testes, para garantir que ele continue fazendo o que você deseja. Também não importa qual truque você usa para causar a divisão (você pode usar qualquer uma das respostas aqui), desde que resulte no resultado correto. Em qualquer lugar que você precisar dividir dois números inteiros, agora você pode simplesmente ligar Utils::dividee confiar que ele faz a coisa certa.



0

A melhor maneira de fazer isso é

int i = 3;
Double d = i * 1.0;

d is 3.0 now.
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.