Округление Java до int с использованием Math.ceil


102
int total = (int) Math.ceil(157/32);

Почему все равно возвращается 4? 157/32 = 4.90625, Мне нужно округлить, я огляделась и, кажется, это правильный метод.

Пробовал totalкак doubleтипа, но получилось 4.0.

Что я делаю не так?

Ответы:


189

Вы делаете то, 157/32что делите два целых числа друг на друга, что всегда приводит к округленному целому числу. Следовательно, (int) Math.ceil(...)ничего не делает. Есть три возможных решения для достижения желаемого. Я рекомендую использовать либо вариант 1, либо вариант 2 . Пожалуйста, НЕ используйте опцию 0 .

## Вариант 0

Преобразуйте aи bв двойное, и вы сможете использовать деление и Math.ceilкак хотите, чтобы оно работало. Однако я категорически не рекомендую использовать этот подход, потому что двойное деление может быть неточным. Чтобы узнать больше о неточности двойников, смотрите этот вопрос .

int n = (int) Math.ceil((double) a / b));

##Опция 1

int n = a / b + ((a % b == 0) ? 0 : 1); 

Вы делаете a / bс всегда пол, если aи bоба являются целыми числами. Затем у вас есть встроенный if-оператор, который проверяет, следует ли вам использовать ceil вместо floor. Итак, +1 или +0, если есть остаток от деления, вам нужен +1. a % b == 0проверяет остаток.

## Вариант 2

Этот вариант очень короткий, но, возможно, для некоторых менее интуитивно понятных. Я думаю, что этот менее интуитивный подход будет быстрее, чем подход двойного деления и сравнения:
обратите внимание, что это не работает b < 0.

int n = (a + b - 1) / b;

Чтобы уменьшить вероятность переполнения, вы можете использовать следующее. Однако обратите внимание, что это не работает для a = 0и b < 1.

int n = (a - 1) / b + 1;

## Объяснение «менее интуитивного подхода»

Поскольку деление двух целых чисел в Java (и большинстве других языков программирования) всегда приводит к результату. Так:

int a, b;
int result = a/b (is the same as floor(a/b) )

Но мы не хотим floor(a/b), но ceil(a/b), используя определения и графики из Википедии :введите описание изображения здесь

С помощью этих графиков функции пола и потолка вы можете увидеть взаимосвязь.

Функция пола Функция ceil

Вы это видите floor(x) <= ceil(x). Нам нужно floor(x + s) = ceil(x). Итак, нам нужно найти s. Если мы возьмем, 1/2 <= s < 1это будет в самый раз (попробуйте несколько цифр, и вы увидите, что это так, мне самому трудно это доказать). И 1/2 <= (b-1) / b < 1так

ceil(a/b) = floor(a/b + s)
          = floor(a/b + (b-1)/b)
          = floor( (a+b-1)/b) )

Это не настоящее доказательство, но я надеюсь, что вы удовлетворены им. Если кто-то может объяснить это лучше, я тоже был бы признателен. Может быть, спросите об этом на MathOverflow .


1
Будет огромным одолжением, если вы сможете объяснить интуицию, стоящую за менее интуитивным подходом? Я знаю, что это правильно, я хочу знать, как вы к этому пришли и как я могу математически показать, что это правильно. Я попытался решить это математически, не убедился.
Саад Рехман Шах

Надеюсь, вы удовлетворены моим редактированием, я не могу ничего улучшить, я думаю :(
martijnn2008

Я предполагаю, что Math.floor и ceil верны только для целочисленного деления, а не для длинного деления, когда значения приводятся к двойным. Примеры счетчиков: 4611686018427386880/4611686018427387137 сбой на полу и 4611686018427386881/4611686018427386880 сбой на потолке
Воутер

2
Уточнение: результаты двух подвариантов варианта 2 не во всех случаях идентичны. Нулевое значение a даст 0 в первом и 1 во втором (что не является правильным ответом для большинства приложений).
Sushisource

1
Вы уверены, что не имели в виду «Однако учтите, что это не работает для a = 0 и b <
дантистон

61

157/32 int/int, что приводит к расширению int.

Попробуйте использовать двойной Литерал - 157/32d, которая int/double, что приводит к double.


Вы уверены, что int / int всегда приведет к int ?! не могли бы вы дать источник этого ?!


35

157/32является целочисленным делением, потому что все числовые литералы являются целыми числами, если иное не указано с суффиксом ( dдля double lдля long)

деление округляется в меньшую сторону (до 4) перед преобразованием в двойное (4,0), которое затем округляется в большую сторону (до 4,0)

если вы используете переменные, вы можете избежать этого

double a1=157;
double a2=32;
int total = (int) Math.ceil(a1/a2);


7

Никто не упомянул самые интуитивные:

int x = (int) Math.round(Math.ceil((double) 157 / 32));

Это решение устраняет неточность двойного деления.


1
Math.round возвращается долго
Zulqurnain Jutt

Спасибо @ZulqurnainJutt, добавлен состав
IG Pascual


3

При делении двух целых чисел, например,

int c = (int) a / (int) b;

результатом является intзначение, aделенное на b, округленное до нуля. Поскольку результат уже округлен, ceil()ничего не делает. Обратите внимание, что это округление отличается от floor(), которое округляется до отрицательной бесконечности. Итак, 3/2равно 1floor(1.5)равно 1.0, но (-3)/2равно -1(но floor(-1.5)равно -2.0).

Это очень важно , потому что если a/bвсегда были такими же , как floor(a / (double) b), то можно просто реализовать ceil()в a/bкачестве -( (-a) / b).

Предложение получить ceil(a/b)от

int n = (a + b - 1) / b;, что эквивалентно a / b + (b - 1) / b, или(a - 1) / b + 1

работает, потому что ceil(a/b)всегда на единицу больше floor(a/b), кроме a/bцелых чисел. Итак, вы хотите перебросить его (или пройти) до следующего целого числа, если a/bэто не целое число. Добавление 1 - 1 / bсделает это. Для целых чисел это не совсем подтолкнет их к следующему целому числу. Во всем остальном так и будет.

Ой. Надеюсь, это имеет смысл. Я уверен, что есть более математически элегантный способ объяснить это.


2

Также, чтобы преобразовать число из целого в действительное, вы можете добавить точку:

int total = (int) Math.ceil(157/32.);

И результат (157/32.) Тоже будет реальным. ;)



