Pacerier
Pacerier

Reputation: 89693

Why do we need to convert the double into a string, before we can convert it into a BigDecimal?

The source for round in apache commons looks like this:

public static double round(double x, int scale, int roundingMethod) {
    try {
        return (new java.math.BigDecimal(Double.toString(x)).setScale(scale, roundingMethod)).doubleValue();
    } catch (NumberFormatException ex) {
        if (Double.isInfinite(x)) {
            return x;
        } else {
            return Double.NaN;
        }
    }
}

I was wondering, when creating the BigDecimal why did they chose to convert the double to a string (using the Double.toString) instead of simply using the double itself?

In other words, what's wrong with this? :

public static double round(double x, int scale, int roundingMethod) {
    try {
        return (new java.math.BigDecimal(x).setScale(scale, roundingMethod)).doubleValue();
    } catch (NumberFormatException ex) {
        if (Double.isInfinite(x)) {
            return x;
        } else {
            return Double.NaN;
        }
    }
}

Upvotes: 5

Views: 1394

Answers (5)

zsxwing
zsxwing

Reputation: 20836

However, I found one question:

System.out.println(java.math.BigDecimal.valueOf(0.1000000000000000055511151231257827021181583404541015625).toString());

It will print 0.1.

I think the key is java.lang.Double.toString(double d). When we input 0.1(In Java, the exact value is 0.1000000000000000055511151231257827021181583404541015625), the function will drop the tail and we get 0.1. However. if we input 0.1000000000000000055511151231257827021181583404541015625, it always drop the tail and we also get 0.1 which is against our expectation.

So we must be aware of our requirement to choose java.math.BigDecimal.valueOf(double val) or java.math.BigDecimal.BigDecimal(double val).

Upvotes: 1

Fabian Barney
Fabian Barney

Reputation: 14549

It's because the result of BigDecimal(double) constructor is unpredictable as mentioned in javadoc.

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

The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

Test Case:

System.out.println(java.math.BigDecimal.valueOf(0.1).toString());
System.out.println(new java.math.BigDecimal(0.1).toString());

Upvotes: 7

Steve J
Steve J

Reputation: 2674

It seems to me that the key is in this statement:

BigDecimal(double val)
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value.

versus

BigDecimal(String val)
Translates the string representation of a BigDecimal into a BigDecimal.

By going to String first, you just see the double as a BigDecimal, but if construct directly from a double, you're working with the bit-level representation, and I'm willing to bet you'd get a subtly different answer. Remember that floating-point values are represented using a sections of the 64-bits for the whole part, and a section for the fraction part, and a section to represent an exponent (in base 2) -- it's all very esoteric stuff, differs from machine to machine, and I think the only formal definition is found in the accursed Necronomicon.

Update: What Borodin said.

Upvotes: 1

Borodin
Borodin

Reputation: 126742

From the documentation here for the constructor BigDecimal(String val) :

Note: For values other than float and double NaN and ±Infinity, this constructor is compatible with the values returned by Float.toString(float) and Double.toString(double). This is generally the preferred way to convert a float or double into a BigDecimal, as it doesn't suffer from the unpredictability of the BigDecimal(double) constructor.

Upvotes: 4

Bohemian
Bohemian

Reputation: 425178

You don't have to convert to String.
Use the static method BigDecimal.valueOf(double val):

To use in your code:

...
return (BigDecimal.valueOf(x).setScale(scale, roundingMethod)).doubleValue();
....

Upvotes: 1

Related Questions