Formatando BigDecimal's?

Olá pessoal,

estou com o seguinte problema: tenho alguns números em notação exponencial

1.23E+12
2.34E+13
3.45E+14

Preciso colocá-los todos na mesma ordem de grandeza, ou seja:

1.23E+12
23.4E+12
345E+12

ou

0.0123E+14
0.234E+14
3.45E+14

Alguém sabe como posso fazer essa formatação?

Desde já, agradeço o apoio.

Att
Renan

Você quer a “notação de engenharia”, onde os expoentes são múltiplos de 3. No seu caso, seriam formatados como:

1.23E+12
23.4E+12
345.0E+12

http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

Na verdade, não.
Eu só preciso que todos os valores estejam com o mesmo valor de expoente, pois irei passá-los como entrada para uma biblioteca de geração de gráficos, que não entende valores BigDecimal.
Por exemplo: se eu tiver a tabela

Eixo X ---- Eixo Y
1 ---- 1.23E+17
2 ---- 2.34E+17
3 ---- 4.56E+18
4 ---- 2.34E+19

Eu deveria converter todos os valores para a mesma base pois senão o 4o valor, que é muito maior que o primeiro, irá ser plotado erradamente.

Eixo X ---- Eixo Y
1 ---- 1.23E+17
2 ---- 2.34E+17
3 ---- 45.60E+17
4 ---- 234.00E+17

Outra solução que me atenderia seria ter uma biblioteca para geração de gráficos que entenda esse tipo de dado (BigDecimal).
Obrigado.

Att
Renan

[quote=renanc]Na verdade, não.
Eu só preciso que todos os valores estejam com o mesmo valor de expoente, pois irei passá-los como entrada para uma biblioteca de geração de gráficos, que não entende valores BigDecimal.
Por exemplo: se eu tiver a tabela

Eixo X ---- Eixo Y
1 ---- 1.23E+17
2 ---- 2.34E+17
3 ---- 4.56E+18
4 ---- 2.34E+19

Eu deveria converter todos os valores para a mesma base pois senão o 4o valor, que é muito maior que o primeiro, irá ser plotado erradamente.

Eixo X ---- Eixo Y
1 ---- 1.23E+17
2 ---- 2.34E+17
3 ---- 45.60E+17
4 ---- 234.00E+17

Outra solução que me atenderia seria ter uma biblioteca para geração de gráficos que entenda esse tipo de dado (BigDecimal).
Obrigado.
[/quote]

Vai continuar plotanto do mesmo jeito porque o numero é o mesmo!!!

0.01 e 1E-2 são o mesmo número.

O seu problema não é de formatação. É de entender que o plot é o plot e o problema está na escala do seu gráfico, não nos numeros.
Experimente usar uma escala logaritmica no eixo do plot do gráfico

sergiotaborda, obviamente os números são os mesmos. Não é essa a questão.

A biblioteca que conheço não aceita números no formato ###.##E#. Tampouco, ela é “inteligente” o suficiente para plotar em escala logarítmica sem que sejam necessárias várias alterações (como as que estou tentando fazer). Além disso, por motivos óbvios, não posso simplesmente converter esses números de BigDecimal para long, por exemplo, para fazer o plot.

Se eu conseguisse passar valores tais como

Eixo X ---- Eixo Y
1 ---- 123000000000000000
2 ---- 234000000000000000000
3 ---- 45600000000000000000000
4 ---- 2340000000000000000000000
ou
Eixo X ---- Eixo Y
1 ---- 0,0000000000000000000123
2 ---- 0,0000000000000000000000234
3 ---- 0,000000000000000000000000456
4 ---- 0,00000000000000000000000000234

para a função de plot, eu já o teria feito.
Se eu conseguir colocar todos os números na mesma base, aí sim eu posso isolar os valores do expoente e plotar os números dentro de um gráfico na mesma escala.
Exemplo:

Eixo X ---- Eixo Y ---- Conversão ----- Valor plotado (^17)
1 ---- 1.23E+17 ---- 1.23E+17 ---- 1.23
2 ---- 2.34E+17 ---- 2.34E+17 ---- 2.34
3 ---- 4.56E+18 ---- 45.60E+17 ---- 45.6
4 ---- 2.34E+19 ---- 234.00E+17 ---- 234.0

Então, voltando à questão inicial, alguém sabe como posso resolver esse problema ou tem alguma sugestão, exemplo, etc?

