Pergunta

public class doublePrecision {
    public static void main(String[] args) {

        double total = 0;
        total += 5.6;
        total += 5.8;
        System.out.println(total);
    }
}

O código acima imprime:

11.399999999999

Como eu poderia chegar a este apenas impressão (ou ser capaz de usá-lo como) 11,4?

Foi útil?

Solução

Como já foi mencionado, você provavelmente vai querer usar o BigDecimal classe, se você quer ter uma representação exata de 11,4.

Agora, uma pequena explicação sobre por que isso está acontecendo:

Os float e double tipos primitivos em Java são números de ponto flutuante , em que o número é armazenado como uma representação binária de uma fração e um expoente.

Mais especificamente, um valor de ponto flutuante de precisão dupla, tais como o tipo de double é um valor de 64 bits, em que:

  • 1 bit denota o sinal (positivo ou negativo).
  • 11 bits para o expoente.
  • 52 bits para os dígitos significativos (a parte fracionária como um binário).

Estas peças são combinados para produzir uma representação de um valor double.

(Fonte: Wikipedia: Duplo precisão )

Para uma descrição detalhada dos valores de ponto como flutuantes são tratadas em Java, consulte o Seção 4.2.3: ponto flutuante tipos, formatos e valores da especificação de linguagem Java

.

Os byte, char, int, tipos long são números ponto fixo, que são representions exatas de números. Ao contrário de números de ponto fixo, números de ponto flutuante vai algumas vezes (seguro assumir "a maior parte do tempo") não será capaz de retornar uma representação exata de um número. Esta é a razão pela qual você acabar com 11.399999999999 como o resultado de 5.6 + 5.8.

Quando exigindo um valor que é exato, como 1,5 ou 150,1005, você vai querer usar um dos tipos de ponto fixo, que será capaz de representar o número exatamente.

Como foi mencionado várias vezes já, Java tem um class BigDecimal que irá lidar com números muito grandes e muito pequenos números.

A partir da API Referência Java para a classe BigDecimal:

Imutável, de precisão arbitrária decimal assinado números. Um BigDecimal consiste numa precisão arbitrária inteiro unscaled valor e uma escala de números inteiros de 32 bits. E se zero ou positiva, a escala é a número de dígitos à direita da ponto decimal. Se negativo, o valor sem escala do número é multiplicado por dez para o poder da negação da escala. O valor de o número representado pela BigDecimal é, portanto, (unscaledValue × 10 ^ -scale).

Tem havido muitas perguntas sobre estouro de pilha relacionados com a questão de números de ponto flutuante de precisão e sua. Aqui está uma lista de perguntas relacionadas que podem ser de interesse:

Se você realmente quer descer aos detalhes Nitty Gritty de flutuanteNúmeros de ponto, dê uma olhada O que cada cientista computador deve saber sobre Floating-Point Arithmetic .

Outras dicas

Quando você inserir um número duplo, por exemplo, 33.33333333333333, o valor que você recebe é na verdade o valor de precisão dupla representável mais próximo, que é exatamente:

33.3333333333333285963817615993320941925048828125

Dividir que por 100 dá:

0.333333333333333285963817615993320941925048828125

que também não é representável como um número de precisão dupla, então, novamente, é arredondado para o valor representável mais próximo, que é exatamente:

0.3333333333333332593184650249895639717578887939453125

Quando você imprime este valor para fora, ele fica arredondada mais uma vez para 17 dígitos decimais, dando:

0.33333333333333326

Se você quiser apenas para valores de processo como frações, você pode criar uma classe Fraction que detém um campo numerador eo denominador.

métodos de gravação para adicionar, subtrair, multiplicar e dividir, bem como um método toDouble. Desta forma, você pode evitar flutua durante cálculos.

EDIT: Rápida implementação,

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop-bTop, a.denominator*b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}

Observe que você teria o mesmo problema se você usou de precisão limitada decimal aritmética e queria lidar com 1/3: 0,333333333 * 3 é 0,999999999, não 1,00000000.

