Pergunta

Como é que BigDecimal de Java pode ser este doloroso?

Double d = 13.3D;

BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));


System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());

RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3

Existe alguma situação onde Resultado 1 seria desejável? Eu sei que Java 1.5 mudou o método toString() mas foi essa a consequência pretendida?

Também percebo que BigDecimal tem doubleValue() etc, mas a biblioteca que eu estou trabalhando com prestativamente usa um toString() e eu não posso mudar isso: - (

Felicidades.

Foi útil?

Solução

Bem, a API faz endereço essa aparente inconsistência na BigDecimal(double val) construtor:

  1. Os resultados desta construtor pode ser um pouco imprevisível. Alguém pode assumir que escrever novo BigDecimal (0,1) em Java cria um BigDecimal que é exatamente igual a 0,1 (um valor fora de escala de 1, com uma escala de 1), mas na verdade é igual para 0,1000000000000000055511151231257827021181583404541015625. Isso ocorre porque 0.1 não pode ser representado exatamente como um duplo (ou, para essa matéria, como uma fração binária de qualquer comprimento finito). Assim, o valor que está a ser transmitido para o construtor não é exatamente igual a 0,1, as aparências, não obstante.

  2. O construtor String, por outro lado, é perfeitamente previsível: escrever novo BigDecimal ( "0.1") cria um BigDecimal que é exatamente igual a 0,1, como seria de esperar. Portanto, é geralmente recomendado que o construtor corda ser utilizado em preferência a este.

  3. Quando um casal deve ser utilizado como uma fonte para um BigDecimal , nota que Este construtor fornece uma exata conversão; ele não dá o mesmo resultar como converter a dupla a um String usando o Double.toString método (duas vezes) e em seguida, usando o BigDecimal (String) construtor. Para obter esse resultado, uso a estática valueOf método (duplo) .

Moral da história: A dor parece, o uso apenas auto-infligido new BigDecimal(String val) ou BigDecimal.valueOf(double val) vez =)

Outras dicas

Seu problema não tem nada a ver com BigDecimal, e tudo com Double, que não pode representar 13,3 precisão, uma vez que utiliza frações binárias internamente.

Portanto, o seu erro é introduzido na primeira linha. O primeiro BigDecimal simplesmente preserva-lo, enquanto String.valueOf() faz algum arredondamento de peixe que faz com que o segundo a ter o conteúdo desejado, praticamente com a sorte.

Você pode querer informar-se sobre como valores de ponto flutuante são implementadas ( IEEE 754-1985 ). E de repente, tudo se tornará cristalina.

Isto não é culpa do BigDecimal - é culpa do double. BigDecimal está representando com precisão o exata valor de d. String.valueOf é apenas mostrar o resultado de algumas casas decimais.

As fracções representado com tipos número binário (isto é. double, float) não podem ser armazenados com precisão nesses tipos.

    Double d = 13.3;        
    BigDecimal bdNotOk = new BigDecimal(d);
    System.out.println("not ok: " + bdNotOk.toString());

    BigDecimal bdNotOk2 = new BigDecimal(13.3);
    System.out.println("not ok2: " + bdNotOk2.toString());

    double x = 13.3;
    BigDecimal ok = BigDecimal.valueOf(x);
    System.out.println("ok: " + ok.toString());

    double y = 13.3;
    // pretty lame, constructor's behavior is different from valueOf static method
    BigDecimal bdNotOk3 = new BigDecimal(y);
    System.out.println("not ok3: " + bdNotOk3.toString());

    BigDecimal ok2 = new BigDecimal("13.3");
    System.out.println("ok2: " + ok2.toString());

    Double e = 0.0;
    for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
    System.out.println("not ok4: " + e.toString()); // should be 1


    BigDecimal notOk5 = BigDecimal.valueOf(e);
    System.out.println("not ok5: " + notOk5.toString()); // should be 1

    /* 
     * here are some fractions that can be represented exactly in binary:
     * 0.5   = 0.1   = 1 / 2
     * 0.25  = 0.01  = 1 / 4
     * 0.75  = 0.11  = 3 / 4
     * 0.125 = 0.001 = 1 / 8
     */

saída:

not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999

Apenas uso BigDecimal.valueOf(d) ou new BigDecimal(s) .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top