5

I want to round my double value down to N decimal places (say, one), esentially just leaving out all the digits that follow:

0.123 #=> 0.1
0.19  #=> 0.1
0.2   #=> 0.2

This question has been brought up numerous times, for example here and here. The recommended approach is to use BigDecimal and then scale it, in particular to avoid expensive converting to string and back. The rounding mode I need is apparently RoundingMode.DOWN.

So the method is something like this:

static double truncate(double value, int places) {
    return new BigDecimal(value)
        .setScale(places, RoundingMode.DOWN)
        .doubleValue();
}

But, due to the loss of precision, it returns somewhat unexpected results:

truncate(0.2, 1) #=> 0.2
truncate(0.3, 1) #=> 0.2
truncate(0.4, 1) #=> 0.4

truncate(0.2, 3) #=> 0.2
truncate(0.3, 3) #=> 0.299
truncate(0.4, 3) #=> 0.4

This begs for two questions:

  1. Is it how it's supposed to work for 0.3? Why would there be a loss of precision in this case? Doesn't it defeat the whole purpose of having BigDecimal?

  2. How do I correctly truncate my values?

Thanks.

Community
  • 1
  • 1
SqueezyMo
  • 1,606
  • 2
  • 21
  • 34

4 Answers4

4

"in particular to avoide the expensive conversion to String and back" - This is exactly what avoids the loss of precision. You can't get arbitrary precision for free.

If you need arbitrary precision then you should not use double but instead do:

static String truncate(String value, int places) {
return new BigDecimal(value)
    .setScale(places, RoundingMode.DOWN)
    .stripTrailingZeros()
    .toString()
}
user1886323
  • 1,179
  • 7
  • 15
  • Of course if you really need to avoid the "expensive" conversion from string, then you should write a method yourself that just manipulates a String e.g. using value.indexOf(".") + n – user1886323 Aug 01 '15 at 14:52
  • This doesn't really work, it just appears to work in most cases. `truncate("0.1234567891012345678", 18)` is wrong for example. – Paul Boddington Aug 01 '15 at 14:54
  • You're right - I have modified the answer to also return a String instead of double. – user1886323 Aug 01 '15 at 15:03
  • Yes, seeding `BigDecimal` with a `double` doesn't look like a bright idea. I really need to educate myself on the floating point representation. Thanks. – SqueezyMo Aug 01 '15 at 15:06
  • Looks good now. I edited my answer, mentioning that this is the correct approach. – Paul Boddington Aug 01 '15 at 15:10
  • 2
    It's worth noting that BigDecimal.valueOf(0.3).toString() = "0.3" instead of "0.2999..." because it uses the double's canonical string representation anyway. See http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html – user1886323 Aug 01 '15 at 15:15
1

Multiply the number with pow(10,n) and store it in integer type & then again divide it by pow(10,n)

  • can you explain a bit – SSH Aug 01 '15 at 14:32
  • yeah, see if you have 0.3124 and you want to truncate it to 0.31. The you should multiply it with 100, it will make it 31.24 which will be 31 due to conversion to int. And then when you divide it with 100 again(with casrting) it will be 0.31 – Priyanshu Jha Aug 01 '15 at 14:35
  • This solution has been discussed [here](https://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places/2808648#2808648), and it really doesn't work well with big values. It suits me fine, I just wondered if there's a more general approach. – SqueezyMo Aug 01 '15 at 14:48
  • Oh Sorry @SqueezyMo I haven't seen that. – Priyanshu Jha Aug 01 '15 at 14:50
1

Truncating a double to N decimal places is not really a question that makes any sense, because simple decimal numbers (e.g. 0.3) cannot be represented as doubles.

If you want to know the true value of the double 0.3 you can do

System.out.println(new BigDecimal(0.3));

and you will see that it is really

0.299999999999999988897769753748434595763683319091796875

So 0.299 is the correct answer.

This question only really makes sense using BigDecimal and giving the answer as a BigDecimal or a String, not a double. @user1886323's answer shows how to do this.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
1

I know this is an ancient post, but i just came up with a simple solution to it:

Have a second double which recieves the modulo of the number you are truncating, and then subtracts it from the first. Example (truncate to one digit)

    double original = 23.875114784205696;
    double truncater = original % 0.1;
    //truncater now equals 0.075114784205696;
    original - truncater = 23.8;

Obviously this can be expanded to whatever truncation is needed, and while it presumably does not shrink the double in memory allocation, it will cut off the end for display purposes.

kahlzun
  • 11
  • 2