Точность с плавающей точкой с Math.Ceil (Java)

1

Я использую бизнес-правило для расчета процентного прироста на уровне запасов:

Stock level | Percentage Increase | Expected output
    100     |           93        |        193

Поскольку уровни десятичного запаса не определены правильно, правило состоит в округлении вывода:

public int calculateStockLevel(int rawStockLevel, double percentageIncrease) {
    return Math.ceil(rawStockLevel * (1 + (percentageIncrease / 100));
}

Фактический выход для ситуации выше: 194 (Тест не прошел)

Это похоже на ошибку точности с плавающей запятой, какой элегантный и читаемый способ пройти этот тест?

  • 3
    Использование плавающих точек с денежными данными не очень хорошая идея, именно из-за проблем с точностью; вы хотите использовать BigDecimal ; также есть Joda Money (о последнем, я знаю, что он существует, я никогда не использовал его на самом деле)
  • 0
    почему ты делаешь 1+ ??
Показать ещё 10 комментариев
Теги:
floating-point

2 ответа

2
Лучший ответ

Вы должны использовать BigDecimal, чтобы указать, какую точность вы хотите, но для вашего случая вы могли бы сделать что-то более простое: просто разделите на 100 в конце.

public int calculateStockLevel(int rawStockLevel, double percentageIncrease) {
    return Math.ceil(rawStockLevel * (100 + percentageIncrease) / 100);
}
  • 0
    Я попробовал и проверил это с целочисленными процентами, и это, кажется, работает, но почему это отличается от моего решения в вопросе? Это математически идентично ....
  • 0
    @ Ed0906 Было бы математически идентично, если бы арифметика с плавающей запятой была арифметикой действительных чисел или даже рациональной арифметикой. Это другая арифметика со своими собственными правилами, предназначенными для ограничения результатов до конечного набора значений двоичной дроби путем округления. Поскольку результат с плавающей запятой может быть немного больше или чуть меньше результата арифметики с рациональным числом, такие операции, как потолок и пол, могут получить другой ответ.
Показать ещё 1 комментарий
0

Двоичные типы с плавающей запятой не могут представлять каждое значение в десятичном значении. Таким образом, percentageIncrease/100 получит самое близкое представление в двоичном формате. В вашем случае точное значение 0,93 с двойной точностью составляет 0,930000000000000048849813083507, что немного больше, чем истинное десятичное значение. Поэтому после ceil он будет округлен до 0,94

Если вам нужен точный десятичный вывод, вы должны использовать десятичную арифметику, например BigDecimal. Но в вашем случае вы можете просто сделать это в виде простого целого числа, используя ract, чтобы дробная часть результата была больше 0, если остаток от деления не равен нулю.

int temp   = rawStockLevel * (100 + percentageIncrease);
int result = temp/100;
if (temp % 100 != 0)
    result++; // round up if not divisible by 100
return result;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню