1

I am having following code, however I am not able to understand why the two bigdecimal are not considered as equal

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        BigDecimal b = new BigDecimal(13.90);
        BigDecimal b2 = new BigDecimal("13.9");
        System.out.println(b.compareTo(b2));
    }
}

This code outputs 1 as output. Why would that be the case? Shouldn't it be 0?

Also if I write 13.9 instead of "13.9" it gives 0 as output

codeomnitrix
  • 4,179
  • 19
  • 66
  • 102
  • 1
    Check this https://stackoverflow.com/questions/6787142/bigdecimal-equals-versus-compareto – Maytham Fahmi May 26 '18 at 18:14
  • Thats different. if both are string or both are numerical values then it works and returns 0 but it returns non zero if one is derived from string while other from double – codeomnitrix May 26 '18 at 18:17
  • Because `13.9` isn't a precise `double` value. `System.out.println(Double.toHexString(13.9));` – Elliott Frisch May 26 '18 at 18:22
  • 1
    @codeomnitrix: No thats not generally true. It fails because that double cannot be exactly represented. – Dorian Gray May 26 '18 at 18:23
  • how can i compare these two bigdecimal without rounding? – codeomnitrix May 26 '18 at 18:23
  • @codeomnitrix if you want exact comparisons, get rid of all floats and doubles from your program. Never use them. I exaggerate of course. But don't involve them in the computations that you want exact, because the very point of float and double is to not be exact. – kumesana May 26 '18 at 18:26

3 Answers3

6

Because you are assuming that if you use 13.9 it will exactly be 13.9. Try printing the values of b and b2 and you'll see that the 13.9 is actually 13.9000000000000003552713678800500929355621337890625, and the parsed string value is 13.9. So b is (slightly) higher than b2.

public static void main(String...strings) {
            BigDecimal b = new BigDecimal(13.9);
            BigDecimal b2 = new BigDecimal("13.9");
            System.out.printf("%s %s %d%n", b, b2, b.compareTo(b2));
}

Gives as output:

13.9000000000000003552713678800500929355621337890625 13.9 1

On the topic of floating point mathemetics you might want to read Why Are Floating Point Numbers Inaccurate? and other links available on stackoverflow.

M. le Rutte
  • 3,525
  • 3
  • 18
  • 31
  • Thats weird implementation, any option to compare that the values are same? apart from using rounding? – codeomnitrix May 26 '18 at 18:20
  • 2
    It is not wierd implementation, it is the limitation you get when you have to encode a floating point in a binary mechanism. It is the limitation that a computer has. You need some form of rounding/truncation to decide when they are the same, or go `BigDecimal-with-string-constructor` complety, as the implemetation of `BigDecimal` does not suffer from that problem (afaik). – M. le Rutte May 26 '18 at 18:23
  • They are evdently not the same. – Dorian Gray May 26 '18 at 18:24
  • It is not at all weird. A double literal isn't a string literal! Floating point math is complex, so take your time and read that link given to you. – GhostCat May 26 '18 at 18:24
  • yes floating point math is clear to me...I wanted to say that string to big decimal constructor is a little different than float inside a big decimal constructor. – codeomnitrix May 26 '18 at 18:26
  • 3
    @codeomnitrix well obviously they would be different constructors. BigDecimal has infinite precision, and building one with String enables to build any value with such infinite precision. So it obviously will. On the other hand, a constructor taking a floating-point value as parameter cannot do anything about that floating-point value not being exactly what it looks like. – kumesana May 26 '18 at 18:29
  • thanks for clarification...will keep in mind in future. – codeomnitrix May 26 '18 at 18:32
3

Floating-point numeric values are not exact, they're approximate. So when you type the literal value 13.90, the actual value retained is something not exactly 13.90, but as close to it as possible.

BigDecimal values are exact, and Strings are just Strings.

So when you use new BigDecimal(13.90) and new BigDecimal("13.9"), the first one holds a value that's exactly the floating-point value passed to it, which is very close but different to 13.9. And the second one contains exactly 13.9.

One of them is 13.9 and the other is close, but not equals, to 13.9. So the two objects aren't equals.

kumesana
  • 2,495
  • 1
  • 9
  • 10
2

The javadoc for the BigDecimal constructor of double notes that the result "can be somewhat unpredictable". That's because the input decimal value is actually a floating point approximation. Generally, it is a good practice to construct decimal values using the BigDecimal String constructor. However, if you don't or are unable to do that, you can always set the scale and round. The code below would give you the results you seek.

BigDecimal b = new BigDecimal(13.90).setScale(2,BigDecimal.ROUND_HALF_UP);
BigDecimal b2 = new BigDecimal("13.9");
System.out.println(b.compareTo(b2));

Note: The rounding occurs at the end of the scale. So with a set of 2, the value 13.900004 would round to 13.90.

LiveNLearn
  • 321
  • 1
  • 5