Infelizmente, 5,6, 5,8 e 11,4 não são apenas números redondos em binário, porque envolvem quintos. Assim, a representação flutuador deles não é exata, assim como 0,3333 não é exatamente 1/3.

Se todos os números que usamos são decimais não-recorrentes, e você quer resultados exatos, use BigDecimal. Ou, como já foi dito, se os seus valores são como dinheiro no sentido de que todos eles são um múltiplo de 0,01, ou 0,001, ou algo assim, então tudo se multiplicam por uma potência fixa de 10 e utilização int ou long (adição e subtração são trivial:. atente para multiplicação)

No entanto, se você está feliz com o binário para o cálculo, mas você só quer imprimir as coisas em um formato ligeiramente mais amigável, tente java.util.Formatter ou String.format. Na seqüência de formato de especificar uma precisão inferior a toda a precisão de um duplo. Para 10 algarismos significativos, digamos, 11,399999999999 é 11,4, então o resultado será quase tão precisos e mais legível nos casos em que o resultado binário está muito perto de um valor que requer apenas alguns lugares decimais.

A precisão para especificar depende um pouco sobre o quanto a matemática que você fez com seus números - em geral, quanto mais você faz, mais de erro irá acumular, mas alguns algoritmos acumulá-lo muito mais rápido do que outros (eles são chamados " instável" em oposição a 'estável' com respeito a erros de arredondamento). Se tudo que você está fazendo é adicionar alguns valores, então eu acho que deixar cair apenas uma casa decimal de precisão vai resolver as coisas. Experimento.

Você pode querer olhar em usar classe java.math.BigDecimal de java se você realmente precisa de matemática precisão. Aqui está um bom artigo da Oracle / Sun on o caso para BigDecimal . Enquanto você nunca pode representar 1/3 como alguém mencionou, você pode tem o poder de decidir exatamente como precisa você quer que o resultado seja. setScale () é seu amigo ..:)

Ok, porque não tenho tempo demais em minhas mãos no momento em que aqui é um exemplo de código que se refere a sua pergunta:

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

e ligar o meu novo idioma favorito, Groovy, aqui está um exemplo mais puro da mesma coisa:

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333

Pretty certeza de que poderia ter feito isso em um exemplo de três linhas. :)

Se você quiser precisão exata, use BigDecimal. Caso contrário, você pode usar ints multiplicado por 10 ^ seja qual for a precisão que você deseja.

Como outros já mencionado, nem todos os valores decimais pode ser representado como binário desde decimal é baseado em potências de 10 e binário é baseado em potências de dois.

Se as questões de precisão, use BigDecimal, mas se você quiser apenas saída amigável:

System.out.printf("%.2f\n", total);

Vamos dar-lhe:

11.40

Você está correndo contra a limitação de precisão do tipo double.

java.math tem algumas instalações bignum.

Você não pode, porque 7.3 não tem uma representação finita em binário. O mais próximo que você pode obter é 2054767329987789/2 ** 48 = 7,3 + 1/1407374883553280.

Dê uma olhada http://docs.python.org/tutorial/floatingpoint.html para uma explicação adicional. (É no site do Python, mas Java e C ++ têm o mesmo "problema".)

