[Resolvido] Uso de BigDecimal no Construtor

Pessoal,

Vejam se podem me ajudar por favor. Estou com o seguinte problema: Minha classe precisa receber alguns valores em decimais e para não usar o double e ter problema de precisão, decidi usar o BigDecimal.

Havia refatorado meu construtor para receber meus parâmetros BigDecimal dessa forma:

public Candlestick(BigDecimal abertura, BigDecimal fechamento, BigDecimal minimo, BigDecimal maximo, BigDecimal volume, Calendar data) { if (abertura == null || fechamento == null || minimo == null || maximo == null || volume == null || data == null) { throw new IllegalArgumentException( "Nenhum valor pode ser nulo!"); } else if (minimo.compareTo(maximo)>0) { throw new IllegalArgumentException( "Valor mínimo não pode ser maior que o valor máximo"); } this.abertura = abertura; this.fechamento = fechamento; this.minimo = minimo; this.maximo = maximo; this.volume = volume; this.data = data; }

Mas desse jeito, toda vez que fosse instanciar essa classe, teria que passar vários objetos BigDecimal. Então fiz um outro construtor para receber valores double, e transformar em BigDecimal. Segue:

public Candlestick(double abertura, double fechamento, double minimo, double maximo, double volume, Calendar data) { if (abertura < 0 || fechamento < 0 || minimo < 0 || maximo < 0 || volume < 0) { throw new IllegalArgumentException( "Nenhum valor pode ser negativo!"); } else if (data == null) { throw new IllegalArgumentException( "Data é um campo obrigatório"); } else if (minimo > maximo) { throw new IllegalArgumentException( "Valor mínimo não pode ser maior que o valor máximo"); } this.abertura = BigDecimal.valueOf(abertura); this.fechamento = BigDecimal.valueOf(fechamento); this.minimo = BigDecimal.valueOf(minimo); this.maximo = BigDecimal.valueOf(maximo); this.volume = BigDecimal.valueOf(volume); this.data = data; }

Gostaria de saber qual a melhor abordagem. Manter os 2 construtores? Deixar apenas o que recebe BigDecimal? Ou alguma outra abordagem?

Ah, e já ia me esquecendo, todo os getter dessa classe, devolvem um BigDecimal.

Obrigado pela atenção

Cara sinceramente não entendi o problema de passar BigDecimal no contrutor…

manter 2 metodos construtores é uma boa, dai vc utiliza dependendo da sua necessidade

Eu deixaria os dois construtores, não vejo problema nenhum nisso, pelo q eu eu entendi tu não queria usar o construtor com BigDecimal pelo fato de ficar um pouco grande a invocação do mesmo por exemplo

new Construtor(new BigDecimal(1), new BigDecimal(2), new BigDecimal(3), ...)

agora imagina que tu tem só o construtor com double e tu tem variaveis BigDecimal sendo tratadas, então tu iria passar pelo mesmo problema

// decimais tratados
a = new BigDecimal(1);
b = new BigDecimal(1);
c = new BigDecimal(1);

