136

I came across two ways of getting BigDecimal object out of a double d.

  1. new BigDecimal(d)
  2. BigDecimal.valueOf(d)

Which would be a better approach? Would valueOf create a new object?

In general (not just BigDecimal), what is recommended - new or valueOf?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Manish Mulani
  • 7,125
  • 9
  • 43
  • 45
  • 10
    In general, valueOf is preferred (because it can avoid making new objects by reusing "popular" instances), but in the case of BigDecimals and double, unfortunately, the two methods produce different results, so you have to choose which one you need. – Thilo Aug 25 '11 at 07:03

4 Answers4

205

Those are two separate questions: "What should I use for BigDecimal?" and "What do I do in general?"

For BigDecimal: this is a bit tricky, because they don't do the same thing. BigDecimal.valueOf(double) will use the canonical String representation of the double value passed in to instantiate the BigDecimal object. In other words: The value of the BigDecimal object will be what you see when you do System.out.println(d).

If you use new BigDecimal(d) however, then the BigDecimal will try to represent the double value as accurately as possible. This will usually result in a lot more digits being stored than you want. Strictly speaking, it's more correct than valueOf(), but it's a lot less intuitive.

There's a nice explanation of this in the JavaDoc:

The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

In general, if the result is the same (i.e. not in the case of BigDecimal, but in most other cases), then valueOf() should be preferred: it can do caching of common values (as seen on Integer.valueOf()) and it can even change the caching behaviour without the caller having to be changed. new will always instantiate a new value, even if not necessary (best example: new Boolean(true) vs. Boolean.valueOf(true)).

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • This also explains my question: http://stackoverflow.com/questions/15685705/android-java-rounding-error-cant-understand-why#15685784 – Christian Mar 28 '13 at 17:22
  • 3
    @Joachim, it wasn't clear. Is `new BigDecimal()` better than `BigDecimal.valueOf()`? – ryvantage Dec 24 '13 at 22:20
  • 7
    @ryvantage: if one where strictly better than the other then there would be no need for both and my answer would be much shorter. They don't do the same thing, so you can't rank them at like that. – Joachim Sauer Dec 24 '13 at 22:27
  • 3
    @JoachimSauer, ok sorry I should've been more specific. Would you mind giving an example of when `new BigDecimal()` would be preferred and an example of when `BigDecimal.valueOf()` would be preferred? – ryvantage Dec 24 '13 at 22:43
  • @ryvantage: Compare the results of `new BigDecimal(1.0/30.0);` and `BigDecimal.valueOf(1.0/30.0)`. See which result is actually closer to the numerical fraction 1/30. – supercat Apr 04 '14 at 20:52
  • _"valueOf() should be preferred: it can do caching of common values"_ I just looked at the source code for `BigDecimal.valueOf(double)` and it does NOT do any sort of caching. It just has one line: `return new BigDecimal(Double.toString(val));` and that has to always create a new object. (I believed the lie too, so I'm stumped myself.) – ADTC Feb 01 '18 at 20:12
  • This has bitten me multiple times. When I have a double that displays as .3065 I expect my BigDecimal of it to also be .3065... Not so when using "new"! I'm now just defaulting to using valueOf (unless I specifically require the other) as the vast majority of my work depends on exact matches of precision (current industry requirements). – Brian Knoblauch Apr 12 '18 at 14:17
  • 2
    @BrianKnoblauch: ironically the "new" one actually matches the input double more closely and it's the toString output that's "cheating" by cutting off digits that are correct but not necessary to distinguish one double value from the next. – Joachim Sauer Apr 12 '18 at 15:03
  • 1
    @JoachimSauer Yeah, what I really *want* to do is remove all doubles and convert entirely to BigDecimal, but that'd be a massive undertaking for an application whose EOL date is approaching. :-) – Brian Knoblauch Apr 12 '18 at 15:13
  • but new BigDecimal(0.99) became 0.989999..., which return me a wrong result – Beeing Jk Jan 28 '19 at 04:27
  • 1
    @BeeingJk: yes, and I explain in detail in this answer why that is and how to fix it. – Joachim Sauer Jan 30 '19 at 10:54
60

If you are using your BigDecimal objects to store currency values, then I strongly recommend that you do NOT involve any double values anywhere in their calculations.

As stated in another answer, there are known accuracy issues with double values and these will come back to haunt you big time.

Once you get past that, the answer to your question is simple. Always use the constructor method with the String value as the argument to the constructor, as there is no valueOf method for String.

If you want proof, try the following:

BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal("0.01");
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);

You'll get the following output:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

See also this related question

Community
  • 1
  • 1
DuncanKinnear
  • 4,563
  • 2
  • 34
  • 65
7

Basically valueOf(double val) just does this:

return new BigDecimal(Double.toString(val));

Therefore -> yep, a new object will be created :).

In general I think it depends upon your coding style. I would not mixure valueOf and "new", if both are the same outcome.

