Why is BigDecimal natural ordering inconsistent with equals?

后端 未结 4 1510
感情败类
感情败类 2020-12-13 19:32

From the Javadoc for BigDecimal:

Note: care should be exercised if BigDecimal objects are used as keys in a SortedMap or ele

4条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-13 20:10

    BigDecimal works by having two numbers, an integer and a scale. The integer is the "number" and the scale is the number of digits to the right of the decimal place. Basically a base 10 floating point number.

    When you say "1.0" and "1.00" these are technically different values in BigDecimal notation:

    1.0
          integer: 10
            scale: 1
        precision: 2
                 = 10 x 10 ^ -1
    
    1.00
          integer: 100
            scale: 2
        precision: 3
                 = 100 x 10 ^ -2
    

    In scientific notation you wouldn't do either of those, it should be 1 x 10 ^ 0 or just 1, but BigDecimal allows it.

    In compareTo the scale is ignored and they are evaluated as ordinary numbers, 1 == 1. In equals the integer and scale values are compared, 10 != 100 and 1 != 2. The BigDecimal equals method ignores the object == this check I assume because the intention is that each BigDecimal is treated as a type of number, not like an object.

    I would liken it to this:

    // same number, different types
    float floatOne = 1.0f;
    double doubleOne = 1.0;
    
    // true: 1 == 1
    System.out.println( (double)floatOne == doubleOne );
    
    // also compare a float to a double
    Float boxFloat = floatOne;
    Double boxDouble = doubleOne;
    
    // false: one is 32-bit and the other is 64-bit
    System.out.println( boxInt.equals(boxDouble) );
    
    // BigDecimal should behave essentially the same way
    BigDecimal bdOne1 = new BigDecimal("1.0");
    BigDecimal bdOne2 = new BigDecimal("1.00");
    
    // true: 1 == 1
    System.out.println( bdOne1.compareTo(bdOne2) );
    
    // false: 10 != 100 and 1 != 2 ensuring 2 digits != 3 digits
    System.out.println( bdOne1.equals(bdOne2) );
    

    Because BigDecimal allows for a specific "precision", comparing both the integer and the scale is more or less the same as comparing both the number and the precision.

    Although there is a semi-caveat to that when talking about BigDecimal's precision() method which always returns 1 if the BigDecimal is 0. In this case compareTo && precision evaluates true and equals evaluates false. But 0 * 10 ^ -1 should not equal 0 * 10 ^ -2 because the former is a 2 digit number 0.0 and the latter is a 3 digit number 0.00. The equals method is comparing both the value and the number of digits.

    I suppose it is weird that BigDecimal allows trailing zeroes but this is basically necessary. Doing a mathematical operation like "1.1" + "1.01" requires a conversion but "1.10" + "1.01" doesn't.

    So compareTo compares BigDecimals as numbers and equals compares BigDecimals as BigDecimals.

    If the comparison is unwanted, use a List or array where this doesn't matter. HashSet and TreeSet are of course designed specifically for holding unique elements.

提交回复
热议问题