Mais uma vez, agradeço a colaboração de todos.

[]s
Renan

[quote=renanc]sergiotaborda, obviamente os números são os mesmos. Não é essa a questão.

A biblioteca que conheço não aceita números no formato ###.##E#. Tampouco, ela é “inteligente” o suficiente para plotar em escala logarítmica sem que sejam necessárias várias alterações (como as que estou tentando fazer). Além disso, por motivos óbvios, não posso simplesmente converter esses números de BigDecimal para long, por exemplo, para fazer o plot.
[/quote]

Tem algo muito errado no seu pensamento.
BigDecimal pode ser convertido para double. Vc pode perder precisão, mas em tese um double pode representar qualquer numero.
A biblioteca não aceita String., certo ? Aceita BigDecimal ou double (provavelmente double) . Portanto a formatação é irrelevante.
Se ela não inteligente para usar escala logaritmica, faça-a vc. Basta aplicar o logaritmo aos valores de Y.

Mais uma vez, isto não tem que ver com formatação.
Se quiser colocar todos na mesma escala use BigDecimal.setScale(int newScale) - o numero permancerá o mesmo, mas vc ainda terá que dividir por dez elevado à escala.

sergiotaborda, sequer o conheço, mas sei que se está aqui é porque está querendo ajudar. No entanto, ainda não consegui entender onde está a falha no meu pensamento. Mas isso realmente não vem ao caso. Sei que a falha é minha em não conseguir te explicar o que para mim está bem claro.

Aí é que está a questão. Essas tabelas e gráficos que estou fazendo são para minha tese de doutorado e, nesse caso, precisão é tudo. O código escrito por baixo dessa camada Java, é todo escrito em Fortran para você ter ideia.
Quando estamos lidando com números grandes, ok.
Mas quando lidamos com números muito pequenos, aí complica.
doubles estão beeeem longe de poder “representar qualquer número”, como você disse, mesmo em tese.
Como você bem sabe, são infintos números entre 0 e 1, mas apenas um pequeno conjunto de bits para tentar representá-los.

É o que estou tentando fazer. Escrevi um trecho de código aqui rapidinho para mostrar o meu problema com a conversão de BigDecimal’s.

[code]import java.math.BigDecimal;

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

//Ok. Dá para eu me virar tranquilo com isso.
BigDecimal trinity = new BigDecimal(“9.18345E+18”);
double neo = trinity.doubleValue();
System.out.println("\n BigDecimal value :" + trinity);
System.out.format(“method generating result: %f%n”, neo);

//Aqui está meu problema
trinity = new BigDecimal(“9.18765E-10”);
neo = trinity.doubleValue();
System.out.println("\n BigDecimal value :" + trinity);
System.out.format(“method generating result: %f%n”, neo);

}
}[/code]

Saída:
BigDecimal value :9.18345E+18
method generating result: 9183450000000000000,000000

BigDecimal value :9.18765E-10
method generating result: 0,000000

Ou seja, se todos os números pequenos forem mapeados para zero, não tem como eu plotar. Por isso, pensei naquela alternativa que propus.
Espero que com esse exemplo, tenha ficado mais claro o meu problema.

Se você, ou mais alguém, souber como posso “consertar” essa conversão, ou como fazer o que eu estava planejando inicialmente (mudar todos para a mesma base), eu agradeceria bastante.

[]s
Renan

[quote=renanc][quote=sergiotaborda]
Tem algo muito errado no seu pensamento.
[/quote]

sergiotaborda, sequer o conheço, mas sei que se está aqui é porque está querendo ajudar. No entanto, ainda não consegui entender onde está a falha no meu pensamento. Mas isso realmente não vem ao caso. Sei que a falha é minha em não conseguir te explicar o que para mim está bem claro.

Aí é que está a questão. Essas tabelas e gráficos que estou fazendo são para minha tese de doutorado e, nesse caso, precisão é tudo. O código escrito por baixo dessa camada Java, é todo escrito em Fortran para você ter ideia.
Quando estamos lidando com números grandes, ok.
Mas quando lidamos com números muito pequenos, aí complica.
doubles estão beeeem longe de poder “representar qualquer número”, como você disse, mesmo em tese.
Como você bem sabe, são infintos números entre 0 e 1, mas apenas um pequeno conjunto de bits para tentar representá-los.

É o que estou tentando fazer. Escrevi um trecho de código aqui rapidinho para mostrar o meu problema com a conversão de BigDecimal’s.