1

Проверьте решение своего вопроса ниже:

int total = (int) Math.ceil(157/32);

Здесь вы должны умножить числитель на 1.0, тогда он даст ваш ответ.

int total = (int) Math.ceil(157*1.0/32);

0

Используйте double, чтобы использовать как

Math.ceil((double)value) или как

Math.ceil((double)value1/(double)value2);

0

/По умолчанию в Java предусмотрено только разделение этажей . Но мы можем обозначить потолок полом . Посмотрим:

В yформе можно записать любое целое число y == q*k+r. Согласно определению разделения этажей (здесь floor), которое округляется r,

floor(q*k+r, k) == q  , where 0  r  k-1

и разделение потолка (здесь ceil), которое округляется вверх r₁,

ceil(q*k+r₁, k) == q+1  , where 1  r  k

где мы можем заменить r+1на r₁:

ceil(q*k+r+1, k) == q+1  , where 0  r  k-1


Затем мы подставляем первое уравнение в третье, чтобы qполучить

ceil(q*k+r+1, k) == floor(q*k+r, k) + 1  , where 0  r  k-1

Наконец, учитывая любое целое число , yгде y = q*k+r+1для некоторых q, k, rмы имеем

ceil(y, k) == floor(y-1, k) + 1

И мы закончили. Надеюсь это поможет.


Я уверен, что это правильно, но поскольку цель этого состоит в том, чтобы прояснить, мне непонятно, почему ceilопределяется как таковое из интуитивного определения, в частности, когда мы берем ceil целого числа, т.е. r1 = k. Поскольку крайние случаи - это то, что здесь сложно, я думаю, что это нужно разъяснить немного подробнее.
Луиджи Плиндж

@LuigiPlinge Для меня вывод не может быть проще из-за существенной разницы между полом и потолком в контексте операции разделения. Я думаю, вам не нужно сосредотачиваться на крайнем случае - это естественный факт, когда вы пытаетесь унифицировать определения пола и потолка, разбивая целое число. В результате доказательство состоит всего из трех шагов, а вывод можно примерно запомнить как «один амортизированный шаг назад, затем один абсолютный шаг вперед».
ShellayLee

0

Есть два метода округления двойного значения.

  1. Math.ceil
  2. Math.floor

Если вы хотите, чтобы ваш ответ 4,90625 был равен 4, вам следует использовать Math.floor, а если вы хотите, чтобы ваш ответ 4,90625 был равен 5, вы можете использовать Math.ceil

Для этого вы можете сослаться на следующий код.

public class TestClass {

    public static void main(String[] args) {
        int floorValue = (int) Math.floor((double)157 / 32);
        int ceilValue = (int) Math.ceil((double)157 / 32);
        System.out.println("Floor: "+floorValue);
        System.out.println("Ceil: "+ceilValue);

    }

}

0

Я знаю, что это старый вопрос, но, на мой взгляд, у нас есть лучший подход, который использует BigDecimal, чтобы избежать потери точности. Кстати, с помощью этого решения у нас есть возможность использовать несколько стратегий округления и масштабирования.

final var dividend = BigDecimal.valueOf(157);
final var divisor = BigDecimal.valueOf(32);
final var result = dividend.divide(divisor, RoundingMode.CEILING).intValue();

-3
int total = (157-1)/32 + 1

или более общий

(a-1)/b +1 

Я думаю, это работает, но вы не совсем объяснили, почему исходная версия не сработала.
Teepeemm 08

« Однако обратите внимание, что это не работает для a = 0 и b <1 »
IG Pascual
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.