A solução depende do que exatamente o seu problema é:

  • Se é que você simplesmente não gosto de ver todos os dígitos de ruído, em seguida, corrigir a seqüência de formatação. Não exibir mais de 15 dígitos significativos (ou 7 para float).
  • Se é que a inexatidão dos seus números é quebrar coisas como "se" declarações, então você deve escrever if (abs (x - 7,3)
  • Se você está trabalhando com o dinheiro, então o que você provavelmente realmente quer é decimal ponto fixo. Armazenar um número inteiro de centavos ou qualquer que seja a menor unidade de sua moeda é.
  • (muito improvável) Se você precisar de mais de 53 bits significativos (15-16 dígitos significativos) de precisão, em seguida, usar um de alta precisão tipo de ponto flutuante, como BigDecimal.
private void getRound() {
    // this is very simple and interesting 
    double a = 5, b = 3, c;
    c = a / b;
    System.out.println(" round  val is " + c);

    //  round  val is  :  1.6666666666666667
    // if you want to only two precision point with double we 
            //  can use formate option in String 
           // which takes 2 parameters one is formte specifier which 
           // shows dicimal places another double value 
    String s = String.format("%.2f", c);
    double val = Double.parseDouble(s);
    System.out.println(" val is :" + val);
    // now out put will be : val is :1.67
}

Use java.math.BigDecimal

Duplas são frações binárias internamente, de modo que às vezes não pode representar frações decimais para o decimal exato.

Multiplicar tudo por 100 e armazená-lo em um longo como centavos.

Computadores armazenar números em binário e não pode realmente representar números como 33,333333333 ou 100,0 exatamente. Esta é uma das coisas complicadas sobre o uso de duplas. Você terá que apenas em torno a resposta antes de mostrá-lo a um usuário. Felizmente, na maioria dos aplicativos, você não precisa que muitas casas decimais de qualquer maneira.

Números de ponto flutuante diferem dos números reais em que para qualquer número de ponto flutuante não é um número de ponto imediatamente superior flutuante. Mesmo como inteiros. Não há nenhum número inteiro entre 1 e 2.

Não há nenhuma maneira para representar 1/3 como um float. Há um flutuador abaixo dela e há um float acima dela, e há uma certa distância entre eles. E 1/3 é nesse espaço.

Apfloat para Java diz trabalhar com números de ponto flutuante de precisão arbitrária, mas eu nunca usei. Provavelmente vale uma olhada. http://www.apfloat.org/apfloat_java/

Uma pergunta semelhante foi perguntado aqui antes Java ponto flutuante de alta precisão biblioteca

Duplas são aproximações dos números decimais em sua fonte Java. Você está vendo a conseqüência da incompatibilidade entre o casal (que é um valor codificado binário) e sua fonte (que é codificado decimal-).

Java está produzindo a aproximação binário mais próximo. Você pode usar o java.text.DecimalFormat para exibir um valor decimal de melhor aparência.

Use um BigDecimal. Ele ainda permite que você especificar regras de arredondamento (como ROUND_HALF_EVEN, que irá minimizar o erro estatístico por arredondamento para o vizinho mesmo se ambos são a mesma distância, isto é tanto 1,5 e 2,5 rodada a 2)

.

Confira BigDecimal, ele lida com problemas em lidar com aritmética de ponto flutuante assim.

A nova chamada ficaria assim:

term[number].coefficient.add(co);

Use setScale () para definir o número de precisão casa decimal para ser usado.

Por que não usar o método round () da classe Math?

// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4

Se você não tem nenhuma outra do que usar valores double escolha, pode usar o código abaixo.

public static double sumDouble(double value1, double value2) {
    double sum = 0.0;
    String value1Str = Double.toString(value1);
    int decimalIndex = value1Str.indexOf(".");
    int value1Precision = 0;
    if (decimalIndex != -1) {
        value1Precision = (value1Str.length() - 1) - decimalIndex;
    }

    String value2Str = Double.toString(value2);
    decimalIndex = value2Str.indexOf(".");
    int value2Precision = 0;
    if (decimalIndex != -1) {
        value2Precision = (value2Str.length() - 1) - decimalIndex;
    }

    int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
    sum = value1 + value2;
    String s = String.format("%." + maxPrecision + "f", sum);
    sum = Double.parseDouble(s);
    return sum;
}

Não desperdice seu efford usando BigDecimal. Em 99,99999% dos casos você não precisa dele. java duplo tipo é de cource aproximada mas em quase todos os casos, é suficientemente preciso. Mente que o seu tem um erro no dígito significativo 14º. Esta é realmente desprezível!

Para começar a usar a saída agradável:

System.out.printf("%.2f\n", total);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top