[code]import java.math.BigDecimal;

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

//Ok. Dá para eu me virar tranquilo com isso.
BigDecimal trinity = new BigDecimal(“9.18345E+18”);
double neo = trinity.doubleValue();
System.out.println("\n BigDecimal value :" + trinity);
System.out.format(“method generating result: %f%n”, neo);

//Aqui está meu problema
trinity = new BigDecimal(“9.18765E-10”);
neo = trinity.doubleValue();
System.out.println("\n BigDecimal value :" + trinity);
System.out.format(“method generating result: %f%n”, neo);

}
}[/code]

Saída:
BigDecimal value :9.18345E+18
method generating result: 9183450000000000000,000000

BigDecimal value :9.18765E-10
method generating result: 0,000000

Ou seja, se todos os números pequenos forem mapeados para zero, não tem como eu plotar. Por isso, pensei naquela alternativa que propus.
Espero que com esse exemplo, tenha ficado mais claro o meu problema.

Se você, ou mais alguém, souber como posso “consertar” essa conversão, ou como fazer o que eu estava planejando inicialmente (mudar todos para a mesma base), eu agradeceria bastante.
[/quote]

Vou deixar que outro alguém explique melhor, mas o ponto é:

  1. Entenda que o numero é sempre o mesmo
  2. a formatação só mostra o que vc madou mostrar e não mostra o numero verdadeiro (é por isso que aparecem só zeros, não é porque o numero é zero, é porque essas são as primeiras seis casas)
  3. para formatação verdadeira use DecimalFormat
  4. É bom explicar qual a lib de gráfico que vc está usando e qual é o código que está usando.

Experimente este código:

		BigDecimal trinity = new BigDecimal("9.18765E-10");
		double neo = trinity.doubleValue();
		System.out.println("\n BigDecimal value :" + trinity);
		
		DecimalFormat f = new DecimalFormat("0.###E00");
		
		System.out.format("method generating result: %f%n", neo); 
		System.out.println("method generating result using format:" + f.format(neo));
  1. Sim, claro, mas a máquina tem as limitações de representação. Infelizmente não estou com tempo aqui de te passar uns links sobre isso, mas se desejar, posso te enviar depois.
  2. Não estou com o código aqui, mas vou olhar lá e depois posto aqui.

Executei o seu código e o resultado foi “o mesmo”. Pior, ele faz arredondamento.
Fiz umas modificações em cima do seu código para mostrar a limitação e como o problema ainda persiste:

[code]import java.math.BigDecimal;
import java.text.DecimalFormat;

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

//Ok. Dá para eu me virar tranquilo com isso.
BigDecimal trinity = new BigDecimal(“9.18345E+18”);
double neo = trinity.doubleValue();
System.out.println("\n BigDecimal value :" + trinity);
System.out.format(“method generating result: %f%n”, neo);

//Aqui está meu problema
trinity = new BigDecimal(“9.187658768768687687687686876E-10”);
neo = trinity.doubleValue();
System.out.println("\n BigDecimal value :" + trinity);
DecimalFormat f = new DecimalFormat(“0.###E00”);

System.out.format(“method generating result: %f%n”, neo);
System.out.println(“method generating result using format:” + f.format(neo));

//Aqui está meu problema
trinity = new BigDecimal(“9.123456789012345678901234567890E-10”);
neo = trinity.doubleValue();
System.out.println("\n BigDecimal value :" + trinity);
f = new DecimalFormat(“0.###############################E00”);

System.out.format(“method generating result: %f%n”, neo);
System.out.println(“method generating result using format:” + f.format(neo));
}
}
[/code]

Saída:

BigDecimal value :9.18345E+18
method generating result: 9183450000000000000,000000

BigDecimal value :9.187658768768687687687686876E-10
method generating result: 0,000000
method generating result using format:9,188E-10

// Não representou em formato decimal, assim como fez com o número positivo, e ainda por cima arredondou.

BigDecimal value :9.123456789012345678901234567890E-10
method generating result: 0,000000
method generating result using format:9,123456789012345E-10

// Pelo menos agora deu para ver o limite: 15 casas decimais. Mas continua imprimindo em formato exponencial (E).

Alguém tem outra sugestão?

[]s
Renan