// chamada do construtor
new Construtor(a.valueOf(1), b.valueOf(2), c.valueOf(3), ...

Retificando, eu manteria os dois construtores!

Olá,

Por tudo que há de sagrado, não deixe os construtores com double!
rsrsrs

O motivo é o seguinte: os valores double, no momento em que são fornecidos ao construtor, já possuem os problemas de precisão característicos dos números de ponto flutuante. O BigDecimal criado a partir dele “herda” essa imprecisão.

Para comprovar isso, experimente rodar o seguinte código:

[code]class TesteBigDec {
public static void main(String[] args) {
Somador s = new Somador(1.1, 0.1);
System.out.println(“1.0 + 0.1? Acho que vai dar 1.2 - vamos ver…”);
System.out.println(s.somar());
}
}

class Somador {
BigDecimal num1;
BigDecimal num2;

public Somador(double dbl1, double dbl2) {
	this.num1 = new BigDecimal(dbl1);
	this.num2 = new BigDecimal(dbl2);
}

BigDecimal somar() {
	return num1.add(num2);
}

}[/code]

Como disse o colega acima: se precisa de um construtor de conveniência, passe os valores como String.

[quote=wellington.nogueira]Sinceramente, eu não gosto de trabalhar com double nem mesmo para passar para um BigDecimal (especialmente se esse double já foi matematicamente manipulado).

Se for para fazer algo do tipo, prefiro trabalhar diretamente com uma String e passar a String para o BigDecimal (mas nesse caso, tem-se que garantir que o valor passado está de acordo com o padrão para double.

Quanto a teu construtor, sugeriria manter os dois, mas no construtor que recebe os doubles, mudaria a implementação:

[code]public Candlestick(double abertura, double fechamento, double minimo,
double maximo, double volume, Calendar data) {
if (abertura < 0 || fechamento < 0 || minimo < 0 || maximo < 0
|| volume < 0) {
throw new IllegalArgumentException(
“Nenhum valor pode ser negativo!”);
} else if (data == null) {
throw new IllegalArgumentException(
“Data é um campo obrigatório”);
} else if (minimo > maximo) {
throw new IllegalArgumentException(
“Valor mínimo não pode ser maior que o valor máximo”);
}

    this(new BigDecimal (abertura),new BigDecimal (fechamento), new BigDecimal (minimo),  new BigDecimal (maximo), new BigDecimal (volume), data);
}  [/code]

[/quote]

Então, dessa forma, eu passo o double no construtor do BigDecimale e assim não funciona muito bem. Se eu usar o método valueOf, ele passa para String e cria o BigDecimal.
Segue post no blog da caelum, nos comentários, ele discutem isso

[quote=wellington.nogueira]
Mas uma coisa que achei interessante é que as verificações de cada um de seus construtores são diferentes.
No que recebe double, você não permite valores negativos, algo que não é testado no contrutor que recebe os BigDecimais.[/quote]

Realmente, acabei esquecendo. :oops:

Valeu

[quote=gomesrod]Olá,

Por tudo que há de sagrado, não deixe os construtores com double!
rsrsrs

O motivo é o seguinte: os valores double, no momento em que são fornecidos ao construtor, já possuem os problemas de precisão característicos dos números de ponto flutuante. O BigDecimal criado a partir dele “herda” essa imprecisão.

Para comprovar isso, experimente rodar o seguinte código:

[code]class TesteBigDec {
public static void main(String[] args) {
Somador s = new Somador(1.1, 0.1);
System.out.println(“1.0 + 0.1? Acho que vai dar 1.2 - vamos ver…”);
System.out.println(s.somar());
}
}

class Somador {
BigDecimal num1;
BigDecimal num2;

public Somador(double dbl1, double dbl2) {
	this.num1 = new BigDecimal(dbl1);
	this.num2 = new BigDecimal(dbl2);
}

BigDecimal somar() {
	return num1.add(num2);
}

}[/code]

Como disse o colega acima: se precisa de um construtor de conveniência, passe os valores como String.
[/quote]

Muito bom cara. Fiz uma alteração no seu código.

Troquei essas linhas

this.num1 = new BigDecimal(dbl1);
this.num2 = new BigDecimal(dbl2);

por essas

this.num1 = BigDecimal.valueOf(dbl1); this.num2 = BigDecimal.valueOf(dbl2);

Você verá que vai funcionar!

Valeu pela ajuda

Entendi cara, valeu!

Sinceramente, eu não gosto de trabalhar com double nem mesmo para passar para um BigDecimal (especialmente se esse double já foi matematicamente manipulado).

Se for para fazer algo do tipo, prefiro trabalhar diretamente com uma String e passar a String para o BigDecimal (mas nesse caso, tem-se que garantir que o valor passado está de acordo com o padrão para double.

Quanto a teu construtor, sugeriria manter os dois, mas no construtor que recebe os doubles, mudaria a implementação:

[code]public Candlestick(double abertura, double fechamento, double minimo,
double maximo, double volume, Calendar data) {
if (abertura < 0 || fechamento < 0 || minimo < 0 || maximo < 0
|| volume < 0) {
throw new IllegalArgumentException(
“Nenhum valor pode ser negativo!”);
} else if (data == null) {
throw new IllegalArgumentException(
“Data é um campo obrigatório”);
} else if (minimo > maximo) {
throw new IllegalArgumentException(
“Valor mínimo não pode ser maior que o valor máximo”);
}

    this(new BigDecimal (abertura),new BigDecimal (fechamento), new BigDecimal (minimo),  new BigDecimal (maximo), new BigDecimal (volume), data);
}  [/code]

Mas uma coisa que achei interessante é que as verificações de cada um de seus construtores são diferentes.
No que recebe double, você não permite valores negativos, algo que não é testado no contrutor que recebe os BigDecimais.

A sim, esqueci de complementar, double não funciona direito com o operador decomparação == <= e >= (os dois últimos na condição de igualdade).

Legal que o valueOf transforme o double, primeiro em String e depois sim para BigDecimal. Ainda assim, se houver manipulação matemática de doubles antes de passá-lo ao valueOf, pode ter problemas (minimizados, mas não acredito que elimine-o).

Prefiro trabalhar diretamente com BigDecimal e Strings a me arriscar em manipulações com double (salvo alguma exceção muito convincente).

No mesmo link que você passou, há um exemplo de uso com valueOf que não satisfaz a necessidade.

[code] double d = 1.0/3.0;

	double d2 = 0.3333333333333333333333333333333333333333333333333333333;
	
	BigDecimal bd = BigDecimal.valueOf(d);
	BigDecimal bd2 = BigDecimal.valueOf(d2);
	BigDecimal bdS = new BigDecimal("0.3333333333333333333333333333333333333333333333333333333");
	

	System.out.println("Imprimindo double");
	System.out.println(d);
	System.out.println(d2);
	
	System.out.println("Imprimindo BigDecimal");
	System.out.println(bd);
	System.out.println(bd2);
	
	System.out.println(bdS);
	
	DecimalFormat f = new DecimalFormat("0.0000000000000000000000000000000000000000000000000000000");
	System.out.println("Imprimindo double através de um Format");
	System.out.println(f.format(d));
	System.out.println(f.format(d2));
	
	System.out.println("Imprimindo BigDecimal através de um Format");
	System.out.println(f.format(bd));
	System.out.println(f.format(bd2));
	System.out.println(f.format(bdS));

[/code]O único resultado que satisfez o que era esperado foram os System.out da variável bdS.

Imprimindo double 0.3333333333333333 0.3333333333333333 Imprimindo BigDecimal 0.3333333333333333 0.3333333333333333 0.3333333333333333333333333333333333333333333333333333333 Imprimindo double através de um Format 0,3333333333333333000000000000000000000000000000000000000 0,3333333333333333000000000000000000000000000000000000000 Imprimindo BigDecimal através de um Format 0,3333333333333333000000000000000000000000000000000000000 0,3333333333333333000000000000000000000000000000000000000 0,3333333333333333333333333333333333333333333333333333333

Outro exemplo:

[code] double d = 4.0/9.0;
double d2 = 0.4444444444444444449444444444444444444444444444444444444;
// ^
BigDecimal bd = BigDecimal.valueOf(d);
BigDecimal bd2 = BigDecimal.valueOf(d2);

	System.out.println(d == d2);
	System.out.println(bd == bd2);
	System.out.println(bd.equals(bd2));

[/code]
Percebe que tem um 9 no meio dos 4?, a saída resultante foi:

true //Eles são iguais????? false //Certo. Estou comparando objetos diferentes. true //Eles são iguais?????
São detalhes como esse que me forçam a evitar o double… O BigDecimal TAMBÉM não é perfeito, mas acho que ele é mais adequado.