2

I wanted to see if anyone can explain why the following code works with valueOf but not others.

import java.math.BigDecimal; 
public class Change {
   public static void main(String args[]) {
     double a = 4.00d;
     double b = 3.10d;
     BigDecimal a1 = new BigDecimal(a);
     BigDecimal b1 = new BigDecimal(b);
     BigDecimal diff = a1.subtract(b1);
     System.out.println("Double difference");
     System.out.println(diff);

     float c = 4.00f;
     float d = 3.10f;
     BigDecimal a2 = new BigDecimal(c);
     BigDecimal b2 = new BigDecimal(d);
     BigDecimal diff2 = a2.subtract(b2);
     System.out.println("Float difference");
     System.out.println(diff2);

     System.out.println("Valueof Difference");
     System.out.println(BigDecimal.valueOf(4.00).subtract(BigDecimal.valueOf(3.10)));

   }
 }

The output looks like:

>java Change
 Double difference
  0.899999999999999911182158029987476766109466552734375
 Float difference
  0.900000095367431640625
 Valueof Difference
 0.9

My question is: What does valueOf() do to get the precision? Is there any other way of getting the correct result without rounding off to the 2 digits manually?

thanks,

oers
  • 18,436
  • 13
  • 66
  • 75
truthSeekr
  • 1,603
  • 4
  • 25
  • 44
  • you can look at adding the MathContext parameter to your BigDecimal constructor, if you want to play around with the precision. – Sean May 16 '11 at 18:15

4 Answers4

8

Looking at the source code for BigDecimal, it does:

public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO.  This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
}

From its JavaDoc:

Translates a double into a BigDecimal, using the double's canonical string representation provided by the Double.toString(double) method.

Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).

Because of floating-point representation, a double value is not exactly what you set it as. However, during String representation, it rounds off what it displays. (All of the rules are on it's JavaDoc).

Furthermore, because of this rounding, if you did:

BigDecimal d = BigDecimal.valueOf(4.00000000000000000000000000000000001));

you would get the wrong value. (d == 4.0)

So, it's pretty much always better to initialize these with strings.

Reverend Gonzo
  • 39,701
  • 6
  • 59
  • 77
  • 2
    You are absolutely right. valueOf(4.00000000000000000001) doesn't work. Learnt something new today :) thanks. – truthSeekr May 16 '11 at 18:21
1

The valueOf works because it calls Double.toString. from the Javadoc:

public static BigDecimal valueOf(double val)

    Translates a double into a BigDecimal, using the double's

canonical string representation provided by the Double.toString(double) method.

When you pass a double into the BigDecimal constructor, the constructor takes the floating-point value and reproduces it exactly. The toString code finds an approximation for the floating point value.

In case you didn't notice, using System.out.println() to show a floating point number doesn't show the same results as if you wrap the floating point number in a BigDecimal first (using the BigDecimal constructor that takes a double).

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
1

BigDecimal.valueOf(double) first does a conversion from double to String, then String to BigDecimal.

In the first case, you're starting with a double or float, converting to BigDecimal, calculating the difference. In the second case, you're starting with double or float, converting to a String, then converting to BigDecimal, then calculating the difference.

ataylor
  • 64,891
  • 24
  • 161
  • 189
1

From the Javadocs:

public static BigDecimal valueOf(double val)

Translates a double into a BigDecimal, using the double's canonical string representation provided by the Double.toString(double) method. Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).

I think this answers both of your questions.

Cheers,

Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118