Minha sugestão é a seguinte. Pegue os numeros a serem plotados e os normalize (ou seja, se você tem vários números que estão por volta de 10 elevado a 12 até 10 elevado a 15, então use algum desses números como base (digamos 10 elevado a 12) e então, na legenda, indique que todos os números devem ser multiplicados por 10 elevado a 12.

Por exemplo, você tem 1.2E12, 2.4E13, 3.6E14, 5E15. Na hora de plotar o gráfico, passe os valores 1.2, 24, 360 e 5000, e na legenda ponha “X 10^12”).

Como é que você vai normalizar esses números fica como exercício (até porque não sei exatamente suas reais necessidades). Uma dica é sempre ter números com expoentes números de 3.
Outra coisa, se números muito pequenos também são significativos (por exemplo, 1E-10 é tão significativo quanto 1E+17), você deve usar escala logarítmica.

[quote=entanglement]Minha sugestão é a seguinte. Pegue os numeros a serem plotados e os normalize (ou seja, se você tem vários números que estão por volta de 10 elevado a 12 até 10 elevado a 15, então use algum desses números como base (digamos 10 elevado a 12) e então, na legenda, indique que todos os números devem ser multiplicados por 10 elevado a 12.

Por exemplo, você tem 1.2E12, 2.4E13, 3.6E14, 5E15. Na hora de plotar o gráfico, passe os valores 1.2, 24, 360 e 5000, e na legenda ponha “X 10^12”).
[/quote]

entanglement,

mas é exatamente isso que estou tentando fazer deeeeesde o começo! :smiley:

A questão é: como isolar a base do expoente? Ou como descobrir em que expoente o bigDecimal foi representado?
Se eu conseguisse isolá-los, ou descobrir qual a ordem de grandeza (expoente) de um determinado BigDecimal, eu poderia facilmente convertê-lo para uma notação comum, e aí meu problema estaria resolvido.

No seu exemplo, a questão é exatamente como descobrir que o primeiro número é E12, o segundo E13, etc, para poder fazer a transformação para 1.2, 24, etc.

I’ll keep searching for an answer. :roll:

Rode este programa. Na verdade, para descobrir o verdadeiro expoente de um BigDecimal, se ele for pequeno (ou seja, for menor que 10 elevado a 312) você pode convertê-lo para um double, e calcular o logaritmo.

import java.math.BigDecimal;
class TesteBigDecimalScale {
    public static void main (String[] args) {
	    BigDecimal[] bd = {
			new BigDecimal ("3.1400E+10"),
			new BigDecimal ("31.400E+10"),
			new BigDecimal ("31400E+10"),
			new BigDecimal ("0.31400E+10"),
			new BigDecimal ("3.1400E-7"),
			new BigDecimal ("0"),
		};
		// Note que "scale" não é a solução para o seu problema. 
		// Vai imprimir o seguinte:
// Scale of 31400000000.000000 is -6
// Scale of 314000000000.000000 is -7
// Scale of 314000000000000.000000 is -10
// Scale of 3140000000.000000 is -5
		System.out.println ("nao resolve usar scale...");
		for (BigDecimal b : bd) {
			System.out.printf ("Scale de %s == %d %n", b, b.scale());
		}
		// Nem tirar os zeros à direita vai resolver.
// Scale of 31400000000.000000 is -8
// Scale of 314000000000.000000 is -9
// Scale of 314000000000000.000000 is -12
// Scale of 3140000000.000000 is -7		
		System.out.println ("nao resolve usar skipTrailingZeros...");
		for (BigDecimal b : bd) {
			System.out.printf ("Scale de %s == %d %n", b, b.stripTrailingZeros().scale());
		}
		// Se o número não for exageradamente grande (o que deve ser seu caso, não? - não deve
		// passar de 10 elevado a 312) 
		// você pode simplesmente convertê-lo para double, e achar o logaritmo decimal. 
		// Note que quando o logaritmo é negativo, devemos acrescentar "-1". 
		// Isso é a resposta que talvez você quisesse:
		System.out.println ("Usando logaritmos...");
		for (BigDecimal b : bd) {
		    double d = b.doubleValue();
			int n = 0;
			if (d != 0) {
			    n = (int) Math.log10 (d);  // note que log de zero é um número indefinido
			    if (n < 0) n--; // é este número "n" que você quer, não?
			} 
			System.out.printf ("%s == %s E %+d %n", b, 
			    b.scaleByPowerOfTen (-n).stripTrailingZeros(),
			    n);
			System.out.printf ("%s (scientific notation) == %s %n", b, b.stripTrailingZeros().toEngineeringString());
		}
	}
}