DXTR66
  • 563
  • 5
  • 17
  • 9
    Technically true, **but**: it'll make a **huge** difference. `valueOf()` has the more *intuitive* behaviour, while `new BigDecimal(d)` has the more *correct* one. Try both and see the difference. – Joachim Sauer Aug 25 '11 at 06:58
  • Technically false. 'new' always keyword always creates a new object while the javadoc does not tell if valueOf will return always a new object or not. It does not, not always. It has some values in cache so `new BigDecimal(1) != new BigDecimal(1)` but `BigDecimal.valueOf(1) == BigDecimal.valueOf(1)` – aalku Aug 25 '11 at 07:43
  • 2
    @user: yes, but since `BigDecimal` is immutable it should be treated the same way that the primitive wrappers (`Integer`, `Byte`, ...) and `String` are treated: object identity *should not matter* to your code, only the *value* should matter. – Joachim Sauer Aug 25 '11 at 08:21
  • @Joachim Right but that internal cache is there for a reason. Too many not needed equal instances of BigDecimal are not a good thing to have. And I was answering to Dr, He said "a new object will be created" – aalku Aug 25 '11 at 08:48
  • 4
    @user: yes, that's why I said that `valueOf()` should *generally* be preferred. But note that `BigDecimal.valueOf(double)` doesn't do any caching (and it probably wouldn't be worth it, either). – Joachim Sauer Aug 25 '11 at 08:50
  • @Joachim Right, it is for valueOf(long). As commented in the source code it is planned to do it with double too but you are right. – aalku Aug 25 '11 at 15:37
1

Foreword

Why are we having these discussions about floating-point type, numbers & arithmetic? Simple. We count in base 10, but the machine count in base 2.

BigDecimal - Need for an exact representation (not approximation)

If you are using BigDecimal, this means that you want an exact representation of 0.1 and other negative powers of ten (usually you would be dealing with money or arithmetic involving decimals).

Double means trouble (where BigDecimal is concerned)

Then, if you are finding yourself having to manipulate double(or float) values using BigDecimal, then you are in double trouble, because it is impossible to represent 0.1 as a double in base 2. The machine "stores" doubles(IEEE-754 standard for floating-point arithmetic) as base 2. Here is a good write-up of what's really happening if you are interested.). Duncan's answer illustrates what i am trying to say, of what to do and not do.

Any programming language that you think can store 0.1 accurately is actually not. It is just an approximation.

System.out.println(0.1d);
//Prints 0.1 or so you think ;-)

//If you are not convinced, try this:
double x = 1.1; double y = 1.0;
if (x-y == 0.1) {// print true } else {// print false}

//or perhaps this:
double amount1 = 2.15;
double amount2 = 1.10;
System.out.println("Difference: " + (amount1 - amount2));

Examples

  double smallD = 0.0001;
  double smallDNoScientificNotation = 0.001; //>= 10E-3
  double normalD = 10.345678;
  double bigDNoScientificNotation = 1234567.123456789; //<=10E7 
  double bigD = 56_789_123_456_789.123456789;

  //double
  System.out.println(smallD); //1.0E-4, computerized scientific notation, this is how Double toString works
  System.out.println(smallDNoScientificNotation); //0.001, OK
  System.out.println(normalD); //10.345678, OK
  System.out.println(bigDNoScientificNotation); //1234567.123456789, OK
  System.out.println(bigD); //5.6789123456789125E13, computerized scientific notation, this is how Double toString works
  
  //new BigDecimal(double): not OK, don't use! Attempting to representing the base-2 representation as accurately as possible
  System.out.println(new BigDecimal(smallD)); //0.000100000000000000004792173602385929598312941379845142364501953125
  System.out.println(new BigDecimal(smallDNoScientificNotation)); //0.001000000000000000020816681711721685132943093776702880859375
  System.out.println(new BigDecimal(normalD)); //10.34567799999999948568074614740908145904541015625
  System.out.println(new BigDecimal(bigDNoScientificNotation)); //1234567.12345678894780576229095458984375
  System.out.println(new BigDecimal(bigD)); //56789123456789.125
  
  //BigDecimal.valueOf (Dont use if the range is >= 10E-3, >= 10E7), under the hood it's using Double.toString
  System.out.println(BigDecimal.valueOf(smallD)); //0.00010 - notice the extra 0, stemming from 1.0E-4
  System.out.println(BigDecimal.valueOf(smallDNoScientificNotation)); //0.001
  System.out.println(BigDecimal.valueOf(normalD)); //10.345678
  System.out.println(BigDecimal.valueOf(bigDNoScientificNotation)); //1234567.123456789
  System.out.println(BigDecimal.valueOf(bigD)); //56789123456789.125 //loss of accuracy 

Computerized scientific notation - more here.

BONUS 1 - Pitfalls

Here

BONUS 2 - Effective Java 3rd edition (Joshua Bloch)

Item 60: Avoid float or double if exact answers are required

The float and double types are particularly ill-suited for monetary calculations because it is impossible to represent 0.1 (or any other negative power of ten) as a float or double exactly.
:
There are, however, two disadvantages to using BigDecimal: it's a lot less convenient than using a primitive arithmetic type, and it's a lot slower. The latter disadvantage is irrelevant if you're solving a single short problem, but the former may annoy you.
:
An alternative to using BigDecimal is to use int or long, depending on the amounts involved, and to keep track of the decimal point yourself. In this example, the obvious approach is to do all computation in cents.

Extra reading for the mathematically inclined ;-)

What Every Computer Scientist Should Know About Floating-Point Arithmetic

jumping_monkey
  • 5,941
  • 2
  • 43
  • 58