Identificar quantos caracteres existem depois do ponto em um Double

Boa noite…

o problema é bem simples (eu acho).

eu queria saber se tem alguma forma de eu saber quantos caracteres existem depois do ponto.

exemplo:
double A = 25.49;

no exemplo acima, a variável A, possui, depois do ponto, 2 caracteres.

tem como fazer isso?

Tem que ser em double mesmo?
Ele não é muito preciso após o ponto.

Se fosse uma String poderia fazer um split no ponto e contar quantos chars tem no index 1 do array.

Preciso ele é, mas com alguns valores ele pode não ser exato.

“Preciso” e “exato” não são sinônimos? :grinning:

Não.

Pode até ser em contextos informais, no dia a dia, mas para números de ponto flutuante, são coisas diferentes.

Precisão é - de forma resumida - a quantidade de casas decimais que o tipo suporta.

Exatidão é a capacidade de representar um valor da forma mais… exata possível.

Por causa da forma como os números de ponto flutuante são organizados internamente (seguindo a norma IEEE 754), nem todos os valores podem ser representados com exatidão. Por exemplo:

double x = 0.0;
for (int i = 0; i < 100; i++){
  x += 0.1;
}
System.out.println(x); // 9.99999999999998

Somei 0.1 cem vezes, o resultado deveria ser 10, mas foi 9.99999999999998. Isso acontece porque o 0.1 é um dos muitos valores que não podem ser representados com exatidão, e sim de forma aproximada.

E por mais que aumente a precisão (mais casas decimais), não adianta, o valor continuará não sendo exato.


Dito isso, se está lidando com valores numéricos, não existem caracteres, somente dígitos. Então você teria que transformar o valor em string para depois analisá-lo. Uma alternativa é usar BigDecimal:

double d = 25.49;
System.out.println(new BigDecimal(String.valueOf(d)).scale()); // 2 

Até daria para usar Double.toString e contar os caracteres depois do ponto, o problema é que valores muito grandes resultam em uma string em notação científica (10 milhões resulta na string 1.0E7), então usar BigDecimal acaba tratando esses casos também.

Lembrando que, caso não haja nenhuma casa decimal, scale pode retornar um número negativo, conforme consta na documentação. Obs: na verdade, para números inteiros, scale não é o ideal. Mais tarde, se der tempo, vejo uma solução mais geral…

2 curtidas

São sinônimos tanto no português quanto no inglês.

Acontece que “precision” tem o significado adicional de “The number of significant digits to which a value has been reliably measured.”

No contexto da programação são diferentes e eu usei o sinônimo errado.

Complementando o post anterior, eu disse que BigDecimal::scale não funciona bem para números sem casas decimais. Então uma solução mais geral é transformar o número em string e ver quantos dígitos tem depois do ponto.

Só que transformar o double em string diretamente nem sempre funciona. Por exemplo, Double.toString(10000000) resulta em 1.0E7 (números menores que 0.001 e maiores que 10 milhões geram uma string em notação científica, então usar a quantidade de caracteres depois do ponto não serve para esses casos).

Então o jeito é forçar que a string nunca fique em notação científica. Uma forma de fazer é usar java.text.NumberFormat:

// locale en_US usa o ponto como separador decimal
NumberFormat fmt = DecimalFormat.getNumberInstance(new Locale("en", "US"));
fmt.setGroupingUsed(false); // não usar separador de milhares
fmt.setMinimumFractionDigits(0); // pode ter zero casas decimais
fmt.setMaximumFractionDigits(Integer.MAX_VALUE);
double d = // um número qualquer
String s = fmt.format(d);
int i = s.indexOf('.'); // procura a posição do ponto
int casasDecimais;
if (i < 0) { // se não tem ponto, não tem casas decimais
    casasDecimais = 0;
} else { // se tem ponto, quantidade de casas é o tamanho da string menos a posição posterior ao ponto
    casasDecimais = s.length() - i - 1;
}

Fazendo um teste:

NumberFormat fmt = DecimalFormat.getNumberInstance(new Locale("en", "US"));
fmt.setGroupingUsed(false);
fmt.setMinimumFractionDigits(0);
fmt.setMaximumFractionDigits(Integer.MAX_VALUE);
double nums[] = new double[] { 0, 1, 100, 10000000, 10000000.1, 12.345, -12.34, -2, 0.01, 0.0009, 1.12345678901, Double.MIN_VALUE, Double.MAX_VALUE };
for (double d : nums) {
    String s = fmt.format(d);
    int i = s.indexOf('.'); // procura a posição do ponto
    int casasDecimais;
    if (i < 0) { // se não tem ponto, não tem casas decimais
        casasDecimais = 0;
    } else { // se tem ponto, quantidade de casas é o tamanho da string menos a posição posterior ao ponto 
        casasDecimais = s.length() - i - 1;
    }
    System.out.printf("%4d %s\n", casasDecimais, s);
}

Saída:

   0 0
   0 1
   0 100
   0 10000000
   1 10000000.1
   3 12.345
   2 -12.34
   0 -2
   2 0.01
   4 0.0009
  11 1.12345678901
 325 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049
   0 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1 curtida