Подтвердить что ты не робот

BigDecimal умножается на ноль

Я выполняю простое умножение с BigDecimal, и я обнаружил какое-то странное поведение при умножении на ноль (умножение на ноль правильное в этом случае).

Базовая математика говорит мне, что все, что умножается на ноль, будет равно нулю (см. Свойство нулевого продукта и Свойства умножения)

Однако следующий код будет последовательно терпеть неудачу с той же ошибкой:

assertEquals(new BigDecimal(0), new BigDecimal(22.3).multiply(new BigDecimal(0)));
java.lang.AssertionError: 
Expected :0
Actual   :0E-48

Является ли это неточностями с BigDecimal или есть какая-то нишевая ветвь математики, которую я где-то не хватает?

Примечания: JDK 1.6.0_27 работает в IntelliJ 11

4b9b3361

Ответ 1

Вы не можете использовать метод equals() для сравнения BigDecimals, как это делает утверждение. Это потому, что эта равная функция будет сравнивать масштаб. Если масштаб отличается, equals() вернет false, даже если они будут одинаковым числом математически.

Однако вы можете использовать compareTo() для выполнения того, что хотите:

Как указывает @assylias, вы также должны использовать конструктор new BigDecimal("22.3"), чтобы избежать проблем с двойной точностью.

BigDecimal expected = BigDecimal.ZERO;
BigDecimal actual = new BigDecimal("22.3").multiply(BigDecimal.ZERO);
assertEquals(0, expected.compareTo(actual));

Существует также метод под названием signum(), который возвращает -1, 0 или 1 для отрицательных, нулевых и положительных. Таким образом, вы можете также проверить на ноль с помощью

assertEquals(0, actual.signum());

Ответ 2

В коде есть 2 проблемы:

  • вам следует сравнить BigDecimal с compareTo вместо equals, как советуют другие ответы
  • но вы также должны использовать конструктор строк: new BigDecimal("22.3") вместо двойного конструктора new BigDecimal(22.3), чтобы избежать проблем с двойной точностью

Другими словами, следующий код (который правильно использует compareTo) по-прежнему возвращает false:

BigDecimal bd = new BigDecimal(0.1).multiply(new BigDecimal(10));
System.out.println(bd.compareTo(BigDecimal.ONE) == 0);

потому что 0.1d * 10d != 1

Ответ 3

equals() on BigDecimal проверяет внутреннее состояние BigDecimal для сравнения

Обратитесь к приведенному ниже коду

public boolean equals(Object x) {
    if (!(x instanceof BigDecimal))
        return false;
    BigDecimal xDec = (BigDecimal) x;
    if (x == this)
        return true;
    if (scale != xDec.scale)
        return false;
    long s = this.intCompact;
    long xs = xDec.intCompact;
    if (s != INFLATED) {
        if (xs == INFLATED)
            xs = compactValFor(xDec.intVal);
        return xs == s;
    } else if (xs != INFLATED)
        return xs == compactValFor(this.intVal);

    return this.inflate().equals(xDec.inflate());
}

если вы хотите сравнить значения, используйте compareTo()

Измените свой код на

assertEquals(0 , new BigDecimal(0).compareTo(new BigDecimal(22.3).multiply(new BigDecimal(0)));

Обновление:

Использовать конструктор, берущий String в качестве параметра для BigDecimal для точной точности проверки ссылок ниже


Также см.