Round double in Java

I found this wonderful rounding solution:

static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } 

However, the results are confused:

 System.out.println(round(2.655d,2)); // -> 2.65 System.out.println(round(1.655d,2)); // -> 1.66 

Why does he give this result? I am using jre 1.7.0_45.

+57
java floating-point
Feb 26 '14 at 9:22
source share
6 answers

You must replace

 BigDecimal bigDecimal = new BigDecimal(d); 

from

 BigDecimal bigDecimal = BigDecimal.valueOf(d); 

and you will get the expected results:

 2.66 1.66 

Explanation from the Java API:

BigDecimal.valueOf (double val) - uses the double canonical string representation provided by the Double.toString () method. This is the preferred way to convert double (or float) to BigDecimal.

new BigDecimal (double val ) - uses the exact decimal representation of a binary binary floating-point value, and therefore the results of this constructor may be somewhat unpredictable.

+79
Feb 26 '14 at 9:31
source share

You can try changing your program as follows: -

 static Double round(Double d, int precise) { BigDecimal bigDecimal = BigDecimal.valueOf(d); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } 

Ideon Pattern

 Success time: 0.07 memory: 381184 signal:0 Rounded: 2.66 Rounded: 1.66 Success time: 0.07 memory: 381248 signal:0 Rounded: 2.66 Rounded: 1.66 

The reason you get the expected result with BigDecimal.valueOf , and not with new BigDecimal , in the words of Joachim Sauer :

BigDecimal.valueOf(double) will use the canonical string representation of the double value passed to create the BigDecimal object. In other words: the value of the BigDecimal object will be what you see when you execute System.out.println(d) .

If you use new BigDecimal(d) , then BigDecimal will try to represent the double value as accurately as possible. Usually this leads to the fact that much more digits are saved than you want.

This leads to some confusion that you look at in your program.

From the Java Doc document:

BigDecimal.valueOf (double val) . Converts a double to BigDecimal using the double canonical string representation provided by the Double.toString (double) method.

new BigDecimal (double val) -

Translates double to BigDecimal, which is the exact decimal representation of a binary binary floating-point value. The scale of the returned BigDecimal is the smallest value, such as (10scale Γ— val) is an integer. Notes:

  • The results of this constructor may be somewhat unpredictable. We can assume that writing a new BigDecimal (0.1) in Java creates a BigDecimal, which is exactly 0.1 (unscaled value 1,
    with a scale of 1), but in fact it is equal to 0.1000000000000000055511151231257827021181583404541015625. This is due to the fact that 0.1 cannot be represented exactly as double (or, for this matter, as a binary fraction of any finite length). Thus, the value that is passed to the constructor is not exactly 0.1, despite this.
  • The String constructor, on the other hand, is quite predictable: writing a new BigDecimal ("0.1") creates a BigDecimal that is exactly 0.1, as you would expect. Therefore, it is usually recommended that the String constructor be used in preference to this.
  • When double should be used as the source for BigDecimal, note that this constructor provides accurate conversion; this does not give the same result as converting double to string using Double.toString (double) and then using BigDecimal (String)
    constructor. To get this result, use the static value Of (double)
    Method.
+26
Feb 26 '14 at 9:38
source share

This test case is pretty straightforward:

 public static void main (String[] args) throws java.lang.Exception { System.out.println("Rounded: " + round(2.655d,2)); // -> 2.65 System.out.println("Rounded: " + round(1.655d,2)); // -> 1.66 } public static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); System.out.println("Before round: " + bigDecimal.toPlainString()); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); System.out.println("After round: " + bigDecimal.toPlainString()); return bigDecimal.doubleValue(); } 

Output:

 Before round: 2.654999999999999804600747665972448885440826416015625 After round: 2.65 Rounded: 2.65 Before round: 1.6550000000000000266453525910037569701671600341796875 After round: 1.66 Rounded: 1.66 

Dirty crack it will be round in two stages :

 static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); System.out.println("Before round: " + bigDecimal.toPlainString()); bigDecimal = bigDecimal.setScale(15, RoundingMode.HALF_UP); System.out.println("Hack round: " + bigDecimal.toPlainString()); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); System.out.println("After round: " + bigDecimal.toPlainString()); return bigDecimal.doubleValue(); } 

Here 15 is under the maximum number of digits that a double can represent in base 10. Output:

 Before round: 2.654999999999999804600747665972448885440826416015625 Hack round: 2.655000000000000 After round: 2.66 Rounded: 2.66 Before round: 1.6550000000000000266453525910037569701671600341796875 Hack round: 1.655000000000000 After round: 1.66 Rounded: 1.66 
+18
Feb 26 '14 at 9:31
source share

As said in the API

  • The results of this constructor may be somewhat unpredictable. We can assume that writing a new BigDecimal (0.1) in Java creates a BigDecimal, which is exactly 0.1 (unscaled value 1, with a scale of 1), but in fact it is 0.1000000000000000055511151231257827021181583404541015625. This is due to the fact that 0.1 cannot be represented exactly as double (or, for this matter, as a binary fraction of any finite length). Thus, the value that is passed to the constructor is not exactly 0.1, despite this.

  • The String constructor, on the other hand, is quite predictable: writing a new BigDecimal ("0.1") creates a BigDecimal that is exactly 0.1, as you would expect. Therefore, it is usually recommended to use the String constructor in preference to this.

  • If double should be used as the source of BigDecimal, note that this constructor provides accurate conversion; it does not give the same result as converting double to string using Double.toString (double) and then using the BigDecimal (String) constructor. To get this result, use the static value of the Of (double) Method.

Because of this, it is not possible to accurately represent a double value. Therefore you should use BigDecimal bigDecimal = BigDecimal.valueOf(d); instead of BigDecimal bigDecimal = new BigDecimal(d);

+8
Feb 26 '14 at 9:29
source share

Rounding a double resp double by itself does not make much sense, because the double datatype type cannot round (easy or even?).

What are you doing:

  • Take Double d as input and int precise number of digits behind the separator.
  • Create a BigDecimal from this d .
  • Round the BigDecimal correctly.
  • Return the double value of this BigDecimal , which no longer has rounding to it.

You can go in two ways:

  • You can return BigDecimal , which represents a rounded double, and later decide what you do with it.
  • You can return a String representing a rounded BigDecimal .

Any of these methods will make sense.

+7
Feb 26 '14 at 9:30
source share

Decimal numbers cannot be represented exactly in double.

So, 2.655 ends up being: + 2.65499999999999980460074766597

while 1.655 ends with: +1.655000000000000026645352591

+6
Feb 26 '14 at 9:30
source share



All Articles