How to format a double number to N decimals without rounding?

Asked

Viewed 164 times

-2

I have a type value double and I would like to format it so that it gets 3 decimals only, unfurnished. For this, I’m trying to utilize the String.format as follows:

float value = Float.parseFloat(String.format("%.3f", myDoubleValue));

The problem is that when I run this code, the following error is generated:

Exception in thread "main" java.lang.NumberFormatException: For input string: "-23,319"
        at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
        at sun.misc.FloatingDecimal.parseFloat(Unknown Source)
        at java.lang.Float.parseFloat(Unknown Source)

What am I doing wrong? Is there an "ideal" way to perform this formatting? I know there are other ways to format decimals - like DecimalFormat - but they round up the number, and that’s not what I want.

Detail: for this project, I need as much optimization as possible. So I would like a "lightweight" solution to format the number.

  • You want to generate a string or a float with the first three decimal places? Although it seems, it is not the same thing, because "format" usually means "turn into string", can change the decimal separator and thousands, etc. Inclusive, this was the error: String.format uses the default locale of the JVM, which in your case is one that uses the comma (I explained this already in your other question), and parseFloat only recognizes the point.

  • Anyway, Decimalformat has the option to change the form of rounding, test there (I think it’s the DOWN, but see there). But if you want a float, I find it easier to do the same: https://answall.com/a/459884/112052. (this is in Javascript, but the idea is the same)

  • Anyway 2: String.format round yes :-)

2 answers

1


they round up the number, and that’s not what I wish

Good, String.format also round:

// quarta casa decimal é >= 5, arredonda para cima
System.out.println(String.format("%.3f", 1.2225)); // 1,223

// quarta casa decimal é < 5, arredonda para baixo
System.out.println(String.format("%.3f", 1.2224)); // 1,222

I believe that when you say "do not round up," you mean that the result should be 1,222 for both cases, right?

It is worth remembering that the decimal separator may be the comma or the point, depending on the default locale of the JVM, as already explained in another question. But when you use String.format, rounding is always done, including this is said in documentation:

the value will be rounded using the round half up Algorithm

And the aforementioned rounding algorithm is described here.


Anyway, what exactly do you want to do?

If you want to get one float only with the first 3 decimal places, then you just need to use good old math:

// manter apenas as N primeiras casas decimais
static float manterCasas(float valor, int qtdCasas) {
    float fator = (float) Math.pow(10, qtdCasas);
    return (float) (Math.floor(valor * fator) / fator);
}

public static void main(String[] args) {
    // sempre mantém as 3 primeiras casas, sem arredondar
    System.out.println(manterCasas(1.2225f, 3)); // 1.222
    System.out.println(manterCasas(1.2224f, 3)); // 1.222
}

The idea to keep the N first houses is to multiply the number by 10N, round down and then divide by 10N (as explained above here and here).


But if you want one String with the first 3 decimal places, controlling even the decimal separator, there you can use DecimalFormat even:

public static void main(String[] args) {
    System.out.println(formatar(1.2225f, 3)); // 1.222
    System.out.println(formatar(1.2224f, 3)); // 1.222
}

static String formatar(float valor, int qtdCasas) {
    NumberFormat fmt = DecimalFormat.getNumberInstance();
    // limitar quantidade de casas decimais
    fmt.setMaximumFractionDigits(qtdCasas);
    // arredondar para baixo tem o mesmo efeito que "não arredondar a última casa decimal"
    fmt.setRoundingMode(RoundingMode.DOWN);
    return fmt.format(valor);
}

Remembering that the decimal separator can be the point or the comma, according to the default locale JVM. If you want a specific one, you can pass the locale to the NumberFormat. Ex:

// usar locale pt-BR (português do Brasil) para o separador decimal ser a vírgula
NumberFormat fmt = DecimalFormat.getNumberInstance(new Locale("pt", "BR"));

Remembering that by default the thousands separator is also placed (1000 is formatted as 1.000). If you want to disable that, do it fmt.setGroupingUsed(false);.

Another detail is that if the number has less than 3 decimal places, not all of them will be shown. If you want to always show 3, just add fmt.setMinimumFractionDigits(qtdCasas);.


Finally, it is worth remembering not to confuse a number with its representation. A number like 1,5 is only a numerical value... that has no format in itself (it is only a concept, an idea: it represents a certain value, only that). But this same number can be represented in several ways: as 1.5, 1,5, um e meio, 000001.500, etc..

That is, a numerical value stored in a float (or int, or double, etc) has no format, but it can be converted to a string that represents this value in different ways. When I print the number, it is converted to a string in a given format (with println, for example, I do not control the format, but with format and DecimalFormat yes). So it is important to know what you need: change the numerical value (generate another float only with the first 3 decimal places) or just generate a string that represents the number with the first 3 houses.

0

Try to use Numberformat

   Double variavel_qualquer = 5.5641;
   NumberFormat formatter = new DecimalFormat("#0.00");     
   System.out.println(formatter.format(4.0));
   System.out.println(formatter.format(variavel_qualquer));

Answer:

4.00

5.56

Browser other questions tagged

You are not signed in. Login or sign up in order to post.