JDK-4508009 : rounding error in BigDecimal.setScale
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.math
  • Affected Version: 1.4.0
  • Priority: P2
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2001-09-27
  • Updated: 2001-10-02
  • Resolved: 2001-10-02
Description

Name: nt126004			Date: 09/27/2001


java version "1.4.0-beta2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta2-b77)
Java HotSpot(TM) Client VM (build 1.4.0-beta2-b77, mixed mode)


java.math.BigDecimal using ROUND_HALF_UP does not behave as expected

i.e

import java.math.*;
public class BigTest {
public static void main(String[] args) {

    BigDecimal bd = new BigDecimal(0.115);
    bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
    System.out.println("? 0.12 = " + bd);

    bd = new BigDecimal(1.115);
    bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
    System.out.println("? 1.12 = " + bd);

    bd = new BigDecimal(2.115);
    bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);

    System.out.println("? 2.12 = " + bd);
}
}



output:

? 0.12 = 0.12
? 1.12 = 1.11
? 2.12 = 2.12

should be:

? 0.12 = 0.12
? 1.12 = 1.12
? 2.12 = 2.12


(Review ID: 132704) 
======================================================================

Comments
WORK AROUND Using the string constructor instead of the double constructor avoids the numerical surprise occuring here.
11-06-2004

EVALUATION The "problem" in this bug is that the code is using the BigDecimal double construtor instead of the BigDecimal string constructor. The exact value of a decimal floating-point literal (like "0.115") generally cannot be stored in a floating-point number since floating-point numbers use base 2 instead of base 10. Instead of the exact value of the literal, you get a value that is very close; for the values in question: 0.115d is exactly 0.11500000000000000499600361081320443190634250640869140625 1.115d is exactly 1.1149999999999999911182158029987476766109466552734375 2.115d is exactly 2.1150000000000002131628207280300557613372802734375 These exact double values are then converted to BigDecimal (all finite binary floating-point values can be exactly represented as BigDecimals) as described in the BigDecimal class documetnation. Now, it is easy to see why the given output was observed; using the string constructor gives the "expected" results. This issue is explicitly discussed in the BigDecimal documentation for the double constructor: "Translates a double into a BigDecimal. The scale of the BigDecimal is the smallest value such that (10scale * val) is an integer. Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding. The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one. " See "What Everybody Using the JavaTM Programming Language Should Know About Floating-Point Arithmetic" http://java.sun.com/people/darcy/JavaOne/2001/1789darcy.pdf for further discussion of Java numerics issues. Closing as not a bug. ###@###.### 2001-10-01
01-10-2001

PUBLIC COMMENTS A side-effect of floating-point decimal -> binary conversion is being seen. The value indicated by the decimal string is not exactly representable. Using the string constructor instead avoids this particular issue. ###@###.### 2001-10-01
01-10-2001